From 2b2709b51521c4314bc19c05638b66661b559851 Mon Sep 17 00:00:00 2001
From: kp-laura-sempere <46663952+kp-laura-sempere@users.noreply.github.com>
Date: Mon, 8 Jan 2024 12:07:48 +0100
Subject: [PATCH] PIA-887: Basic IKEv2 connection PoC (#55)
* PIA-886: Selected region dashboard section (UI)
* PIA-886: Quick connect dashboard section (UI)
* PIA-887: IKev2 connection PoC
---
.../UseCases/VpnConnectionUseCase.swift | 14 +-
PIA VPN-tvOS/PIA VPN-tvOS.entitlements | 11 +-
.../Presentation/RootContainerViewModel.swift | 38 +++-
.../RootContainer/UI/RootContainerView.swift | 48 ++--
PIA VPN.xcodeproj/project.pbxproj | 208 +++++++++++++++++-
PIA VPN/AppConfiguration.swift | 7 +-
PIA VPN/AppConstants.swift | 3 +-
PIA VPN/AppPreferences.swift | 30 ++-
PIA VPN/Bootstrapper.swift | 46 +++-
PIA-VPN-tvOS-NetworkExtension/Info.plist | 13 ++
...PIA_VPN_tvOS_NetworkExtension.entitlements | 10 +
.../PacketTunnelProvider.swift | 37 ++++
12 files changed, 422 insertions(+), 43 deletions(-)
create mode 100644 PIA-VPN-tvOS-NetworkExtension/Info.plist
create mode 100644 PIA-VPN-tvOS-NetworkExtension/PIA_VPN_tvOS_NetworkExtension.entitlements
create mode 100644 PIA-VPN-tvOS-NetworkExtension/PacketTunnelProvider.swift
diff --git a/PIA VPN-tvOS/Dashboard/UseCases/VpnConnectionUseCase.swift b/PIA VPN-tvOS/Dashboard/UseCases/VpnConnectionUseCase.swift
index 07d75a12f..21bc03c39 100644
--- a/PIA VPN-tvOS/Dashboard/UseCases/VpnConnectionUseCase.swift
+++ b/PIA VPN-tvOS/Dashboard/UseCases/VpnConnectionUseCase.swift
@@ -1,5 +1,6 @@
import Foundation
+import PIALibrary
protocol VpnConnectionUseCaseType {
func connect()
@@ -16,11 +17,20 @@ class VpnConnectionUseCase: VpnConnectionUseCaseType {
}
func connect() {
- // TODO: Implement
+ // TODO: Inject VPNProvider object
+ let vpnProvider = Client.providers.vpnProvider
+ vpnProvider.connect { error in
+ NSLog("Connection error: \(error)")
+ }
}
func disconnect() {
- // TODO: Implement
+ // TODO: Inject VPNProvider object
+ let vpnProvider = Client.providers.vpnProvider
+ vpnProvider.disconnect { error in
+ NSLog("Disconnect error: \(error)")
+ }
+
}
func connect(to server: ServerType) {
diff --git a/PIA VPN-tvOS/PIA VPN-tvOS.entitlements b/PIA VPN-tvOS/PIA VPN-tvOS.entitlements
index 0c67376eb..25512ea17 100644
--- a/PIA VPN-tvOS/PIA VPN-tvOS.entitlements
+++ b/PIA VPN-tvOS/PIA VPN-tvOS.entitlements
@@ -1,5 +1,14 @@
-
+
+ com.apple.developer.networking.networkextension
+
+ packet-tunnel-provider
+
+ com.apple.security.application-groups
+
+ group.com.privateinternetaccess
+
+
diff --git a/PIA VPN-tvOS/RootContainer/Presentation/RootContainerViewModel.swift b/PIA VPN-tvOS/RootContainer/Presentation/RootContainerViewModel.swift
index 050768a01..b2c22e414 100644
--- a/PIA VPN-tvOS/RootContainer/Presentation/RootContainerViewModel.swift
+++ b/PIA VPN-tvOS/RootContainer/Presentation/RootContainerViewModel.swift
@@ -1,6 +1,7 @@
import Foundation
import SwiftUI
+import PIALibrary
class RootContainerViewModel: ObservableObject {
enum State {
@@ -11,9 +12,10 @@ class RootContainerViewModel: ObservableObject {
}
@Published var state: State = .splash
+ @Published private var isBootstrapped: Bool = false
// TODO: Update this value from the Vpn OnBoarding installation profile screen
- @AppStorage(.kOnboardingVpnProfileInstalled) var onBoardingVpnProfileInstalled = true
+ @AppStorage(.kOnboardingVpnProfileInstalled) var onBoardingVpnProfileInstalled = false
let accountProvider: AccountProviderType
let notificationCenter: NotificationCenterType
@@ -30,7 +32,17 @@ class RootContainerViewModel: ObservableObject {
notificationCenter.removeObserver(self)
}
+ func phaseDidBecomeActive() {
+ // Bootstrap PIALibrary preferences and settings
+ Bootstrapper.shared.bootstrap()
+ isBootstrapped = true
+ updateState()
+ }
+
private func updateState() {
+ guard isBootstrapped else {
+ return
+ }
switch (accountProvider.isLoggedIn, onBoardingVpnProfileInstalled) {
// logged in, vpn profile installed
case (true, true):
@@ -43,7 +55,12 @@ class RootContainerViewModel: ObservableObject {
state = .notActivated
}
}
-
+
+}
+
+// NotificationCenter subscriptions
+
+extension RootContainerViewModel {
private func subscribeToAccountUpdates() {
notificationCenter.addObserver(self, selector: #selector(handleAccountDidLogin), name: .PIAAccountDidLogin, object: nil)
@@ -57,6 +74,19 @@ class RootContainerViewModel: ObservableObject {
@objc func handleAccountDidLogout() {
updateState()
}
-
-
+}
+
+extension RootContainerViewModel {
+ // FIXME: this method should be in the VpnProfile installation VM object
+ // Implemented in PIA-888
+ func installVpnProfile() {
+ let vpnProvider = Client.providers.vpnProvider
+ vpnProvider.install(force: true) { error in
+ NSLog("Install VPN profile error: \(String(describing: error))")
+ if error == nil {
+ self.onBoardingVpnProfileInstalled = true
+ }
+ }
+
+ }
}
diff --git a/PIA VPN-tvOS/RootContainer/UI/RootContainerView.swift b/PIA VPN-tvOS/RootContainer/UI/RootContainerView.swift
index d3aa159a8..4a06089c6 100644
--- a/PIA VPN-tvOS/RootContainer/UI/RootContainerView.swift
+++ b/PIA VPN-tvOS/RootContainer/UI/RootContainerView.swift
@@ -4,25 +4,45 @@ import SwiftUI
struct RootContainerView: View {
@ObservedObject var viewModel: RootContainerViewModel
+ @Environment(\.scenePhase) var scenePhase
var body: some View {
- switch viewModel.state {
- case .splash:
- VStack {
- // TODO: Add Splash screen here
+ VStack {
+ switch viewModel.state {
+ case .splash:
+ VStack {
+ // TODO: Add Splash screen here
+ }
+ case .notActivated:
+ LoginFactory.makeLoginView()
+ case .activatedNotOnboarded:
+ // TODO: Replace this view with the Onboarding Vpn Profile installation view
+ VStack {
+ Text("Show Onboarding vpn installation view")
+ Button {
+ viewModel.installVpnProfile()
+ } label: {
+ Text("Install Vpn profile")
+ }
+
+ }
+ case .activated:
+ DashboardFactory.makeDashboardView()
+
}
- case .notActivated:
- LoginFactory.makeLoginView()
- case .activatedNotOnboarded:
- // TODO: Replace this view with the Onboarding Vpn Profile installation view
- VStack {
- Text("Show Onboarding vpn installation view")
+
+
+ }.onChange(of: scenePhase) { newPhase in
+
+ if newPhase == .active {
+ NSLog(">>> Active")
+ viewModel.phaseDidBecomeActive()
+ } else if newPhase == .inactive {
+ NSLog(">>> Inactive")
+ } else if newPhase == .background {
+ NSLog(">>> Background")
}
- case .activated:
- DashboardFactory.makeDashboardView()
}
}
-
-
}
diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj
index ed8e83ffb..9b3d4d811 100644
--- a/PIA VPN.xcodeproj/project.pbxproj
+++ b/PIA VPN.xcodeproj/project.pbxproj
@@ -176,6 +176,16 @@
692483202AB05F18002A0407 /* PIAConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6924831F2AB05F18002A0407 /* PIAConnectionView.swift */; };
692483222AB05F37002A0407 /* PIACircleIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483212AB05F37002A0407 /* PIACircleIcon.swift */; };
692483262AB05F85002A0407 /* PIAConnectionActivityWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */; };
+ 693CA5AA2B3ED5F600D38378 /* Bootstrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E98BB6D1FD5BC6200B41D6B /* Bootstrapper.swift */; };
+ 693CA5AB2B3ED67A00D38378 /* AppPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFDC1DF1FE4A450007C0B9B /* AppPreferences.swift */; };
+ 693CA5AC2B3ED69B00D38378 /* AppConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFDC1EB1FE4B9DC007C0B9B /* AppConstants.swift */; };
+ 693CA5AD2B3ED74100D38378 /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFDC1EE1FE4B9E6007C0B9B /* AppConfiguration.swift */; };
+ 693CA5AE2B3ED75500D38378 /* Flags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E492C661FE60907007F23DF /* Flags.swift */; };
+ 693CA5B02B3EDB4900D38378 /* RegionFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD271EF21D6718F00B6D20F /* RegionFilter.swift */; };
+ 693CA5B12B3EDBEF00D38378 /* ThemeCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECF5C072017EBAD0047596C /* ThemeCode.swift */; };
+ 693CA5B72B3F268400D38378 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD606AB921C7A17900E0781D /* NetworkExtension.framework */; };
+ 693CA5BA2B3F268400D38378 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693CA5B92B3F268400D38378 /* PacketTunnelProvider.swift */; };
+ 693CA5BF2B3F268400D38378 /* PIA-VPN-tvOS-NetworkExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 693CA5B62B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
694AC74E2B17AB9C007E7B56 /* DashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 694AC74D2B17AB9C007E7B56 /* DashboardView.swift */; };
694AC74F2B17ADF9007E7B56 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 291C6397183EBC210039EC03 /* Images.xcassets */; };
695BF81D2AC30EFB00D1139C /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E7EC043209326E30029811E /* Localizable.strings */; };
@@ -657,6 +667,13 @@
remoteGlobalIDString = 0EFB606F203D7A2C0095398C;
remoteInfo = "PIA VPN AdBlocker";
};
+ 693CA5BD2B3F268400D38378 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 291C6374183EBC210039EC03 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 693CA5B52B3F268300D38378;
+ remoteInfo = "PIA-VPN-tvOS-NetworkExtension";
+ };
69CB24A22B051CED00D83A52 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 291C6374183EBC210039EC03 /* Project object */;
@@ -747,6 +764,17 @@
name = "Embed App Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
+ 693CA5C32B3F268400D38378 /* Embed Foundation Extensions */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 13;
+ files = (
+ 693CA5BF2B3F268400D38378 /* PIA-VPN-tvOS-NetworkExtension.appex in Embed Foundation Extensions */,
+ );
+ name = "Embed Foundation Extensions";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
@@ -899,6 +927,11 @@
6924831F2AB05F18002A0407 /* PIAConnectionView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAConnectionView.swift; sourceTree = ""; tabWidth = 4; };
692483212AB05F37002A0407 /* PIACircleIcon.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIACircleIcon.swift; sourceTree = ""; tabWidth = 4; };
692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAConnectionActivityWidget.swift; sourceTree = ""; tabWidth = 4; };
+ 693CA5AF2B3ED9E100D38378 /* PIA VPN-tvOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "PIA VPN-tvOS.entitlements"; sourceTree = ""; };
+ 693CA5B62B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "PIA-VPN-tvOS-NetworkExtension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 693CA5B92B3F268400D38378 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = ""; };
+ 693CA5BB2B3F268400D38378 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 693CA5BC2B3F268400D38378 /* PIA_VPN_tvOS_NetworkExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PIA_VPN_tvOS_NetworkExtension.entitlements; sourceTree = ""; };
6947AADB2ACDC8AE001BCC66 /* PIA-VPN-e2e-simulator.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = "PIA-VPN-e2e-simulator.xctestplan"; sourceTree = ""; };
694AC74D2B17AB9C007E7B56 /* DashboardView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = DashboardView.swift; sourceTree = ""; tabWidth = 4; };
696E8F0C2B31A8760080BB31 /* NotificationCenterMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterMock.swift; sourceTree = ""; };
@@ -1286,6 +1319,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 693CA5B32B3F268300D38378 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 693CA5B72B3F268400D38378 /* NetworkExtension.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
69B70AAD2ACBF51C0072A09D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -1554,6 +1595,7 @@
E5C507782B0E144E00200A6A /* PIA VPN-tvOS */,
E5C507892B0E145100200A6A /* PIA VPN-tvOSTests */,
E5C507932B0E145100200A6A /* PIA VPN-tvOSUITests */,
+ 693CA5B82B3F268400D38378 /* PIA-VPN-tvOS-NetworkExtension */,
291C637E183EBC210039EC03 /* Frameworks */,
291C637D183EBC210039EC03 /* Products */,
);
@@ -1573,6 +1615,7 @@
E5C507772B0E144D00200A6A /* PIA VPN-tvOS.app */,
E5C507862B0E145100200A6A /* PIA VPN-tvOSTests.xctest */,
E5C507902B0E145100200A6A /* PIA VPN-tvOSUITests.xctest */,
+ 693CA5B62B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension.appex */,
);
name = Products;
sourceTree = "";
@@ -1749,6 +1792,16 @@
path = Core;
sourceTree = "";
};
+ 693CA5B82B3F268400D38378 /* PIA-VPN-tvOS-NetworkExtension */ = {
+ isa = PBXGroup;
+ children = (
+ 693CA5B92B3F268400D38378 /* PacketTunnelProvider.swift */,
+ 693CA5BB2B3F268400D38378 /* Info.plist */,
+ 693CA5BC2B3F268400D38378 /* PIA_VPN_tvOS_NetworkExtension.entitlements */,
+ );
+ path = "PIA-VPN-tvOS-NetworkExtension";
+ sourceTree = "";
+ };
694AC74B2B17AB73007E7B56 /* Dashboard */ = {
isa = PBXGroup;
children = (
@@ -2284,6 +2337,7 @@
E5C507782B0E144E00200A6A /* PIA VPN-tvOS */ = {
isa = PBXGroup;
children = (
+ 693CA5AF2B3ED9E100D38378 /* PIA VPN-tvOS.entitlements */,
69A226B92B307C8A0065EDDB /* RootContainer */,
69A226AF2B3074110065EDDB /* Navigation */,
E5C507A32B153A1800200A6A /* Login */,
@@ -2576,6 +2630,23 @@
productReference = 291C637C183EBC210039EC03 /* PIA VPN.app */;
productType = "com.apple.product-type.application";
};
+ 693CA5B52B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 693CA5C02B3F268400D38378 /* Build configuration list for PBXNativeTarget "PIA-VPN-tvOS-NetworkExtension" */;
+ buildPhases = (
+ 693CA5B22B3F268300D38378 /* Sources */,
+ 693CA5B32B3F268300D38378 /* Frameworks */,
+ 693CA5B42B3F268300D38378 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "PIA-VPN-tvOS-NetworkExtension";
+ productName = "PIA-VPN-tvOS-NetworkExtension";
+ productReference = 693CA5B62B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension.appex */;
+ productType = "com.apple.product-type.tv-app-extension";
+ };
69B70AAF2ACBF51C0072A09D /* PIA-VPN_E2E_Tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 69B70ABA2ACBF51C0072A09D /* Build configuration list for PBXNativeTarget "PIA-VPN_E2E_Tests" */;
@@ -2643,10 +2714,12 @@
E5C507732B0E144D00200A6A /* Sources */,
E5C507742B0E144D00200A6A /* Frameworks */,
E5C507752B0E144D00200A6A /* Resources */,
+ 693CA5C32B3F268400D38378 /* Embed Foundation Extensions */,
);
buildRules = (
);
dependencies = (
+ 693CA5BE2B3F268400D38378 /* PBXTargetDependency */,
);
name = "PIA VPN-tvOS";
packageProductDependencies = (
@@ -2699,7 +2772,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = PIA;
- LastSwiftUpdateCheck = 1500;
+ LastSwiftUpdateCheck = 1510;
LastUpgradeCheck = 0930;
ORGANIZATIONNAME = "Private Internet Access Inc.";
TargetAttributes = {
@@ -2769,6 +2842,9 @@
};
};
};
+ 693CA5B52B3F268300D38378 = {
+ CreatedOnToolsVersion = 15.1;
+ };
69B70AAF2ACBF51C0072A09D = {
CreatedOnToolsVersion = 14.3.1;
TestTargetID = 291C637B183EBC210039EC03;
@@ -2848,6 +2924,7 @@
E5C507762B0E144D00200A6A /* PIA VPN-tvOS */,
E5C507852B0E145100200A6A /* PIA VPN-tvOSTests */,
E5C5078F2B0E145100200A6A /* PIA VPN-tvOSUITests */,
+ 693CA5B52B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension */,
);
};
/* End PBXProject section */
@@ -2984,6 +3061,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 693CA5B42B3F268300D38378 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
69B70AAE2ACBF51C0072A09D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -3449,6 +3533,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 693CA5B22B3F268300D38378 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 693CA5BA2B3F268400D38378 /* PacketTunnelProvider.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
69B70AAC2ACBF51C0072A09D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -3531,6 +3623,7 @@
E5AB288C2B2A487A00744E5F /* LoginViewModelErrorHandler.swift in Sources */,
E5AB28732B279B7000744E5F /* LoginProvider.swift in Sources */,
E5C507A52B153A2F00200A6A /* LoginViewModel.swift in Sources */,
+ 693CA5AE2B3ED75500D38378 /* Flags.swift in Sources */,
E5C507B72B17E75B00200A6A /* CheckLoginAvailability.swift in Sources */,
E5AB288E2B2A489500744E5F /* LoginStatus.swift in Sources */,
69FF0B132B3AF2050074AA04 /* QuickConnectButtonViewModel.swift in Sources */,
@@ -3540,8 +3633,10 @@
69A226B82B3079EA0065EDDB /* PIA+String.swift in Sources */,
69F1C29C2B2B299300E924AE /* VpnConnectionUseCase.swift in Sources */,
69A226B32B3074D30065EDDB /* AppRouter.swift in Sources */,
+ 693CA5AB2B3ED67A00D38378 /* AppPreferences.swift in Sources */,
E5AB288A2B29BDED00744E5F /* Foundation+PIA.swift in Sources */,
E5C507A22B0FE40000200A6A /* LoginView.swift in Sources */,
+ 693CA5AD2B3ED74100D38378 /* AppConfiguration.swift in Sources */,
E5AB287D2B28E4E700744E5F /* UserAccountMapper.swift in Sources */,
69FF0B0A2B3AD3F60074AA04 /* QuickConnectViewModel.swift in Sources */,
699311A02B31894D00D316C8 /* Foundation+Protocols.swift in Sources */,
@@ -3553,9 +3648,11 @@
69FF0B0D2B3AD57C0074AA04 /* SelectedServerUseCase.swift in Sources */,
69FF0B112B3AF1F00074AA04 /* QuickConnectButton.swift in Sources */,
E5AB28712B279B4000744E5F /* LoginProviderType.swift in Sources */,
+ 693CA5B02B3EDB4900D38378 /* RegionFilter.swift in Sources */,
E5AB287B2B28B4C400744E5F /* Credentials.swift in Sources */,
E5C5077C2B0E144E00200A6A /* ContentView.swift in Sources */,
69A226BE2B307D5F0065EDDB /* RootContainerView.swift in Sources */,
+ 693CA5AA2B3ED5F600D38378 /* Bootstrapper.swift in Sources */,
694AC74E2B17AB9C007E7B56 /* DashboardView.swift in Sources */,
69F1C2932B2B10D400E924AE /* DashboardFactory.swift in Sources */,
E5C5077A2B0E144E00200A6A /* PIA_VPN_tvOSApp.swift in Sources */,
@@ -3563,8 +3660,10 @@
69A226B52B3075AB0065EDDB /* RootContainerViewModel.swift in Sources */,
E5C507C72B1FABFF00200A6A /* LoginDomainErrorMapperType.swift in Sources */,
69F1C2972B2B239000E924AE /* PIAConnectionButtonViewModel.swift in Sources */,
+ 693CA5B12B3EDBEF00D38378 /* ThemeCode.swift in Sources */,
E5C507B32B17E5AD00200A6A /* LoginError.swift in Sources */,
E5C507CA2B1FAC3600200A6A /* LoginDomainErrorMapper.swift in Sources */,
+ 693CA5AC2B3ED69B00D38378 /* AppConstants.swift in Sources */,
69FF0B0F2B3AE8F60074AA04 /* PIAImages+SwiftUI.swift in Sources */,
E5AB28772B28B49A00744E5F /* UserAccount.swift in Sources */,
69A226C02B307DBF0065EDDB /* RootContainerFactory.swift in Sources */,
@@ -3642,6 +3741,11 @@
target = 0EFB606F203D7A2C0095398C /* PIA VPN AdBlocker */;
targetProxy = 0EFB607E203D893E0095398C /* PBXContainerItemProxy */;
};
+ 693CA5BE2B3F268400D38378 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 693CA5B52B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension */;
+ targetProxy = 693CA5BD2B3F268400D38378 /* PBXContainerItemProxy */;
+ };
69CB24A32B051CED00D83A52 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 291C637B183EBC210039EC03 /* PIA VPN */;
@@ -4307,6 +4411,95 @@
};
name = Release;
};
+ 693CA5C12B3F268400D38378 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_ENTITLEMENTS = "PIA-VPN-tvOS-NetworkExtension/PIA_VPN_tvOS_NetworkExtension.entitlements";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ DEVELOPMENT_TEAM = 5357M5NW9W;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = "PIA-VPN-tvOS-NetworkExtension/Info.plist";
+ INFOPLIST_KEY_CFBundleDisplayName = "PIA-VPN-tvOS-NetworkExtension";
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Private Internet Access Inc. All rights reserved.";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN-tvOS.PacketTunnel";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = appletvos;
+ SKIP_INSTALL = YES;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = 3;
+ TVOS_DEPLOYMENT_TARGET = 17.2;
+ };
+ name = Debug;
+ };
+ 693CA5C22B3F268400D38378 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_ENTITLEMENTS = "PIA-VPN-tvOS-NetworkExtension/PIA_VPN_tvOS_NetworkExtension.entitlements";
+ CODE_SIGN_STYLE = Automatic;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DEVELOPMENT_TEAM = 5357M5NW9W;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = "PIA-VPN-tvOS-NetworkExtension/Info.plist";
+ INFOPLIST_KEY_CFBundleDisplayName = "PIA-VPN-tvOS-NetworkExtension";
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Private Internet Access Inc. All rights reserved.";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN-tvOS.PacketTunnel";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = appletvos;
+ SKIP_INSTALL = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = 3;
+ TVOS_DEPLOYMENT_TARGET = 17.2;
+ };
+ name = Release;
+ };
69B70AB82ACBF51C0072A09D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -4535,6 +4728,7 @@
E5C507982B0E145100200A6A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
@@ -4545,6 +4739,7 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_ENTITLEMENTS = "PIA VPN-tvOS/PIA VPN-tvOS.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
@@ -4580,6 +4775,7 @@
E5C507992B0E145100200A6A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
@@ -4590,6 +4786,7 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_ENTITLEMENTS = "PIA VPN-tvOS/PIA VPN-tvOS.entitlements";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
@@ -4826,6 +5023,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 693CA5C02B3F268400D38378 /* Build configuration list for PBXNativeTarget "PIA-VPN-tvOS-NetworkExtension" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 693CA5C12B3F268400D38378 /* Debug */,
+ 693CA5C22B3F268400D38378 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
69B70ABA2ACBF51C0072A09D /* Build configuration list for PBXNativeTarget "PIA-VPN_E2E_Tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/PIA VPN/AppConfiguration.swift b/PIA VPN/AppConfiguration.swift
index ce0f3cc10..508340833 100644
--- a/PIA VPN/AppConfiguration.swift
+++ b/PIA VPN/AppConfiguration.swift
@@ -22,8 +22,10 @@
import Foundation
import PIALibrary
+#if canImport(TunnelKitCore)
import TunnelKitCore
import TunnelKitOpenVPN
+#endif
import UIKit
struct AppConfiguration {
@@ -66,7 +68,8 @@ struct AppConfiguration {
}
static let profileName = "Private Internet Access"
-
+
+#if os(iOS)
static let piaDefaultConfigurationBuilder: OpenVPNProvider.ConfigurationBuilder = {
var sessionBuilder = OpenVPN.ConfigurationBuilder()
sessionBuilder.renegotiatesAfter = piaRenegotiationInterval
@@ -88,11 +91,13 @@ struct AppConfiguration {
return builder
}()
+
static let piaAutomaticProtocols: [EndpointProtocol] = [
// let vpnPorts = Client.providers.serverProvider.currentServersConfiguration.vpnPorts
EndpointProtocol(.udp, 8080),
EndpointProtocol(.tcp, 443)
]
+#endif
private static let piaCustomRenegotiation: Renegotiation = .qa
diff --git a/PIA VPN/AppConstants.swift b/PIA VPN/AppConstants.swift
index bd3dd4da1..aef05c744 100644
--- a/PIA VPN/AppConstants.swift
+++ b/PIA VPN/AppConstants.swift
@@ -144,7 +144,7 @@ struct AppConstants {
return servers
}()
}
-
+#if os(iOS)
struct Fonts {
static let typeface: Theme.Typeface = {
let typeface = Theme.Typeface()
@@ -153,6 +153,7 @@ struct AppConstants {
return typeface
}()
}
+#endif
struct VPNWidget {
static let vpnStatus = "vpn.status"
diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift
index bae1dd396..48236e3ec 100644
--- a/PIA VPN/AppPreferences.swift
+++ b/PIA VPN/AppPreferences.swift
@@ -22,8 +22,10 @@
import Foundation
import PIALibrary
+#if canImport(TunnelKitCore)
import TunnelKitCore
import TunnelKitOpenVPN
+#endif
import SwiftyBeaver
import Intents
import UIKit
@@ -141,7 +143,7 @@ class AppPreferences {
defaults.set(newValue, forKey: Entries.didAskToEnableNotifications)
}
}
-
+#if os(iOS)
var currentThemeCode: ThemeCode {
get {
let rawCode = defaults.integer(forKey: Entries.themeCode)
@@ -151,7 +153,7 @@ class AppPreferences {
defaults.set(newValue.rawValue, forKey: Entries.themeCode)
}
}
-
+#endif
var lastVPNConnectionStatus: PIALibrary.VPNStatus {
get {
guard let rawValue = defaults.string(forKey: Entries.lastVPNConnectionStatus) else {
@@ -163,7 +165,7 @@ class AppPreferences {
defaults.set(newValue.rawValue, forKey: Entries.lastVPNConnectionStatus)
}
}
-
+#if os(iOS)
// nil = automatic
var piaSocketType: SocketType? {
get {
@@ -180,7 +182,7 @@ class AppPreferences {
}
}
}
-
+
var piaHandshake: OpenVPN.Configuration.Handshake {
get {
guard let rawValue = defaults.string(forKey: Entries.piaHandshake) else {
@@ -193,7 +195,7 @@ class AppPreferences {
defaults.set(newValue.rawValue, forKey: Entries.piaHandshake)
}
}
-
+#endif
var favoriteServerIdentifiersGen4: [String] {
get {
let keychain = PIALibrary.Keychain(team: AppConstants.teamId, group: AppConstants.appGroup)
@@ -343,7 +345,7 @@ class AppPreferences {
}
}
}
-
+#if os(iOS)
@available(iOS 12.0, *)
var connectShortcut: INVoiceShortcut? {
get {
@@ -376,7 +378,7 @@ class AppPreferences {
defaults.set(encodedObject, forKey: Entries.disconnectShortcut)
}
} }
-
+#endif
var quickSettingThemeVisible: Bool{
get {
return defaults.bool(forKey: Entries.quickSettingThemeVisible)
@@ -664,7 +666,7 @@ class AppPreferences {
try? keychain.set(favorites: favorites)
}
}
-
+ #if os(iOS)
func migrateOVPN() {
guard let currentOpenVPNConfiguration = Client.preferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNProvider.Configuration ??
@@ -704,6 +706,7 @@ class AppPreferences {
}
}
+ #endif
func migrateNMT() {
@@ -829,6 +832,7 @@ class AppPreferences {
}
func reset() {
+ #if os(iOS)
piaHandshake = .rsa4096
piaSocketType = nil
favoriteServerIdentifiersGen4 = []
@@ -842,6 +846,7 @@ class AppPreferences {
transitionTheme(to: .light)
return
}
+ #endif
quickSettingThemeVisible = true
quickSettingKillswitchVisible = true
quickSettingNetworkToolVisible = true
@@ -862,13 +867,16 @@ class AppPreferences {
showServiceMessages = false
dedicatedTokenIPReleation = [:]
appEnvironmentIsProduction = Client.environment == .production ? true : false
+ #if os(iOS)
MessagesManager.shared.reset()
+ #endif
userInteractedWithSurvey = false
successConnectionsUntilSurvey = nil
Client.preferences.lastKnownException = nil
}
func clean() {
+ #if os(iOS)
piaHandshake = .rsa4096
piaSocketType = nil
favoriteServerIdentifiersGen4 = []
@@ -882,6 +890,7 @@ class AppPreferences {
todayWidgetVpnPort = "500"
todayWidgetVpnSocket = "UDP"
todayWidgetTrustedNetwork = false
+ #endif
quickSettingThemeVisible = true
quickSettingKillswitchVisible = true
quickSettingNetworkToolVisible = true
@@ -900,7 +909,9 @@ class AppPreferences {
showServiceMessages = false
dismissedMessages = []
dedicatedTokenIPReleation = [:]
+ #if os(iOS)
MessagesManager.shared.reset()
+ #endif
appEnvironmentIsProduction = Client.environment == .production ? true : false
userInteractedWithSurvey = false
successConnectionsUntilSurvey = nil
@@ -908,7 +919,7 @@ class AppPreferences {
}
// + (void)eraseForTesting;
-
+#if os(iOS)
func transitionTheme(to code: ThemeCode, withDuration duration: Double = AppConfiguration.Animations.duration) {
guard !isTransitioningTheme else {
return
@@ -947,6 +958,7 @@ class AppPreferences {
}
}
}
+#endif
// MARK: Connections
func incrementSuccessConnections() {
diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift
index 3d974406d..bb518bf13 100644
--- a/PIA VPN/Bootstrapper.swift
+++ b/PIA VPN/Bootstrapper.swift
@@ -22,10 +22,13 @@
import Foundation
import PIALibrary
+#if canImport(TunnelKitCore)
import TunnelKitCore
import TunnelKitOpenVPN
-import SwiftyBeaver
import PIAWireguard
+#endif
+import SwiftyBeaver
+
class Bootstrapper {
@@ -87,7 +90,7 @@ class Bootstrapper {
AppPreferences.shared.migrateNMT()
// PIALibrary
-
+ #if os(iOS)
guard let bundledRegionsURL = AppConstants.RegionsGEN4.bundleURL else {
fatalError("Could not find bundled regions file")
}
@@ -97,6 +100,7 @@ class Bootstrapper {
} catch let e {
fatalError("Could not parse bundled regions file: \(e)")
}
+ #endif
Client.configuration.rsa4096Certificate = rsa4096Certificate()
@@ -127,20 +131,25 @@ class Bootstrapper {
Client.configuration.enablesConnectivityUpdates = true
Client.configuration.enablesServerUpdates = true
Client.configuration.enablesServerPings = true
+ #if os(iOS)
Client.configuration.bundledServersJSON = bundledServersJSON
+ #endif
Client.configuration.webTimeout = AppConfiguration.ClientConfiguration.webTimeout
Client.configuration.vpnProfileName = AppConfiguration.VPN.profileName
+ #if os(iOS)
Client.configuration.addVPNProfile(PIATunnelProfile(bundleIdentifier: AppConstants.Extensions.tunnelBundleIdentifier))
Client.configuration.addVPNProfile(PIAWGTunnelProfile(bundleIdentifier: AppConstants.Extensions.tunnelWireguardBundleIdentifier))
-
+ #endif
let defaults = Client.preferences.defaults
defaults.isPersistentConnection = true
defaults.mace = false
defaults.vpnType = IKEv2Profile.vpnType
+ #if os(iOS)
defaults.vpnCustomConfigurations = [
PIATunnelProfile.vpnType: AppConfiguration.VPN.piaDefaultConfigurationBuilder.build(),
PIAWGTunnelProfile.vpnType: PIAWireguardConfiguration(customDNSServers: [], packetSize: AppConstants.WireGuardPacketSize.defaultPacketSize)
]
+ #endif
if Client.preferences.shareServiceQualityData {
ServiceQualityManager.shared.start()
@@ -162,6 +171,7 @@ class Bootstrapper {
})
//FORCE THE MIGRATION TO GEN4
+ #if os(iOS)
if Client.providers.vpnProvider.needsMigrationToGEN4() {
Client.preferences.displayedServer = Server.automatic
@@ -171,7 +181,7 @@ class Bootstrapper {
Client.providers.vpnProvider.reconnect(after: 200, forceDisconnect: true, { _ in
})
}
-
+ #endif
Client.providers.accountProvider.subscriptionInformation { [weak self] (info, error) in
if let _ = error {
@@ -217,25 +227,28 @@ class Bootstrapper {
}
pref.commit()
-
+ #if os(iOS)
AppPreferences.shared.migrateOVPN()
-
+
// Business objects
AccountObserver.shared.start()
// DataCounter.shared.startCounting()
-
+ #endif
// Notifications
let nc = NotificationCenter.default
+ #if os(iOS)
nc.addObserver(self, selector: #selector(reloadTheme), name: .PIAThemeDidChange, object: nil)
+ #endif
nc.addObserver(self, selector: #selector(vpnStatusDidChange(notification:)), name: .PIADaemonsDidUpdateVPNStatus, object: nil)
nc.addObserver(self, selector: #selector(internetUnreachable(notification:)), name: .ConnectivityDaemonDidGetUnreachable, object: nil)
nc.addObserver(self, selector: #selector(internetReachable(notification:)), name: .ConnectivityDaemonDidGetReachable, object: nil)
// PIALibrary (Theme)
-
+ #if os(iOS)
AppPreferences.shared.currentThemeCode.apply(theme: Theme.current, reload: true)
+ #endif
// show walkthrough on upgrade except for logged in users
if Client.providers.accountProvider.isLoggedIn {
@@ -267,36 +280,49 @@ class Bootstrapper {
// MARK: Certificate
func rsa4096Certificate() -> String? {
+ #if os(iOS)
return AppPreferences.shared.piaHandshake.pemString()
+ #else
+ // FIXME: Implement for tvOS
+ return nil
+ #endif
}
// MARK: Notifications
-
+#if os(iOS)
@objc private func reloadTheme() {
Theme.current.applySideMenu()
Theme.current.applyAppearance()
}
-
+#endif
@objc private func vpnStatusDidChange(notification: Notification) {
let vpnStatus = Client.providers.vpnProvider.vpnStatus
switch vpnStatus {
case .connected:
AppPreferences.shared.incrementSuccessConnections()
+ #if os(iOS)
UserSurveyManager.shared.handleConnectionSuccess()
+ #endif
case .disconnected:
AppPreferences.shared.incrementSuccessDisconnections()
default:
break
}
+ #if os(iOS)
RatingManager.shared.handleConnectionStatusChanged()
+ #endif
}
@objc private func internetReachable(notification: Notification) {
+ #if os(iOS)
Macros.removeStickyNote()
+ #endif
}
@objc private func internetUnreachable(notification: Notification) {
+ #if os(iOS)
Macros.displayStickyNote(withMessage: L10n.Localizable.Global.unreachable,
andImage: Asset.Images.iconWarning.image)
+ #endif
}
}
diff --git a/PIA-VPN-tvOS-NetworkExtension/Info.plist b/PIA-VPN-tvOS-NetworkExtension/Info.plist
new file mode 100644
index 000000000..3059459e1
--- /dev/null
+++ b/PIA-VPN-tvOS-NetworkExtension/Info.plist
@@ -0,0 +1,13 @@
+
+
+
+
+ NSExtension
+
+ NSExtensionPointIdentifier
+ com.apple.networkextension.packet-tunnel
+ NSExtensionPrincipalClass
+ $(PRODUCT_MODULE_NAME).PacketTunnelProvider
+
+
+
diff --git a/PIA-VPN-tvOS-NetworkExtension/PIA_VPN_tvOS_NetworkExtension.entitlements b/PIA-VPN-tvOS-NetworkExtension/PIA_VPN_tvOS_NetworkExtension.entitlements
new file mode 100644
index 000000000..448ac3e95
--- /dev/null
+++ b/PIA-VPN-tvOS-NetworkExtension/PIA_VPN_tvOS_NetworkExtension.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.application-groups
+
+ group.com.privateinternetaccess
+
+
+
diff --git a/PIA-VPN-tvOS-NetworkExtension/PacketTunnelProvider.swift b/PIA-VPN-tvOS-NetworkExtension/PacketTunnelProvider.swift
new file mode 100644
index 000000000..a9753e481
--- /dev/null
+++ b/PIA-VPN-tvOS-NetworkExtension/PacketTunnelProvider.swift
@@ -0,0 +1,37 @@
+//
+// PacketTunnelProvider.swift
+// PIA-VPN-tvOS-NetworkExtension
+//
+// Created by Laura S on 12/29/23.
+// Copyright © 2023 Private Internet Access Inc. All rights reserved.
+//
+
+import NetworkExtension
+
+class PacketTunnelProvider: NEPacketTunnelProvider {
+
+ override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
+ // Add code here to start the process of connecting the tunnel.
+ }
+
+ override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
+ // Add code here to start the process of stopping the tunnel.
+ completionHandler()
+ }
+
+ override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
+ // Add code here to handle the message.
+ if let handler = completionHandler {
+ handler(messageData)
+ }
+ }
+
+ override func sleep(completionHandler: @escaping () -> Void) {
+ // Add code here to get ready to sleep.
+ completionHandler()
+ }
+
+ override func wake() {
+ // Add code here to wake up.
+ }
+}