Separate App/Tunnel responsibilities (#984)
Sort out the increasing mess coming from: - AppContext* - Dependencies - Shared* by doing the following: - Keep in the "Shared" folder only the entities actually shared by App/Tunnel - Create TunnelContext - Move AppContext and related to the App/Context folder - Move TunnelContext and related to the Tunnel/Context folder - Delete Shared+* extensions, use AppContext/TunnelContext singletons from the app - Create a Dependencies factory singleton to create entities in a single place - Split extensions by domain - Make it clear with `func` vs `var` when a dependency method returns a new instance
This commit is contained in:
parent
aac04c4008
commit
f7013a98a9
|
@ -27,7 +27,7 @@ import Foundation
|
|||
import PassepartoutKit
|
||||
|
||||
@MainActor
|
||||
public protocol ProfileProcessor {
|
||||
public protocol ProfileProcessor: Sendable {
|
||||
func isIncluded(_ profile: Profile) -> Bool
|
||||
|
||||
func preview(from profile: Profile) -> ProfilePreview
|
||||
|
@ -38,12 +38,12 @@ public protocol ProfileProcessor {
|
|||
}
|
||||
|
||||
@MainActor
|
||||
public protocol AppTunnelProcessor {
|
||||
public protocol AppTunnelProcessor: Sendable {
|
||||
func title(for profile: Profile) -> String
|
||||
|
||||
func willInstall(_ profile: Profile) throws -> Profile
|
||||
}
|
||||
|
||||
public protocol PacketTunnelProcessor {
|
||||
public protocol PacketTunnelProcessor: Sendable {
|
||||
nonisolated func willStart(_ profile: Profile) throws -> Profile
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ import PassepartoutKit
|
|||
import UITesting
|
||||
|
||||
@MainActor
|
||||
public final class AppContext: ObservableObject {
|
||||
public final class AppContext: ObservableObject, Sendable {
|
||||
public let iapManager: IAPManager
|
||||
|
||||
public let migrationManager: MigrationManager
|
||||
|
|
|
@ -213,7 +213,7 @@ extension ProfileEditor {
|
|||
preferences = try preferencesManager.preferences(forProfile: profile)
|
||||
} catch {
|
||||
preferences = [:]
|
||||
pp_log(.app, .error, "Unable to load preferences for profile \(profile.id): \(error)")
|
||||
pp_log(.App.profiles, .error, "Unable to load preferences for profile \(profile.id): \(error)")
|
||||
}
|
||||
removedModules = [:]
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ extension ProfileEditor {
|
|||
}
|
||||
return newProfile
|
||||
} catch {
|
||||
pp_log(.app, .fault, "Unable to save edited profile: \(error)")
|
||||
pp_log(.App.profiles, .fault, "Unable to save edited profile: \(error)")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
0E3E22982CE53510005135DF /* AppUITV in Frameworks */ = {isa = PBXBuildFile; platformFilters = (tvos, ); productRef = 0E3E22972CE53510005135DF /* AppUITV */; };
|
||||
0E3FF4BA2CE3AFBC00BFF640 /* Profiles.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = 0E3FF4B72CE3AFBC00BFF640 /* Profiles.sqlite */; };
|
||||
0E3FF4BB2CE3AFBC00BFF640 /* MigrationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3FF4B92CE3AFBC00BFF640 /* MigrationManagerTests.swift */; };
|
||||
0E483E812CE64D6B00584B32 /* Shared+Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E483E7F2CE64D6B00584B32 /* Shared+Tunnel.swift */; };
|
||||
0E483E842CE6501100584B32 /* Shared+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E483E822CE6501100584B32 /* Shared+App.swift */; };
|
||||
0E483E812CE64D6B00584B32 /* TunnelContext+Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E483E7F2CE64D6B00584B32 /* TunnelContext+Shared.swift */; };
|
||||
0E60512C2CE5393C00F763D4 /* PassepartoutImplementations in Frameworks */ = {isa = PBXBuildFile; productRef = 0E60512B2CE5393C00F763D4 /* PassepartoutImplementations */; };
|
||||
0E6EEEE32CF8CABA0076E2B0 /* AppContext+Testing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6EEEE12CF8CABA0076E2B0 /* AppContext+Testing.swift */; };
|
||||
0E6EEEE42CF8CABA0076E2B0 /* ProfileManager+Testing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6EEEE22CF8CABA0076E2B0 /* ProfileManager+Testing.swift */; };
|
||||
|
@ -29,6 +28,16 @@
|
|||
0E7F46122CF7F44C00B1C53A /* AppScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F46102CF7F44C00B1C53A /* AppScreen.swift */; };
|
||||
0E81955A2CFDA75200CC8FFD /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8195592CFDA75200CC8FFD /* Dependencies.swift */; };
|
||||
0E81955B2CFDA7BF00CC8FFD /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8195592CFDA75200CC8FFD /* Dependencies.swift */; };
|
||||
0E8DFD482D05FA7000531CDE /* TunnelContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD472D05FA7000531CDE /* TunnelContext.swift */; };
|
||||
0E8DFD4E2D05FE5A00531CDE /* Dependencies+PassepartoutKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD4C2D05FE5A00531CDE /* Dependencies+PassepartoutKit.swift */; };
|
||||
0E8DFD4F2D05FE5A00531CDE /* Dependencies+IAPManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD4B2D05FE5A00531CDE /* Dependencies+IAPManager.swift */; };
|
||||
0E8DFD502D05FE5A00531CDE /* Dependencies+Processors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD4D2D05FE5A00531CDE /* Dependencies+Processors.swift */; };
|
||||
0E8DFD512D05FE5A00531CDE /* Dependencies+CoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD492D05FE5A00531CDE /* Dependencies+CoreData.swift */; };
|
||||
0E8DFD532D05FE5A00531CDE /* Dependencies+PassepartoutKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD4C2D05FE5A00531CDE /* Dependencies+PassepartoutKit.swift */; };
|
||||
0E8DFD542D05FE5A00531CDE /* Dependencies+IAPManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD4B2D05FE5A00531CDE /* Dependencies+IAPManager.swift */; };
|
||||
0E8DFD562D05FE5A00531CDE /* Dependencies+CoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD492D05FE5A00531CDE /* Dependencies+CoreData.swift */; };
|
||||
0E8DFD592D05FF0400531CDE /* Dependencies+PreferencesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD582D05FF0400531CDE /* Dependencies+PreferencesManager.swift */; };
|
||||
0E8DFD5A2D05FF0400531CDE /* Dependencies+PreferencesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD582D05FF0400531CDE /* Dependencies+PreferencesManager.swift */; };
|
||||
0E916B782CF80FD60072921A /* ProfileEditorScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E916B772CF80FD60072921A /* ProfileEditorScreen.swift */; };
|
||||
0E916B7C2CF811EB0072921A /* XCUIElement+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E916B7B2CF811EB0072921A /* XCUIElement+Extensions.swift */; };
|
||||
0E94EE582B93554B00588243 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E3D672B9345FD002BBDB4 /* PacketTunnelProvider.swift */; };
|
||||
|
@ -42,8 +51,6 @@
|
|||
0EC332D22B8A1808000B9C2F /* PassepartoutTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0EC332C82B8A1808000B9C2F /* PassepartoutTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
0EC418D22CF86B7400AC6F2F /* ProfileMenuScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC418D12CF86B7400AC6F2F /* ProfileMenuScreen.swift */; };
|
||||
0EC797422B9378E000C093B7 /* AppContext+Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC797402B9378E000C093B7 /* AppContext+Shared.swift */; };
|
||||
0EC797432B9378E000C093B7 /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC797412B9378E000C093B7 /* Shared.swift */; };
|
||||
0EC797442B93790600C093B7 /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC797412B9378E000C093B7 /* Shared.swift */; };
|
||||
0ED61CF82CD0418C008FE259 /* App+macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED61CF72CD0418C008FE259 /* App+macOS.swift */; };
|
||||
0ED61CFA2CD04192008FE259 /* App+iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED61CF92CD04192008FE259 /* App+iOS.swift */; };
|
||||
0EDE56EA2CABE40D0082D21C /* Intents.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0EDE56E62CABE40D0082D21C /* Intents.plist */; };
|
||||
|
@ -137,8 +144,7 @@
|
|||
0E3FF4AE2CE3AF6F00BFF640 /* PassepartoutTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PassepartoutTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0E3FF4B72CE3AFBC00BFF640 /* Profiles.sqlite */ = {isa = PBXFileReference; lastKnownFileType = file; path = Profiles.sqlite; sourceTree = "<group>"; };
|
||||
0E3FF4B92CE3AFBC00BFF640 /* MigrationManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationManagerTests.swift; sourceTree = "<group>"; };
|
||||
0E483E7F2CE64D6B00584B32 /* Shared+Tunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Shared+Tunnel.swift"; sourceTree = "<group>"; };
|
||||
0E483E822CE6501100584B32 /* Shared+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Shared+App.swift"; sourceTree = "<group>"; };
|
||||
0E483E7F2CE64D6B00584B32 /* TunnelContext+Shared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelContext+Shared.swift"; sourceTree = "<group>"; };
|
||||
0E5DFDDC2CDB8F9100F2DE70 /* Passepartout.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Passepartout.storekit; sourceTree = "<group>"; };
|
||||
0E6EEEE12CF8CABA0076E2B0 /* AppContext+Testing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppContext+Testing.swift"; sourceTree = "<group>"; };
|
||||
0E6EEEE22CF8CABA0076E2B0 /* ProfileManager+Testing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileManager+Testing.swift"; sourceTree = "<group>"; };
|
||||
|
@ -159,6 +165,12 @@
|
|||
0E7F46102CF7F44C00B1C53A /* AppScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScreen.swift; sourceTree = "<group>"; };
|
||||
0E8195592CFDA75200CC8FFD /* Dependencies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dependencies.swift; sourceTree = "<group>"; };
|
||||
0E8D852F2C328CA1005493DE /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
||||
0E8DFD472D05FA7000531CDE /* TunnelContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelContext.swift; sourceTree = "<group>"; };
|
||||
0E8DFD492D05FE5A00531CDE /* Dependencies+CoreData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dependencies+CoreData.swift"; sourceTree = "<group>"; };
|
||||
0E8DFD4B2D05FE5A00531CDE /* Dependencies+IAPManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dependencies+IAPManager.swift"; sourceTree = "<group>"; };
|
||||
0E8DFD4C2D05FE5A00531CDE /* Dependencies+PassepartoutKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dependencies+PassepartoutKit.swift"; sourceTree = "<group>"; };
|
||||
0E8DFD4D2D05FE5A00531CDE /* Dependencies+Processors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dependencies+Processors.swift"; sourceTree = "<group>"; };
|
||||
0E8DFD582D05FF0400531CDE /* Dependencies+PreferencesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dependencies+PreferencesManager.swift"; sourceTree = "<group>"; };
|
||||
0E916B772CF80FD60072921A /* ProfileEditorScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileEditorScreen.swift; sourceTree = "<group>"; };
|
||||
0E916B7B2CF811EB0072921A /* XCUIElement+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCUIElement+Extensions.swift"; sourceTree = "<group>"; };
|
||||
0E94EE5C2B93570600588243 /* Tunnel.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Tunnel.plist; sourceTree = "<group>"; };
|
||||
|
@ -172,7 +184,6 @@
|
|||
0EC332C92B8A1808000B9C2F /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
|
||||
0EC418D12CF86B7400AC6F2F /* ProfileMenuScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileMenuScreen.swift; sourceTree = "<group>"; };
|
||||
0EC797402B9378E000C093B7 /* AppContext+Shared.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AppContext+Shared.swift"; sourceTree = "<group>"; };
|
||||
0EC797412B9378E000C093B7 /* Shared.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shared.swift; sourceTree = "<group>"; };
|
||||
0ED1EFDA2C33059600CBD9BD /* App.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = App.plist; sourceTree = "<group>"; };
|
||||
0ED61CF72CD0418C008FE259 /* App+macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+macOS.swift"; sourceTree = "<group>"; };
|
||||
0ED61CF92CD04192008FE259 /* App+iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+iOS.swift"; sourceTree = "<group>"; };
|
||||
|
@ -289,15 +300,6 @@
|
|||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E6EEEE62CF8CB090076E2B0 /* Testing */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E6EEEE12CF8CABA0076E2B0 /* AppContext+Testing.swift */,
|
||||
0E6EEEE22CF8CABA0076E2B0 /* ProfileManager+Testing.swift */,
|
||||
);
|
||||
path = Testing;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E757F112CD0CFFC006E13E1 /* LoginItem */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -328,6 +330,7 @@
|
|||
0E7E3D5A2B9345FD002BBDB4 /* App */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E8DFD462D05F77200531CDE /* Context */,
|
||||
0ED61CF62CD04174008FE259 /* Platforms */,
|
||||
0ED1EFDA2C33059600CBD9BD /* App.plist */,
|
||||
0E7E3D5B2B9345FD002BBDB4 /* App.entitlements */,
|
||||
|
@ -343,14 +346,11 @@
|
|||
0E7E3D612B9345FD002BBDB4 /* Shared */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E6EEEE62CF8CB090076E2B0 /* Testing */,
|
||||
0EC797402B9378E000C093B7 /* AppContext+Shared.swift */,
|
||||
0EAEC8A62D05DB8D001AA50C /* DefaultAppProcessor.swift */,
|
||||
0EAEC8A72D05DB8D001AA50C /* DefaultTunnelProcessor.swift */,
|
||||
0E8195592CFDA75200CC8FFD /* Dependencies.swift */,
|
||||
0EC797412B9378E000C093B7 /* Shared.swift */,
|
||||
0E483E822CE6501100584B32 /* Shared+App.swift */,
|
||||
0E483E7F2CE64D6B00584B32 /* Shared+Tunnel.swift */,
|
||||
0E8DFD492D05FE5A00531CDE /* Dependencies+CoreData.swift */,
|
||||
0E8DFD4B2D05FE5A00531CDE /* Dependencies+IAPManager.swift */,
|
||||
0E8DFD4C2D05FE5A00531CDE /* Dependencies+PassepartoutKit.swift */,
|
||||
0E8DFD582D05FF0400531CDE /* Dependencies+PreferencesManager.swift */,
|
||||
);
|
||||
path = Shared;
|
||||
sourceTree = "<group>";
|
||||
|
@ -358,6 +358,7 @@
|
|||
0E7E3D652B9345FD002BBDB4 /* Tunnel */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E8DFD452D05F76900531CDE /* Context */,
|
||||
0E94EE5C2B93570600588243 /* Tunnel.plist */,
|
||||
0E7E3D662B9345FD002BBDB4 /* Tunnel.entitlements */,
|
||||
0E7E3D672B9345FD002BBDB4 /* PacketTunnelProvider.swift */,
|
||||
|
@ -376,6 +377,28 @@
|
|||
path = UITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E8DFD452D05F76900531CDE /* Context */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0EAEC8A72D05DB8D001AA50C /* DefaultTunnelProcessor.swift */,
|
||||
0E8DFD472D05FA7000531CDE /* TunnelContext.swift */,
|
||||
0E483E7F2CE64D6B00584B32 /* TunnelContext+Shared.swift */,
|
||||
);
|
||||
path = Context;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E8DFD462D05F77200531CDE /* Context */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0EC797402B9378E000C093B7 /* AppContext+Shared.swift */,
|
||||
0E6EEEE12CF8CABA0076E2B0 /* AppContext+Testing.swift */,
|
||||
0EAEC8A62D05DB8D001AA50C /* DefaultAppProcessor.swift */,
|
||||
0E8DFD4D2D05FE5A00531CDE /* Dependencies+Processors.swift */,
|
||||
0E6EEEE22CF8CABA0076E2B0 /* ProfileManager+Testing.swift */,
|
||||
);
|
||||
path = Context;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E916B7A2CF811DE0072921A /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -684,6 +707,10 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0ED61CF82CD0418C008FE259 /* App+macOS.swift in Sources */,
|
||||
0E8DFD4E2D05FE5A00531CDE /* Dependencies+PassepartoutKit.swift in Sources */,
|
||||
0E8DFD4F2D05FE5A00531CDE /* Dependencies+IAPManager.swift in Sources */,
|
||||
0E8DFD502D05FE5A00531CDE /* Dependencies+Processors.swift in Sources */,
|
||||
0E8DFD512D05FE5A00531CDE /* Dependencies+CoreData.swift in Sources */,
|
||||
0E7C3CCD2C9AF44600B72E69 /* AppDelegate.swift in Sources */,
|
||||
0ED61CFA2CD04192008FE259 /* App+iOS.swift in Sources */,
|
||||
0E7E3D6B2B9345FD002BBDB4 /* PassepartoutApp.swift in Sources */,
|
||||
|
@ -693,8 +720,7 @@
|
|||
0E81955A2CFDA75200CC8FFD /* Dependencies.swift in Sources */,
|
||||
0E6EEEE32CF8CABA0076E2B0 /* AppContext+Testing.swift in Sources */,
|
||||
0E6EEEE42CF8CABA0076E2B0 /* ProfileManager+Testing.swift in Sources */,
|
||||
0E483E842CE6501100584B32 /* Shared+App.swift in Sources */,
|
||||
0EC797432B9378E000C093B7 /* Shared.swift in Sources */,
|
||||
0E8DFD592D05FF0400531CDE /* Dependencies+PreferencesManager.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -738,8 +764,12 @@
|
|||
0E81955B2CFDA7BF00CC8FFD /* Dependencies.swift in Sources */,
|
||||
0EAEC8AA2D05DB8D001AA50C /* DefaultTunnelProcessor.swift in Sources */,
|
||||
0E94EE582B93554B00588243 /* PacketTunnelProvider.swift in Sources */,
|
||||
0E483E812CE64D6B00584B32 /* Shared+Tunnel.swift in Sources */,
|
||||
0EC797442B93790600C093B7 /* Shared.swift in Sources */,
|
||||
0E483E812CE64D6B00584B32 /* TunnelContext+Shared.swift in Sources */,
|
||||
0E8DFD482D05FA7000531CDE /* TunnelContext.swift in Sources */,
|
||||
0E8DFD532D05FE5A00531CDE /* Dependencies+PassepartoutKit.swift in Sources */,
|
||||
0E8DFD542D05FE5A00531CDE /* Dependencies+IAPManager.swift in Sources */,
|
||||
0E8DFD562D05FE5A00531CDE /* Dependencies+CoreData.swift in Sources */,
|
||||
0E8DFD5A2D05FF0400531CDE /* Dependencies+PreferencesManager.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -33,8 +33,9 @@ import UITesting
|
|||
final class AppDelegate: NSObject {
|
||||
let context: AppContext = {
|
||||
if AppCommandLine.contains(.uiTesting) {
|
||||
let dependencies: Dependencies = .shared
|
||||
pp_log(.app, .info, "UI tests: mock AppContext")
|
||||
return .forUITesting(withRegistry: .shared)
|
||||
return .forUITesting(withRegistry: dependencies.registry)
|
||||
}
|
||||
return .shared
|
||||
}()
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
//
|
||||
// AppContext+Shared.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 2/24/24.
|
||||
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import AppData
|
||||
import AppDataProfiles
|
||||
import AppDataProviders
|
||||
import CommonLibrary
|
||||
import CommonUtils
|
||||
import Foundation
|
||||
import LegacyV2
|
||||
import PassepartoutKit
|
||||
import UILibrary
|
||||
import UITesting
|
||||
|
||||
extension AppContext {
|
||||
static let shared: AppContext = {
|
||||
let dependencies: Dependencies = .shared
|
||||
|
||||
let iapManager = IAPManager(
|
||||
customUserLevel: dependencies.customUserLevel,
|
||||
inAppHelper: dependencies.simulatedAppProductHelper(),
|
||||
receiptReader: dependencies.simulatedAppReceiptReader(),
|
||||
betaChecker: dependencies.betaChecker(),
|
||||
productsAtBuild: dependencies.productsAtBuild()
|
||||
)
|
||||
|
||||
let processor = dependencies.appProcessor(with: iapManager)
|
||||
|
||||
let profileManager: ProfileManager = {
|
||||
let remoteRepositoryBlock: (Bool) -> ProfileRepository = {
|
||||
let remoteStore = CoreDataPersistentStore(
|
||||
logger: dependencies.coreDataLogger(),
|
||||
containerName: Constants.shared.containers.remoteProfiles,
|
||||
model: AppData.cdProfilesModel,
|
||||
cloudKitIdentifier: $0 ? BundleConfiguration.mainString(for: .cloudKitId) : nil,
|
||||
author: nil
|
||||
)
|
||||
return AppData.cdProfileRepositoryV3(
|
||||
registry: dependencies.registry,
|
||||
coder: CodableProfileCoder(),
|
||||
context: remoteStore.context,
|
||||
observingResults: true,
|
||||
onResultError: {
|
||||
pp_log(.App.profiles, .error, "Unable to decode remote profile: \($0)")
|
||||
return .ignore
|
||||
}
|
||||
)
|
||||
}
|
||||
return ProfileManager(
|
||||
repository: dependencies.mainProfileRepository(),
|
||||
backupRepository: dependencies.backupProfileRepository(),
|
||||
remoteRepositoryBlock: remoteRepositoryBlock,
|
||||
mirrorsRemoteRepository: dependencies.mirrorsRemoteRepository,
|
||||
processor: processor
|
||||
)
|
||||
}()
|
||||
|
||||
let tunnel = ExtendedTunnel(
|
||||
tunnel: Tunnel(strategy: dependencies.tunnelStrategy()),
|
||||
environment: dependencies.tunnelEnvironment(),
|
||||
processor: processor,
|
||||
interval: Constants.shared.tunnel.refreshInterval
|
||||
)
|
||||
|
||||
let providerManager: ProviderManager = {
|
||||
let store = CoreDataPersistentStore(
|
||||
logger: dependencies.coreDataLogger(),
|
||||
containerName: Constants.shared.containers.providers,
|
||||
model: AppData.cdProvidersModel,
|
||||
cloudKitIdentifier: nil,
|
||||
author: nil
|
||||
)
|
||||
let repository = AppData.cdProviderRepositoryV3(context: store.backgroundContext)
|
||||
return ProviderManager(repository: repository)
|
||||
}()
|
||||
|
||||
let migrationManager: MigrationManager = {
|
||||
let profileStrategy = ProfileV2MigrationStrategy(
|
||||
coreDataLogger: dependencies.coreDataLogger(),
|
||||
profilesContainer: .init(
|
||||
Constants.shared.containers.legacyV2,
|
||||
BundleConfiguration.mainString(for: .legacyV2CloudKitId)
|
||||
),
|
||||
tvProfilesContainer: .init(
|
||||
Constants.shared.containers.legacyV2TV,
|
||||
BundleConfiguration.mainString(for: .legacyV2TVCloudKitId)
|
||||
)
|
||||
)
|
||||
let migrationSimulation: MigrationManager.Simulation?
|
||||
if AppCommandLine.contains(.fakeMigration) {
|
||||
migrationSimulation = MigrationManager.Simulation(
|
||||
fakeProfiles: true,
|
||||
maxMigrationTime: 3.0,
|
||||
randomFailures: true
|
||||
)
|
||||
} else {
|
||||
migrationSimulation = nil
|
||||
}
|
||||
return MigrationManager(profileStrategy: profileStrategy, simulation: migrationSimulation)
|
||||
}()
|
||||
|
||||
let preferencesManager = dependencies.preferencesManager(withCloudKit: true)
|
||||
|
||||
return AppContext(
|
||||
iapManager: iapManager,
|
||||
migrationManager: migrationManager,
|
||||
profileManager: profileManager,
|
||||
providerManager: providerManager,
|
||||
preferencesManager: preferencesManager,
|
||||
registry: dependencies.registry,
|
||||
tunnel: tunnel,
|
||||
tunnelReceiptURL: BundleConfiguration.urlForBetaReceipt
|
||||
)
|
||||
}()
|
||||
}
|
||||
|
||||
// MARK: - Dependencies
|
||||
|
||||
private extension Dependencies {
|
||||
var customUserLevel: AppUserLevel? {
|
||||
guard let userLevelString = BundleConfiguration.mainIntegerIfPresent(for: .userLevel),
|
||||
let userLevel = AppUserLevel(rawValue: userLevelString) else {
|
||||
return nil
|
||||
}
|
||||
return userLevel
|
||||
}
|
||||
|
||||
func simulatedAppProductHelper() -> any AppProductHelper {
|
||||
if AppCommandLine.contains(.fakeIAP) {
|
||||
return FakeAppProductHelper()
|
||||
}
|
||||
return appProductHelper()
|
||||
}
|
||||
|
||||
func simulatedAppReceiptReader() -> AppReceiptReader {
|
||||
if AppCommandLine.contains(.fakeIAP) {
|
||||
guard let mockHelper = simulatedAppProductHelper() as? FakeAppProductHelper else {
|
||||
fatalError("When .isFakeIAP, simulatedInAppHelper is expected to be MockAppProductHelper")
|
||||
}
|
||||
return mockHelper.receiptReader
|
||||
}
|
||||
return FallbackReceiptReader(
|
||||
main: StoreKitReceiptReader(),
|
||||
beta: betaReceiptURL.map {
|
||||
KvittoReceiptReader(url: $0)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
var betaReceiptURL: URL? {
|
||||
Bundle.main.appStoreProductionReceiptURL
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Simulator
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
|
||||
private extension Dependencies {
|
||||
func tunnelStrategy() -> TunnelObservableStrategy {
|
||||
FakeTunnelStrategy(environment: .shared, dataCountInterval: 1000)
|
||||
}
|
||||
|
||||
func mainProfileRepository() -> ProfileRepository {
|
||||
coreDataProfileRepository(observingResults: true)
|
||||
}
|
||||
|
||||
func backupProfileRepository() -> ProfileRepository? {
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// MARK: Device
|
||||
|
||||
private extension Dependencies {
|
||||
func tunnelStrategy() -> TunnelObservableStrategy {
|
||||
neStrategy()
|
||||
}
|
||||
|
||||
func mainProfileRepository() -> ProfileRepository {
|
||||
neProfileRepository()
|
||||
}
|
||||
|
||||
func backupProfileRepository() -> ProfileRepository? {
|
||||
coreDataProfileRepository(observingResults: false)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// MARK: Common
|
||||
|
||||
private extension Dependencies {
|
||||
var mirrorsRemoteRepository: Bool {
|
||||
#if os(tvOS)
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
}
|
||||
|
||||
func neProfileRepository() -> ProfileRepository {
|
||||
NEProfileRepository(repository: neStrategy()) {
|
||||
profileTitle($0)
|
||||
}
|
||||
}
|
||||
|
||||
func neStrategy() -> NETunnelStrategy {
|
||||
NETunnelStrategy(
|
||||
bundleIdentifier: BundleConfiguration.mainString(for: .tunnelId),
|
||||
coder: neProtocolCoder(),
|
||||
environment: tunnelEnvironment()
|
||||
)
|
||||
}
|
||||
|
||||
func coreDataProfileRepository(observingResults: Bool) -> ProfileRepository {
|
||||
let store = CoreDataPersistentStore(
|
||||
logger: coreDataLogger(),
|
||||
containerName: Constants.shared.containers.localProfiles,
|
||||
model: AppData.cdProfilesModel,
|
||||
cloudKitIdentifier: nil,
|
||||
author: nil
|
||||
)
|
||||
return AppData.cdProfileRepositoryV3(
|
||||
registry: registry,
|
||||
coder: CodableProfileCoder(),
|
||||
context: store.context,
|
||||
observingResults: observingResults,
|
||||
onResultError: {
|
||||
pp_log(.App.profiles, .error, "Unable to decode local profile: \($0)")
|
||||
return .ignore
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -31,21 +31,19 @@ import UILibrary
|
|||
|
||||
extension AppContext {
|
||||
static func forUITesting(withRegistry registry: Registry) -> AppContext {
|
||||
let dependencies: Dependencies = .shared
|
||||
let iapManager = IAPManager(
|
||||
customUserLevel: .subscriber,
|
||||
inAppHelper: Dependencies.IAPManager.inAppHelper,
|
||||
inAppHelper: dependencies.appProductHelper(),
|
||||
receiptReader: FakeAppReceiptReader(),
|
||||
betaChecker: TestFlightChecker(),
|
||||
productsAtBuild: { _ in
|
||||
[]
|
||||
}
|
||||
)
|
||||
let processor = DefaultAppProcessor(iapManager: iapManager) {
|
||||
$0.localizedPreview
|
||||
}
|
||||
|
||||
let profileManager: ProfileManager = .forTesting(
|
||||
withRegistry: .shared,
|
||||
let processor = dependencies.appProcessor(with: iapManager)
|
||||
let profileManager: ProfileManager = .forUITesting(
|
||||
withRegistry: dependencies.registry,
|
||||
processor: processor
|
||||
)
|
||||
let tunnelEnvironment = InMemoryEnvironment()
|
||||
|
@ -59,13 +57,14 @@ extension AppContext {
|
|||
repository: InMemoryProviderRepository()
|
||||
)
|
||||
let migrationManager = MigrationManager()
|
||||
let preferencesManager = PreferencesManager()
|
||||
|
||||
return AppContext(
|
||||
iapManager: iapManager,
|
||||
migrationManager: migrationManager,
|
||||
profileManager: profileManager,
|
||||
providerManager: providerManager,
|
||||
preferencesManager: PreferencesManager(),
|
||||
preferencesManager: preferencesManager,
|
||||
registry: registry,
|
||||
tunnel: tunnel,
|
||||
tunnelReceiptURL: BundleConfiguration.urlForBetaReceipt
|
|
@ -30,21 +30,28 @@ import PassepartoutKit
|
|||
final class DefaultAppProcessor: Sendable {
|
||||
private let iapManager: IAPManager
|
||||
|
||||
private let preview: @Sendable (Profile) -> ProfilePreview
|
||||
private let title: @Sendable (Profile) -> String
|
||||
|
||||
init(iapManager: IAPManager, preview: @escaping @Sendable (Profile) -> ProfilePreview) {
|
||||
init(
|
||||
iapManager: IAPManager,
|
||||
title: @escaping @Sendable (Profile) -> String
|
||||
) {
|
||||
self.iapManager = iapManager
|
||||
self.preview = preview
|
||||
self.title = title
|
||||
}
|
||||
}
|
||||
|
||||
extension DefaultAppProcessor: ProfileProcessor {
|
||||
func isIncluded(_ profile: Profile) -> Bool {
|
||||
Dependencies.ProfileManager.isIncluded(iapManager, profile)
|
||||
#if os(tvOS)
|
||||
profile.attributes.isAvailableForTV == true
|
||||
#else
|
||||
true
|
||||
#endif
|
||||
}
|
||||
|
||||
func preview(from profile: Profile) -> ProfilePreview {
|
||||
preview(profile)
|
||||
profile.localizedPreview
|
||||
}
|
||||
|
||||
func requiredFeatures(_ profile: Profile) -> Set<AppFeature>? {
|
||||
|
@ -65,7 +72,7 @@ extension DefaultAppProcessor: ProfileProcessor {
|
|||
|
||||
extension DefaultAppProcessor: AppTunnelProcessor {
|
||||
func title(for profile: Profile) -> String {
|
||||
Dependencies.ProfileManager.sharedTitle(profile)
|
||||
title(profile)
|
||||
}
|
||||
|
||||
func willInstall(_ profile: Profile) throws -> Profile {
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// Dependencies+Processors.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/2/24.
|
||||
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import CommonLibrary
|
||||
import Foundation
|
||||
import PassepartoutKit
|
||||
import UILibrary
|
||||
|
||||
extension Dependencies {
|
||||
func appProcessor(with iapManager: IAPManager) -> DefaultAppProcessor {
|
||||
DefaultAppProcessor(
|
||||
iapManager: iapManager,
|
||||
title: profileTitle
|
||||
)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
nonisolated func profileTitle(_ profile: Profile) -> String {
|
||||
String(format: Constants.shared.tunnel.profileTitleFormat, profile.name)
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ import Foundation
|
|||
import PassepartoutKit
|
||||
|
||||
extension ProfileManager {
|
||||
public static func forTesting(withRegistry registry: Registry, processor: ProfileProcessor) -> ProfileManager {
|
||||
public static func forUITesting(withRegistry registry: Registry, processor: ProfileProcessor) -> ProfileManager {
|
||||
let repository = InMemoryProfileRepository()
|
||||
let remoteRepository = InMemoryProfileRepository()
|
||||
let manager = ProfileManager(repository: repository, remoteRepositoryBlock: { _ in
|
||||
|
@ -67,7 +67,7 @@ extension ProfileManager {
|
|||
try await manager.save(profile, isLocal: true, remotelyShared: parameters.isShared)
|
||||
}
|
||||
} catch {
|
||||
pp_log(.App.profiles, .error, "Unable to build mock ProfileManager: \(error)")
|
||||
pp_log(.App.profiles, .error, "Unable to build ProfileManager for UI testing: \(error)")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
//
|
||||
// AppContext+Shared.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 2/24/24.
|
||||
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import AppData
|
||||
import AppDataProfiles
|
||||
import AppDataProviders
|
||||
import CommonLibrary
|
||||
import CommonUtils
|
||||
import Foundation
|
||||
import LegacyV2
|
||||
import PassepartoutKit
|
||||
import UILibrary
|
||||
import UITesting
|
||||
|
||||
// shared registry and environment are picked from Shared.swift
|
||||
|
||||
extension AppContext {
|
||||
static let shared: AppContext = {
|
||||
let iapManager: IAPManager = .sharedForApp
|
||||
let processor = DefaultAppProcessor(iapManager: iapManager) {
|
||||
$0.localizedPreview
|
||||
}
|
||||
|
||||
// MARK: ProfileManager
|
||||
|
||||
let remoteRepositoryBlock: (Bool) -> ProfileRepository = {
|
||||
let remoteStore = CoreDataPersistentStore(
|
||||
logger: .default,
|
||||
containerName: Constants.shared.containers.remoteProfiles,
|
||||
model: AppData.cdProfilesModel,
|
||||
cloudKitIdentifier: $0 ? BundleConfiguration.mainString(for: .cloudKitId) : nil,
|
||||
author: nil
|
||||
)
|
||||
return AppData.cdProfileRepositoryV3(
|
||||
registry: .shared,
|
||||
coder: CodableProfileCoder(),
|
||||
context: remoteStore.context,
|
||||
observingResults: true
|
||||
) { error in
|
||||
pp_log(.app, .error, "Unable to decode remote result: \(error)")
|
||||
return .ignore
|
||||
}
|
||||
}
|
||||
let profileManager = ProfileManager(
|
||||
repository: Dependencies.ProfileManager.mainProfileRepository,
|
||||
backupRepository: Dependencies.ProfileManager.backupProfileRepository,
|
||||
remoteRepositoryBlock: remoteRepositoryBlock,
|
||||
mirrorsRemoteRepository: Dependencies.ProfileManager.mirrorsRemoteRepository,
|
||||
processor: processor
|
||||
)
|
||||
|
||||
// MARK: ExtendedTunnel
|
||||
|
||||
let tunnel = ExtendedTunnel(
|
||||
tunnel: Tunnel(strategy: Dependencies.ExtendedTunnel.strategy),
|
||||
environment: .shared,
|
||||
processor: processor,
|
||||
interval: Constants.shared.tunnel.refreshInterval
|
||||
)
|
||||
|
||||
// MARK: ProviderManager
|
||||
|
||||
let providerManager: ProviderManager = {
|
||||
let store = CoreDataPersistentStore(
|
||||
logger: .default,
|
||||
containerName: Constants.shared.containers.providers,
|
||||
model: AppData.cdProvidersModel,
|
||||
cloudKitIdentifier: nil,
|
||||
author: nil
|
||||
)
|
||||
let repository = AppData.cdProviderRepositoryV3(context: store.backgroundContext)
|
||||
return ProviderManager(repository: repository)
|
||||
}()
|
||||
|
||||
// MARK: MigrationManager
|
||||
|
||||
let profileStrategy = ProfileV2MigrationStrategy(
|
||||
coreDataLogger: .default,
|
||||
profilesContainer: .init(
|
||||
Constants.shared.containers.legacyV2,
|
||||
BundleConfiguration.mainString(for: .legacyV2CloudKitId)
|
||||
),
|
||||
tvProfilesContainer: .init(
|
||||
Constants.shared.containers.legacyV2TV,
|
||||
BundleConfiguration.mainString(for: .legacyV2TVCloudKitId)
|
||||
)
|
||||
)
|
||||
let migrationSimulation: MigrationManager.Simulation?
|
||||
if AppCommandLine.contains(.fakeMigration) {
|
||||
migrationSimulation = MigrationManager.Simulation(
|
||||
fakeProfiles: true,
|
||||
maxMigrationTime: 3.0,
|
||||
randomFailures: true
|
||||
)
|
||||
} else {
|
||||
migrationSimulation = nil
|
||||
}
|
||||
let migrationManager = MigrationManager(profileStrategy: profileStrategy, simulation: migrationSimulation)
|
||||
|
||||
return AppContext(
|
||||
iapManager: iapManager,
|
||||
migrationManager: migrationManager,
|
||||
profileManager: profileManager,
|
||||
providerManager: providerManager,
|
||||
preferencesManager: .sharedForApp,
|
||||
registry: .shared,
|
||||
tunnel: tunnel,
|
||||
tunnelReceiptURL: BundleConfiguration.urlForBetaReceipt
|
||||
)
|
||||
}()
|
||||
}
|
||||
|
||||
// MARK: - Dependencies
|
||||
|
||||
// MARK: Simulator
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
|
||||
private extension Dependencies.ExtendedTunnel {
|
||||
static var strategy: TunnelObservableStrategy {
|
||||
FakeTunnelStrategy(environment: .shared, dataCountInterval: 1000)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private extension Dependencies.ProfileManager {
|
||||
static var mainProfileRepository: ProfileRepository {
|
||||
coreDataProfileRepository(observingResults: true)
|
||||
}
|
||||
|
||||
static var backupProfileRepository: ProfileRepository? {
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// MARK: Device
|
||||
|
||||
@MainActor
|
||||
private extension Dependencies.ExtendedTunnel {
|
||||
static var strategy: TunnelObservableStrategy {
|
||||
Dependencies.ProfileManager.neStrategy
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private extension Dependencies.ProfileManager {
|
||||
static var mainProfileRepository: ProfileRepository {
|
||||
neProfileRepository
|
||||
}
|
||||
|
||||
static var backupProfileRepository: ProfileRepository? {
|
||||
coreDataProfileRepository(observingResults: false)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// MARK: Common
|
||||
|
||||
@MainActor
|
||||
private extension Dependencies.ProfileManager {
|
||||
static let neProfileRepository: ProfileRepository = {
|
||||
NEProfileRepository(repository: neStrategy) {
|
||||
sharedTitle($0)
|
||||
}
|
||||
}()
|
||||
|
||||
static let neStrategy: NETunnelStrategy = {
|
||||
NETunnelStrategy(
|
||||
bundleIdentifier: BundleConfiguration.mainString(for: .tunnelId),
|
||||
coder: Registry.sharedProtocolCoder,
|
||||
environment: .shared
|
||||
)
|
||||
}()
|
||||
|
||||
static func coreDataProfileRepository(observingResults: Bool) -> ProfileRepository {
|
||||
let store = CoreDataPersistentStore(
|
||||
logger: .default,
|
||||
containerName: Constants.shared.containers.localProfiles,
|
||||
model: AppData.cdProfilesModel,
|
||||
cloudKitIdentifier: nil,
|
||||
author: nil
|
||||
)
|
||||
return AppData.cdProfileRepositoryV3(
|
||||
registry: .shared,
|
||||
coder: CodableProfileCoder(),
|
||||
context: store.context,
|
||||
observingResults: observingResults
|
||||
) { error in
|
||||
pp_log(.app, .error, "Unable to decode local result: \(error)")
|
||||
return .ignore
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// Dependencies+CoreData.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/2/24.
|
||||
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import CommonUtils
|
||||
import Foundation
|
||||
import PassepartoutKit
|
||||
|
||||
extension Dependencies {
|
||||
func coreDataLogger() -> CoreDataPersistentStoreLogger {
|
||||
DefaultCoreDataPersistentStoreLogger()
|
||||
}
|
||||
}
|
||||
|
||||
private struct DefaultCoreDataPersistentStoreLogger: CoreDataPersistentStoreLogger {
|
||||
func debug(_ msg: String) {
|
||||
pp_log(.app, .info, msg)
|
||||
}
|
||||
|
||||
func warning(_ msg: String) {
|
||||
pp_log(.app, .error, msg)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// Dependencies+IAPManager.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/2/24.
|
||||
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import CommonLibrary
|
||||
import CommonUtils
|
||||
import Foundation
|
||||
import PassepartoutKit
|
||||
|
||||
extension Dependencies {
|
||||
func appProductHelper() -> any AppProductHelper {
|
||||
StoreKitHelper(
|
||||
products: AppProduct.all,
|
||||
inAppIdentifier: {
|
||||
let prefix = BundleConfiguration.mainString(for: .iapBundlePrefix)
|
||||
return "\(prefix).\($0.rawValue)"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func betaChecker() -> BetaChecker {
|
||||
TestFlightChecker()
|
||||
}
|
||||
|
||||
func productsAtBuild() -> BuildProducts<AppProduct> {
|
||||
{
|
||||
#if os(iOS)
|
||||
if $0 <= 2016 {
|
||||
return [.Full.iOS]
|
||||
} else if $0 <= 3000 {
|
||||
return [.Features.networkSettings]
|
||||
}
|
||||
return []
|
||||
#elseif os(macOS)
|
||||
if $0 <= 3000 {
|
||||
return [.Features.networkSettings]
|
||||
}
|
||||
return []
|
||||
#else
|
||||
return []
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
//
|
||||
// Shared.swift
|
||||
// Dependencies+PassepartoutKit.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 2/25/24.
|
||||
// Created by Davide De Rosa on 12/2/24.
|
||||
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
|
@ -23,17 +23,35 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import AppData
|
||||
import AppDataPreferences
|
||||
import CommonLibrary
|
||||
import CommonUtils
|
||||
import CPassepartoutOpenVPNOpenSSL
|
||||
import Foundation
|
||||
import PassepartoutKit
|
||||
import PassepartoutWireGuardGo
|
||||
|
||||
extension Registry {
|
||||
static let shared = Registry(
|
||||
extension Dependencies {
|
||||
var registry: Registry {
|
||||
Self.sharedRegistry
|
||||
}
|
||||
|
||||
func neProtocolCoder() -> NEProtocolCoder {
|
||||
KeychainNEProtocolCoder(
|
||||
tunnelBundleIdentifier: BundleConfiguration.mainString(for: .tunnelId),
|
||||
registry: registry,
|
||||
coder: CodableProfileCoder(),
|
||||
keychain: AppleKeychain(group: BundleConfiguration.mainString(for: .keychainGroupId))
|
||||
)
|
||||
}
|
||||
|
||||
func tunnelEnvironment() -> TunnelEnvironment {
|
||||
AppGroupEnvironment(
|
||||
appGroup: BundleConfiguration.mainString(for: .groupId),
|
||||
prefix: "PassepartoutKit."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private extension Dependencies {
|
||||
static let sharedRegistry = Registry(
|
||||
withKnownHandlers: true,
|
||||
allImplementations: [
|
||||
OpenVPNModule.Implementation(
|
||||
|
@ -69,59 +87,4 @@ extension Registry {
|
|||
)
|
||||
]
|
||||
)
|
||||
|
||||
static var sharedProtocolCoder: KeychainNEProtocolCoder {
|
||||
KeychainNEProtocolCoder(
|
||||
tunnelBundleIdentifier: BundleConfiguration.mainString(for: .tunnelId),
|
||||
registry: .shared,
|
||||
coder: CodableProfileCoder(),
|
||||
keychain: AppleKeychain(group: BundleConfiguration.mainString(for: .keychainGroupId))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension TunnelEnvironment where Self == AppGroupEnvironment {
|
||||
static var shared: Self {
|
||||
AppGroupEnvironment(
|
||||
appGroup: BundleConfiguration.mainString(for: .groupId),
|
||||
prefix: "PassepartoutKit."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension PreferencesManager {
|
||||
static func sharedImplementation(withCloudKit: Bool) -> PreferencesManager {
|
||||
let preferencesStore = CoreDataPersistentStore(
|
||||
logger: .default,
|
||||
containerName: Constants.shared.containers.preferences,
|
||||
baseURL: BundleConfiguration.urlForGroupDocuments,
|
||||
model: AppData.cdPreferencesModel,
|
||||
cloudKitIdentifier: withCloudKit ? BundleConfiguration.mainString(for: .cloudKitPreferencesId) : nil,
|
||||
author: nil
|
||||
)
|
||||
return PreferencesManager(
|
||||
modulesRepository: AppData.cdModulePreferencesRepositoryV3(context: preferencesStore.context),
|
||||
providersFactory: {
|
||||
try AppData.cdProviderPreferencesRepositoryV3(context: preferencesStore.context, providerId: $0)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Logging
|
||||
|
||||
extension CoreDataPersistentStoreLogger where Self == DefaultCoreDataPersistentStoreLogger {
|
||||
static var `default`: CoreDataPersistentStoreLogger {
|
||||
DefaultCoreDataPersistentStoreLogger()
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultCoreDataPersistentStoreLogger: CoreDataPersistentStoreLogger {
|
||||
func debug(_ msg: String) {
|
||||
pp_log(.app, .info, msg)
|
||||
}
|
||||
|
||||
func warning(_ msg: String) {
|
||||
pp_log(.app, .error, msg)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// Dependencies+PreferencesManager.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/2/24.
|
||||
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import AppData
|
||||
import AppDataPreferences
|
||||
import CommonLibrary
|
||||
import CommonUtils
|
||||
import Foundation
|
||||
import PassepartoutKit
|
||||
|
||||
extension Dependencies {
|
||||
func preferencesManager(withCloudKit: Bool) -> PreferencesManager {
|
||||
let preferencesStore = CoreDataPersistentStore(
|
||||
logger: coreDataLogger(),
|
||||
containerName: Constants.shared.containers.preferences,
|
||||
baseURL: BundleConfiguration.urlForGroupDocuments,
|
||||
model: AppData.cdPreferencesModel,
|
||||
cloudKitIdentifier: withCloudKit ? BundleConfiguration.mainString(for: .cloudKitPreferencesId) : nil,
|
||||
author: nil
|
||||
)
|
||||
return PreferencesManager(
|
||||
modulesRepository: AppData.cdModulePreferencesRepositoryV3(context: preferencesStore.context),
|
||||
providersFactory: {
|
||||
try AppData.cdProviderPreferencesRepositoryV3(context: preferencesStore.context, providerId: $0)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -23,72 +23,9 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import CommonLibrary
|
||||
import CommonUtils
|
||||
import Foundation
|
||||
import PassepartoutKit
|
||||
|
||||
enum Dependencies {
|
||||
enum ExtendedTunnel {
|
||||
}
|
||||
|
||||
enum IAPManager {
|
||||
}
|
||||
|
||||
enum ProfileManager {
|
||||
}
|
||||
}
|
||||
|
||||
extension Dependencies.IAPManager {
|
||||
|
||||
@MainActor
|
||||
static let inAppHelper = StoreKitHelper(
|
||||
products: AppProduct.all,
|
||||
inAppIdentifier: {
|
||||
let prefix = BundleConfiguration.mainString(for: .iapBundlePrefix)
|
||||
return "\(prefix).\($0.rawValue)"
|
||||
}
|
||||
)
|
||||
|
||||
static var betaChecker: BetaChecker {
|
||||
TestFlightChecker()
|
||||
}
|
||||
|
||||
static let productsAtBuild: BuildProducts<AppProduct> = {
|
||||
#if os(iOS)
|
||||
if $0 <= 2016 {
|
||||
return [.Full.iOS]
|
||||
} else if $0 <= 3000 {
|
||||
return [.Features.networkSettings]
|
||||
}
|
||||
return []
|
||||
#elseif os(macOS)
|
||||
if $0 <= 3000 {
|
||||
return [.Features.networkSettings]
|
||||
}
|
||||
return []
|
||||
#else
|
||||
return []
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
extension Dependencies.ProfileManager {
|
||||
static let sharedTitle: @Sendable (Profile) -> String = {
|
||||
String(format: Constants.shared.tunnel.profileTitleFormat, $0.name)
|
||||
}
|
||||
|
||||
#if os(tvOS)
|
||||
static let mirrorsRemoteRepository = true
|
||||
|
||||
static let isIncluded: @MainActor @Sendable (CommonLibrary.IAPManager, Profile) -> Bool = {
|
||||
$1.attributes.isAvailableForTV == true
|
||||
}
|
||||
#else
|
||||
static let mirrorsRemoteRepository = false
|
||||
|
||||
static let isIncluded: @MainActor @Sendable (CommonLibrary.IAPManager, Profile) -> Bool = { _, _ in
|
||||
true
|
||||
}
|
||||
#endif
|
||||
@MainActor
|
||||
struct Dependencies {
|
||||
static let shared = Dependencies()
|
||||
}
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
//
|
||||
// Shared+App.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 11/14/24.
|
||||
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import CommonLibrary
|
||||
import CommonUtils
|
||||
import Foundation
|
||||
import PassepartoutKit
|
||||
import UITesting
|
||||
|
||||
extension IAPManager {
|
||||
static let sharedForApp = IAPManager(
|
||||
customUserLevel: Dependencies.IAPManager.customUserLevel,
|
||||
inAppHelper: Dependencies.IAPManager.simulatedInAppHelper,
|
||||
receiptReader: Dependencies.IAPManager.simulatedAppReceiptReader,
|
||||
betaChecker: Dependencies.IAPManager.betaChecker,
|
||||
productsAtBuild: Dependencies.IAPManager.productsAtBuild
|
||||
)
|
||||
}
|
||||
|
||||
extension PreferencesManager {
|
||||
static let sharedForApp = PreferencesManager.sharedImplementation(withCloudKit: true)
|
||||
}
|
||||
|
||||
// MARK: - Dependencies
|
||||
|
||||
private extension Dependencies.IAPManager {
|
||||
static var customUserLevel: AppUserLevel? {
|
||||
guard let userLevelString = BundleConfiguration.mainIntegerIfPresent(for: .userLevel),
|
||||
let userLevel = AppUserLevel(rawValue: userLevelString) else {
|
||||
return nil
|
||||
}
|
||||
return userLevel
|
||||
}
|
||||
|
||||
@MainActor
|
||||
static let simulatedInAppHelper: any AppProductHelper = {
|
||||
if AppCommandLine.contains(.fakeIAP) {
|
||||
return FakeAppProductHelper()
|
||||
}
|
||||
return inAppHelper
|
||||
}()
|
||||
|
||||
@MainActor
|
||||
static var simulatedAppReceiptReader: AppReceiptReader {
|
||||
if AppCommandLine.contains(.fakeIAP) {
|
||||
guard let mockHelper = simulatedInAppHelper as? FakeAppProductHelper else {
|
||||
fatalError("When .isFakeIAP, simulatedInAppHelper is expected to be MockAppProductHelper")
|
||||
}
|
||||
return mockHelper.receiptReader
|
||||
}
|
||||
return FallbackReceiptReader(
|
||||
main: StoreKitReceiptReader(),
|
||||
beta: betaReceiptURL.map {
|
||||
KvittoReceiptReader(url: $0)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
static var betaReceiptURL: URL? {
|
||||
Bundle.main.appStoreProductionReceiptURL
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Shared+Tunnel.swift
|
||||
// TunnelContext+Shared.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 11/14/24.
|
||||
|
@ -28,32 +28,37 @@ import CommonUtils
|
|||
import Foundation
|
||||
import PassepartoutKit
|
||||
|
||||
extension IAPManager {
|
||||
static let sharedForTunnel = IAPManager(
|
||||
inAppHelper: Dependencies.IAPManager.inAppHelper,
|
||||
receiptReader: Dependencies.IAPManager.tunnelReceiptReader,
|
||||
betaChecker: Dependencies.IAPManager.betaChecker,
|
||||
productsAtBuild: Dependencies.IAPManager.productsAtBuild
|
||||
extension TunnelContext {
|
||||
static let shared: TunnelContext = {
|
||||
let dependencies: Dependencies = .shared
|
||||
let iapManager = IAPManager(
|
||||
inAppHelper: dependencies.appProductHelper(),
|
||||
receiptReader: dependencies.tunnelReceiptReader(),
|
||||
betaChecker: dependencies.betaChecker(),
|
||||
productsAtBuild: dependencies.productsAtBuild()
|
||||
)
|
||||
}
|
||||
|
||||
extension PreferencesManager {
|
||||
static let sharedForTunnel = PreferencesManager.sharedImplementation(withCloudKit: false)
|
||||
let processor: PacketTunnelProcessor = {
|
||||
let preferencesManager = dependencies.preferencesManager(withCloudKit: false)
|
||||
return DefaultTunnelProcessor(preferencesManager: preferencesManager)
|
||||
}()
|
||||
return TunnelContext(
|
||||
iapManager: iapManager,
|
||||
processor: processor
|
||||
)
|
||||
}()
|
||||
}
|
||||
|
||||
// MARK: - Dependencies
|
||||
|
||||
private extension Dependencies.IAPManager {
|
||||
|
||||
@MainActor
|
||||
static var tunnelReceiptReader: AppReceiptReader {
|
||||
private extension Dependencies {
|
||||
func tunnelReceiptReader() -> AppReceiptReader {
|
||||
FallbackReceiptReader(
|
||||
main: StoreKitReceiptReader(),
|
||||
beta: KvittoReceiptReader(url: betaReceiptURL)
|
||||
)
|
||||
}
|
||||
|
||||
static var betaReceiptURL: URL {
|
||||
var betaReceiptURL: URL {
|
||||
BundleConfiguration.urlForBetaReceipt // copied by AppContext.onLaunch
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// TunnelContext.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/8/24.
|
||||
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import CommonLibrary
|
||||
import Foundation
|
||||
|
||||
@MainActor
|
||||
struct TunnelContext {
|
||||
let iapManager: IAPManager
|
||||
|
||||
let processor: PacketTunnelProcessor
|
||||
}
|
|
@ -28,6 +28,13 @@ import CommonLibrary
|
|||
import PassepartoutKit
|
||||
|
||||
final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
||||
|
||||
@MainActor
|
||||
private let context: TunnelContext = .shared
|
||||
|
||||
@MainActor
|
||||
private let dependencies: Dependencies = .shared
|
||||
|
||||
private var fwd: NEPTPForwarder?
|
||||
|
||||
override func startTunnel(options: [String: NSObject]? = nil) async throws {
|
||||
|
@ -36,19 +43,18 @@ final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
|||
parameters: Constants.shared.log,
|
||||
logsPrivateData: UserDefaults.appGroup.bool(forKey: AppPreference.logsPrivateData.key)
|
||||
)
|
||||
let processor = DefaultTunnelProcessor(preferencesManager: .sharedForTunnel)
|
||||
do {
|
||||
fwd = try await NEPTPForwarder(
|
||||
provider: self,
|
||||
decoder: Registry.sharedProtocolCoder,
|
||||
registry: .shared,
|
||||
environment: .shared,
|
||||
profileBlock: processor.willStart
|
||||
decoder: dependencies.neProtocolCoder(),
|
||||
registry: dependencies.registry,
|
||||
environment: dependencies.tunnelEnvironment(),
|
||||
profileBlock: context.processor.willStart
|
||||
)
|
||||
guard let fwd else {
|
||||
fatalError("NEPTPForwarder nil without throwing error?")
|
||||
}
|
||||
try await checkEligibility(of: fwd.profile, environment: .shared)
|
||||
try await checkEligibility(of: fwd.profile, environment: dependencies.tunnelEnvironment())
|
||||
try await fwd.startTunnel(options: options)
|
||||
} catch {
|
||||
pp_log(.app, .fault, "Unable to start tunnel: \(error)")
|
||||
|
@ -85,14 +91,10 @@ final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
|||
|
||||
@MainActor
|
||||
private extension PacketTunnelProvider {
|
||||
var iapManager: IAPManager {
|
||||
.sharedForTunnel
|
||||
}
|
||||
|
||||
func checkEligibility(of profile: Profile, environment: TunnelEnvironment) async throws {
|
||||
await iapManager.reloadReceipt()
|
||||
await context.iapManager.reloadReceipt()
|
||||
do {
|
||||
try iapManager.verify(profile)
|
||||
try context.iapManager.verify(profile)
|
||||
} catch {
|
||||
let error = PassepartoutError(.App.ineligibleProfile)
|
||||
environment.setEnvironmentValue(error.code, forKey: TunnelEnvironmentKeys.lastErrorCode)
|
||||
|
|
Loading…
Reference in New Issue