Refactor and test ProductManager (#437)
Carefully drop the StoreKit and Kvitto dependencies for ProductManager to be testable. Rebuild test target completely to start writing meaningful tests in general.
This commit is contained in:
parent
7c42263fb1
commit
a0da930d98
|
@ -35,4 +35,5 @@ jobs:
|
||||||
go-version: "^1.17"
|
go-version: "^1.17"
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
bundle exec fastlane --env beta,ios scan
|
cd PassepartoutLibrary
|
||||||
|
swift test
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
0E021D9C284E68580077EF5D /* CoreContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E021D9A284E68580077EF5D /* CoreContext.swift */; };
|
0E021D9C284E68580077EF5D /* CoreContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E021D9A284E68580077EF5D /* CoreContext.swift */; };
|
||||||
0E021D9D284E68580077EF5D /* AppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E021D9B284E68580077EF5D /* AppContext.swift */; };
|
0E021D9D284E68580077EF5D /* AppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E021D9B284E68580077EF5D /* AppContext.swift */; };
|
||||||
0E0392772818732D00827C10 /* BuildProducts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0392762818732D00827C10 /* BuildProducts.swift */; };
|
|
||||||
0E039279281890B100827C10 /* AddHostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E039278281890B100827C10 /* AddHostView.swift */; };
|
0E039279281890B100827C10 /* AddHostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E039278281890B100827C10 /* AddHostView.swift */; };
|
||||||
0E04F0062883462E00BFCE1C /* LightUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E04F0052883462E00BFCE1C /* LightUtils.swift */; };
|
0E04F0062883462E00BFCE1C /* LightUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E04F0052883462E00BFCE1C /* LightUtils.swift */; };
|
||||||
0E04F0072883462E00BFCE1C /* LightUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E04F0052883462E00BFCE1C /* LightUtils.swift */; };
|
0E04F0072883462E00BFCE1C /* LightUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E04F0052883462E00BFCE1C /* LightUtils.swift */; };
|
||||||
|
@ -29,12 +28,10 @@
|
||||||
0E0F4C5C29C76B790022E884 /* SceneDelegate+Shortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F4C5B29C76B790022E884 /* SceneDelegate+Shortcuts.swift */; };
|
0E0F4C5C29C76B790022E884 /* SceneDelegate+Shortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F4C5B29C76B790022E884 /* SceneDelegate+Shortcuts.swift */; };
|
||||||
0E0F4C6429C84B5A0022E884 /* LockableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F4C6329C84B5A0022E884 /* LockableView.swift */; };
|
0E0F4C6429C84B5A0022E884 /* LockableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F4C6329C84B5A0022E884 /* LockableView.swift */; };
|
||||||
0E0F4C6629C84CF60022E884 /* LogoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F4C6529C84CF60022E884 /* LogoView.swift */; };
|
0E0F4C6629C84CF60022E884 /* LogoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F4C6529C84CF60022E884 /* LogoView.swift */; };
|
||||||
0E1AD5CC2A2682DA002AE6E6 /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1AD5CB2A2682DA002AE6E6 /* AppError.swift */; };
|
|
||||||
0E1AD5CE2A268645002AE6E6 /* Errors+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1AD5CD2A268645002AE6E6 /* Errors+L10n.swift */; };
|
0E1AD5CE2A268645002AE6E6 /* Errors+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1AD5CD2A268645002AE6E6 /* Errors+L10n.swift */; };
|
||||||
0E1B5F5C29C506AD00FE7D18 /* DiagnosticsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1B5F5B29C506AC00FE7D18 /* DiagnosticsSection.swift */; };
|
0E1B5F5C29C506AD00FE7D18 /* DiagnosticsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1B5F5B29C506AC00FE7D18 /* DiagnosticsSection.swift */; };
|
||||||
0E1F5628287F0ECB00F8ADD7 /* ProviderProfileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1F5627287F0ECB00F8ADD7 /* ProviderProfileItem.swift */; };
|
0E1F5628287F0ECB00F8ADD7 /* ProviderProfileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1F5627287F0ECB00F8ADD7 /* ProviderProfileItem.swift */; };
|
||||||
0E1F562B287F0EF100F8ADD7 /* ProviderProfileItem+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1F5629287F0EEE00F8ADD7 /* ProviderProfileItem+ViewModel.swift */; };
|
0E1F562B287F0EF100F8ADD7 /* ProviderProfileItem+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1F5629287F0EEE00F8ADD7 /* ProviderProfileItem+ViewModel.swift */; };
|
||||||
0E293851285A70AC002A6E0E /* AppPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E293850285A70AC002A6E0E /* AppPreference.swift */; };
|
|
||||||
0E293857285A73BC002A6E0E /* AppContext+Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E293856285A73BC002A6E0E /* AppContext+Shared.swift */; };
|
0E293857285A73BC002A6E0E /* AppContext+Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E293856285A73BC002A6E0E /* AppContext+Shared.swift */; };
|
||||||
0E2A8D4927ADF87F00207D04 /* PassepartoutApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2A8D4727ADF87F00207D04 /* PassepartoutApp.swift */; };
|
0E2A8D4927ADF87F00207D04 /* PassepartoutApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2A8D4727ADF87F00207D04 /* PassepartoutApp.swift */; };
|
||||||
0E2A8D4F27B04BBA00207D04 /* OrganizerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2A8D4E27B04BB900207D04 /* OrganizerView.swift */; };
|
0E2A8D4F27B04BBA00207D04 /* OrganizerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2A8D4E27B04BB900207D04 /* OrganizerView.swift */; };
|
||||||
|
@ -44,15 +41,18 @@
|
||||||
0E2DE71C27DCCFE80067B9E1 /* TunnelKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2DE71B27DCCFE80067B9E1 /* TunnelKit+Extensions.swift */; };
|
0E2DE71C27DCCFE80067B9E1 /* TunnelKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2DE71B27DCCFE80067B9E1 /* TunnelKit+Extensions.swift */; };
|
||||||
0E2DE71F27DCD0290067B9E1 /* TunnelKit+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2DE71E27DCD0290067B9E1 /* TunnelKit+L10n.swift */; };
|
0E2DE71F27DCD0290067B9E1 /* TunnelKit+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2DE71E27DCD0290067B9E1 /* TunnelKit+L10n.swift */; };
|
||||||
0E2DE72527DCDF550067B9E1 /* WireGuard+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2DE72427DCDF550067B9E1 /* WireGuard+L10n.swift */; };
|
0E2DE72527DCDF550067B9E1 /* WireGuard+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2DE72427DCDF550067B9E1 /* WireGuard+L10n.swift */; };
|
||||||
|
0E2E0B6F2B335A8E00E3204A /* AppPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E0B6D2B335A8E00E3204A /* AppPreference.swift */; };
|
||||||
|
0E2E0B702B335A8E00E3204A /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E0B6E2B335A8E00E3204A /* AppError.swift */; };
|
||||||
|
0E2E0B752B335AAB00E3204A /* IntentsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E0B712B335AAB00E3204A /* IntentsManager.swift */; };
|
||||||
|
0E2E0B762B335AAB00E3204A /* UpgradeManagerStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E0B722B335AAB00E3204A /* UpgradeManagerStrategy.swift */; };
|
||||||
|
0E2E0B772B335AAB00E3204A /* UpgradeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E0B732B335AAB00E3204A /* UpgradeManager.swift */; };
|
||||||
|
0E2E0B782B335AAB00E3204A /* PersistenceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E0B742B335AAB00E3204A /* PersistenceManager.swift */; };
|
||||||
0E34A2B627CAA8CC00C73B67 /* Core+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34A2B527CAA8CC00C73B67 /* Core+L10n.swift */; };
|
0E34A2B627CAA8CC00C73B67 /* Core+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34A2B527CAA8CC00C73B67 /* Core+L10n.swift */; };
|
||||||
0E34A2B927CAA96A00C73B67 /* OpenVPN+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34A2AF27CAA84500C73B67 /* OpenVPN+L10n.swift */; };
|
0E34A2B927CAA96A00C73B67 /* OpenVPN+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34A2AF27CAA84500C73B67 /* OpenVPN+L10n.swift */; };
|
||||||
0E34A2CF27CADA6300C73B67 /* GenericVersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34A2CE27CADA6300C73B67 /* GenericVersionView.swift */; };
|
0E34A2CF27CADA6300C73B67 /* GenericVersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34A2CE27CADA6300C73B67 /* GenericVersionView.swift */; };
|
||||||
0E34AC7827F840890042F2AB /* OrganizerView+Scene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34AC7727F840890042F2AB /* OrganizerView+Scene.swift */; };
|
0E34AC7827F840890042F2AB /* OrganizerView+Scene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34AC7727F840890042F2AB /* OrganizerView+Scene.swift */; };
|
||||||
0E34AC8227F892C40042F2AB /* OnDemandView+SSID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34AC8127F892C40042F2AB /* OnDemandView+SSID.swift */; };
|
0E34AC8227F892C40042F2AB /* OnDemandView+SSID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34AC8127F892C40042F2AB /* OnDemandView+SSID.swift */; };
|
||||||
0E35C09A280E95BB0071FA35 /* ProviderProfileAvailability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E35C099280E95BB0071FA35 /* ProviderProfileAvailability.swift */; };
|
0E35C09A280E95BB0071FA35 /* ProviderProfileAvailability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E35C099280E95BB0071FA35 /* ProviderProfileAvailability.swift */; };
|
||||||
0E3A3C132AAB7C480003A5F6 /* UpgradeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3A3C112AAB7C470003A5F6 /* UpgradeManager.swift */; };
|
|
||||||
0E3A3C142AAB7C480003A5F6 /* DefaultUpgradeManagerStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3A3C122AAB7C480003A5F6 /* DefaultUpgradeManagerStrategy.swift */; };
|
|
||||||
0E3A3C162AAB8AB80003A5F6 /* UpgradeManagerStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3A3C152AAB8AB80003A5F6 /* UpgradeManagerStrategy.swift */; };
|
|
||||||
0E3A593C2A50975700B3FE40 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3A593B2A50975700B3FE40 /* ErrorHandler.swift */; };
|
0E3A593C2A50975700B3FE40 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3A593B2A50975700B3FE40 /* ErrorHandler.swift */; };
|
||||||
0E3B7FCD27E47B3700C66F13 /* AddHostView+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FCC27E47B3700C66F13 /* AddHostView+Name.swift */; };
|
0E3B7FCD27E47B3700C66F13 /* AddHostView+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FCC27E47B3700C66F13 /* AddHostView+Name.swift */; };
|
||||||
0E3B7FD627E5173A00C66F13 /* ProfileView+VPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */; };
|
0E3B7FD627E5173A00C66F13 /* ProfileView+VPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */; };
|
||||||
|
@ -68,7 +68,6 @@
|
||||||
0E49F6BB27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49F6BA27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift */; };
|
0E49F6BB27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49F6BA27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift */; };
|
||||||
0E49F6BD27D7639000385834 /* EndpointAdvancedView+WireGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49F6BC27D7639000385834 /* EndpointAdvancedView+WireGuard.swift */; };
|
0E49F6BD27D7639000385834 /* EndpointAdvancedView+WireGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49F6BC27D7639000385834 /* EndpointAdvancedView+WireGuard.swift */; };
|
||||||
0E49F6BF27D764AF00385834 /* EndpointAdvancedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49F6BE27D764AF00385834 /* EndpointAdvancedView.swift */; };
|
0E49F6BF27D764AF00385834 /* EndpointAdvancedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49F6BE27D764AF00385834 /* EndpointAdvancedView.swift */; };
|
||||||
0E53249927D26B51002565C3 /* ProductManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E53249627D26B51002565C3 /* ProductManager.swift */; };
|
|
||||||
0E53249D27D28FC7002565C3 /* Kvitto in Frameworks */ = {isa = PBXBuildFile; productRef = 0E53249C27D28FC7002565C3 /* Kvitto */; };
|
0E53249D27D28FC7002565C3 /* Kvitto in Frameworks */ = {isa = PBXBuildFile; productRef = 0E53249C27D28FC7002565C3 /* Kvitto */; };
|
||||||
0E5324A927D2AC55002565C3 /* LongContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5324A827D2AC55002565C3 /* LongContentView.swift */; };
|
0E5324A927D2AC55002565C3 /* LongContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5324A827D2AC55002565C3 /* LongContentView.swift */; };
|
||||||
0E5349BE27C16A4500C71BB3 /* StyledPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5349BD27C16A4500C71BB3 /* StyledPicker.swift */; };
|
0E5349BE27C16A4500C71BB3 /* StyledPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5349BD27C16A4500C71BB3 /* StyledPicker.swift */; };
|
||||||
|
@ -106,9 +105,7 @@
|
||||||
0E71ACFB27C12E5300F85C4B /* VersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACFA27C12E5300F85C4B /* VersionView.swift */; };
|
0E71ACFB27C12E5300F85C4B /* VersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACFA27C12E5300F85C4B /* VersionView.swift */; };
|
||||||
0E71ACFD27C1321A00F85C4B /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACFC27C1321A00F85C4B /* ActivityView.swift */; };
|
0E71ACFD27C1321A00F85C4B /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACFC27C1321A00F85C4B /* ActivityView.swift */; };
|
||||||
0E7577DF2817E22C00081CBE /* VPNToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7577DE2817E22C00081CBE /* VPNToggle.swift */; };
|
0E7577DF2817E22C00081CBE /* VPNToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7577DE2817E22C00081CBE /* VPNToggle.swift */; };
|
||||||
0E7A8C0A2A1D410500780F4B /* PersistenceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7A8C092A1D410400780F4B /* PersistenceManager.swift */; };
|
|
||||||
0E7A8C0C2A1D4A6100780F4B /* PassepartoutLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = 0E7A8C0B2A1D4A6100780F4B /* PassepartoutLibrary */; };
|
0E7A8C0C2A1D4A6100780F4B /* PassepartoutLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = 0E7A8C0B2A1D4A6100780F4B /* PassepartoutLibrary */; };
|
||||||
0E7A8C0E2A1D4A6E00780F4B /* PassepartoutLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = 0E7A8C0D2A1D4A6E00780F4B /* PassepartoutLibrary */; };
|
|
||||||
0E7A8C0F2A1D54DE00780F4B /* Picker+OpenVPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7A8C072A1D40BA00780F4B /* Picker+OpenVPN.swift */; };
|
0E7A8C0F2A1D54DE00780F4B /* Picker+OpenVPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7A8C072A1D40BA00780F4B /* Picker+OpenVPN.swift */; };
|
||||||
0E7A8C102A1D54DE00780F4B /* Picker+Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7A8C082A1D40BA00780F4B /* Picker+Network.swift */; };
|
0E7A8C102A1D54DE00780F4B /* Picker+Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7A8C082A1D40BA00780F4B /* Picker+Network.swift */; };
|
||||||
0E90DFE627BACC1500EF5078 /* AddHostViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E90DFE527BACC1500EF5078 /* AddHostViewModel.swift */; };
|
0E90DFE627BACC1500EF5078 /* AddHostViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E90DFE527BACC1500EF5078 /* AddHostViewModel.swift */; };
|
||||||
|
@ -138,7 +135,6 @@
|
||||||
0E96D31128721855005EFBCF /* Constants+Mac.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D31028721855005EFBCF /* Constants+Mac.swift */; };
|
0E96D31128721855005EFBCF /* Constants+Mac.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D31028721855005EFBCF /* Constants+Mac.swift */; };
|
||||||
0E96D31428721FC3005EFBCF /* ObservableProcessTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D31328721FC3005EFBCF /* ObservableProcessTransformer.swift */; };
|
0E96D31428721FC3005EFBCF /* ObservableProcessTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D31328721FC3005EFBCF /* ObservableProcessTransformer.swift */; };
|
||||||
0E9AA978259F756A003FAFF1 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9AA977259F756A003FAFF1 /* PacketTunnelProvider.swift */; };
|
0E9AA978259F756A003FAFF1 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9AA977259F756A003FAFF1 /* PacketTunnelProvider.swift */; };
|
||||||
0E9C233027F47032007D5FC7 /* IntentsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9C232F27F47032007D5FC7 /* IntentsManager.swift */; };
|
|
||||||
0E9C233327F47E95007D5FC7 /* IntentDispatcher+Activities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9C233227F47E95007D5FC7 /* IntentDispatcher+Activities.swift */; };
|
0E9C233327F47E95007D5FC7 /* IntentDispatcher+Activities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9C233227F47E95007D5FC7 /* IntentDispatcher+Activities.swift */; };
|
||||||
0E9C3B6F27FC573E00D0F02E /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E9C3B6E27FC573E00D0F02E /* CloudKit.framework */; };
|
0E9C3B6F27FC573E00D0F02E /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E9C3B6E27FC573E00D0F02E /* CloudKit.framework */; };
|
||||||
0E9E5AEF27B44CF1008C95DA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E9E5AE227B44CF1008C95DA /* Localizable.strings */; };
|
0E9E5AEF27B44CF1008C95DA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E9E5AE227B44CF1008C95DA /* Localizable.strings */; };
|
||||||
|
@ -146,11 +142,9 @@
|
||||||
0EA1D84728805EAE00F3CA48 /* Flags.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0EA1D84628805EAE00F3CA48 /* Flags.xcassets */; };
|
0EA1D84728805EAE00F3CA48 /* Flags.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0EA1D84628805EAE00F3CA48 /* Flags.xcassets */; };
|
||||||
0EA591162733DDDA0096F796 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0EA591142733DDDA0096F796 /* Intents.intentdefinition */; };
|
0EA591162733DDDA0096F796 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0EA591142733DDDA0096F796 /* Intents.intentdefinition */; };
|
||||||
0EA9030B287045F70087BC73 /* SystemMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA9030A287045F70087BC73 /* SystemMenu.swift */; };
|
0EA9030B287045F70087BC73 /* SystemMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA9030A287045F70087BC73 /* SystemMenu.swift */; };
|
||||||
0EB13BC0290E8C8D003CB654 /* PassepartoutTestsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB13BBF290E8C8D003CB654 /* PassepartoutTestsApp.swift */; };
|
|
||||||
0EB17EA727D226B400D473B5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EA127D2263700D473B5 /* Constants.swift */; };
|
0EB17EA727D226B400D473B5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EA127D2263700D473B5 /* Constants.swift */; };
|
||||||
0EB17EA927D226C900D473B5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EA127D2263700D473B5 /* Constants.swift */; };
|
0EB17EA927D226C900D473B5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EA127D2263700D473B5 /* Constants.swift */; };
|
||||||
0EB17EAA27D226C900D473B5 /* Constants+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EA527D2263700D473B5 /* Constants+App.swift */; };
|
0EB17EAA27D226C900D473B5 /* Constants+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EA527D2263700D473B5 /* Constants+App.swift */; };
|
||||||
0EB17EAE27D226CF00D473B5 /* LocalProduct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EA327D2263700D473B5 /* LocalProduct.swift */; };
|
|
||||||
0EB17EBA27D2560300D473B5 /* PassepartoutProviders+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EB927D2560300D473B5 /* PassepartoutProviders+Extensions.swift */; };
|
0EB17EBA27D2560300D473B5 /* PassepartoutProviders+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EB927D2560300D473B5 /* PassepartoutProviders+Extensions.swift */; };
|
||||||
0EB2B1482733FB6F007705AB /* PassepartoutOpenVPNTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0EDE8DBF20C86910004C739C /* PassepartoutOpenVPNTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
0EB2B1482733FB6F007705AB /* PassepartoutOpenVPNTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0EDE8DBF20C86910004C739C /* PassepartoutOpenVPNTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
0EB3413027C7761A00483410 /* Binding+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB3412F27C7761A00483410 /* Binding+Extensions.swift */; };
|
0EB3413027C7761A00483410 /* Binding+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB3412F27C7761A00483410 /* Binding+Extensions.swift */; };
|
||||||
|
@ -165,6 +159,8 @@
|
||||||
0EBC075D27EC529000208AD9 /* DebugLog+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBC075C27EC529000208AD9 /* DebugLog+Constants.swift */; };
|
0EBC075D27EC529000208AD9 /* DebugLog+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBC075C27EC529000208AD9 /* DebugLog+Constants.swift */; };
|
||||||
0EBC076027EC587900208AD9 /* SwiftGen+Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBC075F27EC587900208AD9 /* SwiftGen+Strings.swift */; };
|
0EBC076027EC587900208AD9 /* SwiftGen+Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBC075F27EC587900208AD9 /* SwiftGen+Strings.swift */; };
|
||||||
0EBE880F281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE880E281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift */; };
|
0EBE880F281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE880E281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift */; };
|
||||||
|
0EBF254E2B334C980045C547 /* StoreKitReceiptReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBF254D2B334C980045C547 /* StoreKitReceiptReader.swift */; };
|
||||||
|
0EBF25502B334CA00045C547 /* DefaultUpgradeManagerStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBF254F2B334CA00045C547 /* DefaultUpgradeManagerStrategy.swift */; };
|
||||||
0ECB78E9285F5DE300B0E460 /* PassepartoutMac.bundle in Embed Plugins */ = {isa = PBXBuildFile; fileRef = 0ECB78DA285F52F700B0E460 /* PassepartoutMac.bundle */; platformFilter = maccatalyst; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
0ECB78E9285F5DE300B0E460 /* PassepartoutMac.bundle in Embed Plugins */ = {isa = PBXBuildFile; fileRef = 0ECB78DA285F52F700B0E460 /* PassepartoutMac.bundle */; platformFilter = maccatalyst; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
0ECF71EE27B6A99300CDB528 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECF71ED27B6A99300CDB528 /* AccountView.swift */; };
|
0ECF71EE27B6A99300CDB528 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECF71ED27B6A99300CDB528 /* AccountView.swift */; };
|
||||||
0ED1A5FD2B2B98CC00A0EA90 /* Constants+Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED1A5FC2B2B98CC00A0EA90 /* Constants+Tunnel.swift */; };
|
0ED1A5FD2B2B98CC00A0EA90 /* Constants+Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED1A5FC2B2B98CC00A0EA90 /* Constants+Tunnel.swift */; };
|
||||||
|
@ -217,13 +213,6 @@
|
||||||
remoteGlobalIDString = 0E41BD96286711C3006346B4;
|
remoteGlobalIDString = 0E41BD96286711C3006346B4;
|
||||||
remoteInfo = PassepartoutLauncher;
|
remoteInfo = PassepartoutLauncher;
|
||||||
};
|
};
|
||||||
0EB13BBB290E82F1003CB654 /* PBXContainerItemProxy */ = {
|
|
||||||
isa = PBXContainerItemProxy;
|
|
||||||
containerPortal = 0E57F63020C83FC5008323CF /* Project object */;
|
|
||||||
proxyType = 1;
|
|
||||||
remoteGlobalIDString = 0ECF71F327B6D9CD00CDB528;
|
|
||||||
remoteInfo = WireGuardGo;
|
|
||||||
};
|
|
||||||
0EB2B1492733FB6F007705AB /* PBXContainerItemProxy */ = {
|
0EB2B1492733FB6F007705AB /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 0E57F63020C83FC5008323CF /* Project object */;
|
containerPortal = 0E57F63020C83FC5008323CF /* Project object */;
|
||||||
|
@ -301,9 +290,7 @@
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
0E021D9A284E68580077EF5D /* CoreContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreContext.swift; sourceTree = "<group>"; };
|
0E021D9A284E68580077EF5D /* CoreContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreContext.swift; sourceTree = "<group>"; };
|
||||||
0E021D9B284E68580077EF5D /* AppContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppContext.swift; sourceTree = "<group>"; };
|
0E021D9B284E68580077EF5D /* AppContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppContext.swift; sourceTree = "<group>"; };
|
||||||
0E0392762818732D00827C10 /* BuildProducts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildProducts.swift; sourceTree = "<group>"; };
|
|
||||||
0E039278281890B100827C10 /* AddHostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddHostView.swift; sourceTree = "<group>"; };
|
0E039278281890B100827C10 /* AddHostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddHostView.swift; sourceTree = "<group>"; };
|
||||||
0E0480372A1A4AE000462F2F /* Passepartout.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Passepartout.xctestplan; sourceTree = "<group>"; };
|
|
||||||
0E04F0052883462E00BFCE1C /* LightUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightUtils.swift; sourceTree = "<group>"; };
|
0E04F0052883462E00BFCE1C /* LightUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightUtils.swift; sourceTree = "<group>"; };
|
||||||
0E04F0082883466500BFCE1C /* DefaultLightUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultLightUtils.swift; sourceTree = "<group>"; };
|
0E04F0082883466500BFCE1C /* DefaultLightUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultLightUtils.swift; sourceTree = "<group>"; };
|
||||||
0E065F102813269500062CAF /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
|
0E065F102813269500062CAF /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -321,14 +308,12 @@
|
||||||
0E0F4C5B29C76B790022E884 /* SceneDelegate+Shortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SceneDelegate+Shortcuts.swift"; sourceTree = "<group>"; };
|
0E0F4C5B29C76B790022E884 /* SceneDelegate+Shortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SceneDelegate+Shortcuts.swift"; sourceTree = "<group>"; };
|
||||||
0E0F4C6329C84B5A0022E884 /* LockableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockableView.swift; sourceTree = "<group>"; };
|
0E0F4C6329C84B5A0022E884 /* LockableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockableView.swift; sourceTree = "<group>"; };
|
||||||
0E0F4C6529C84CF60022E884 /* LogoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogoView.swift; sourceTree = "<group>"; };
|
0E0F4C6529C84CF60022E884 /* LogoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogoView.swift; sourceTree = "<group>"; };
|
||||||
0E1AD5CB2A2682DA002AE6E6 /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = "<group>"; };
|
|
||||||
0E1AD5CD2A268645002AE6E6 /* Errors+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Errors+L10n.swift"; sourceTree = "<group>"; };
|
0E1AD5CD2A268645002AE6E6 /* Errors+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Errors+L10n.swift"; sourceTree = "<group>"; };
|
||||||
0E1B5F5B29C506AC00FE7D18 /* DiagnosticsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiagnosticsSection.swift; sourceTree = "<group>"; };
|
0E1B5F5B29C506AC00FE7D18 /* DiagnosticsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiagnosticsSection.swift; sourceTree = "<group>"; };
|
||||||
0E1C0A52238FFF97009FC087 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
0E1C0A52238FFF97009FC087 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
0E1F5627287F0ECB00F8ADD7 /* ProviderProfileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderProfileItem.swift; sourceTree = "<group>"; };
|
0E1F5627287F0ECB00F8ADD7 /* ProviderProfileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderProfileItem.swift; sourceTree = "<group>"; };
|
||||||
0E1F5629287F0EEE00F8ADD7 /* ProviderProfileItem+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProviderProfileItem+ViewModel.swift"; sourceTree = "<group>"; };
|
0E1F5629287F0EEE00F8ADD7 /* ProviderProfileItem+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProviderProfileItem+ViewModel.swift"; sourceTree = "<group>"; };
|
||||||
0E23B4A12298559800304C30 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
0E23B4A12298559800304C30 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
||||||
0E293850285A70AC002A6E0E /* AppPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPreference.swift; sourceTree = "<group>"; };
|
|
||||||
0E293856285A73BC002A6E0E /* AppContext+Shared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppContext+Shared.swift"; sourceTree = "<group>"; };
|
0E293856285A73BC002A6E0E /* AppContext+Shared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppContext+Shared.swift"; sourceTree = "<group>"; };
|
||||||
0E2A8D4727ADF87F00207D04 /* PassepartoutApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassepartoutApp.swift; sourceTree = "<group>"; };
|
0E2A8D4727ADF87F00207D04 /* PassepartoutApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassepartoutApp.swift; sourceTree = "<group>"; };
|
||||||
0E2A8D4E27B04BB900207D04 /* OrganizerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrganizerView.swift; sourceTree = "<group>"; };
|
0E2A8D4E27B04BB900207D04 /* OrganizerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrganizerView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -338,15 +323,18 @@
|
||||||
0E2DE71B27DCCFE80067B9E1 /* TunnelKit+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelKit+Extensions.swift"; sourceTree = "<group>"; };
|
0E2DE71B27DCCFE80067B9E1 /* TunnelKit+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelKit+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
0E2DE71E27DCD0290067B9E1 /* TunnelKit+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelKit+L10n.swift"; sourceTree = "<group>"; };
|
0E2DE71E27DCD0290067B9E1 /* TunnelKit+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelKit+L10n.swift"; sourceTree = "<group>"; };
|
||||||
0E2DE72427DCDF550067B9E1 /* WireGuard+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WireGuard+L10n.swift"; sourceTree = "<group>"; };
|
0E2DE72427DCDF550067B9E1 /* WireGuard+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WireGuard+L10n.swift"; sourceTree = "<group>"; };
|
||||||
|
0E2E0B6D2B335A8E00E3204A /* AppPreference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppPreference.swift; sourceTree = "<group>"; };
|
||||||
|
0E2E0B6E2B335A8E00E3204A /* AppError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = "<group>"; };
|
||||||
|
0E2E0B712B335AAB00E3204A /* IntentsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntentsManager.swift; sourceTree = "<group>"; };
|
||||||
|
0E2E0B722B335AAB00E3204A /* UpgradeManagerStrategy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpgradeManagerStrategy.swift; sourceTree = "<group>"; };
|
||||||
|
0E2E0B732B335AAB00E3204A /* UpgradeManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpgradeManager.swift; sourceTree = "<group>"; };
|
||||||
|
0E2E0B742B335AAB00E3204A /* PersistenceManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PersistenceManager.swift; sourceTree = "<group>"; };
|
||||||
0E34A2AF27CAA84500C73B67 /* OpenVPN+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenVPN+L10n.swift"; sourceTree = "<group>"; };
|
0E34A2AF27CAA84500C73B67 /* OpenVPN+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenVPN+L10n.swift"; sourceTree = "<group>"; };
|
||||||
0E34A2B527CAA8CC00C73B67 /* Core+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Core+L10n.swift"; sourceTree = "<group>"; };
|
0E34A2B527CAA8CC00C73B67 /* Core+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Core+L10n.swift"; sourceTree = "<group>"; };
|
||||||
0E34A2CE27CADA6300C73B67 /* GenericVersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericVersionView.swift; sourceTree = "<group>"; };
|
0E34A2CE27CADA6300C73B67 /* GenericVersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericVersionView.swift; sourceTree = "<group>"; };
|
||||||
0E34AC7727F840890042F2AB /* OrganizerView+Scene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrganizerView+Scene.swift"; sourceTree = "<group>"; };
|
0E34AC7727F840890042F2AB /* OrganizerView+Scene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrganizerView+Scene.swift"; sourceTree = "<group>"; };
|
||||||
0E34AC8127F892C40042F2AB /* OnDemandView+SSID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnDemandView+SSID.swift"; sourceTree = "<group>"; };
|
0E34AC8127F892C40042F2AB /* OnDemandView+SSID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnDemandView+SSID.swift"; sourceTree = "<group>"; };
|
||||||
0E35C099280E95BB0071FA35 /* ProviderProfileAvailability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderProfileAvailability.swift; sourceTree = "<group>"; };
|
0E35C099280E95BB0071FA35 /* ProviderProfileAvailability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderProfileAvailability.swift; sourceTree = "<group>"; };
|
||||||
0E3A3C112AAB7C470003A5F6 /* UpgradeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpgradeManager.swift; sourceTree = "<group>"; };
|
|
||||||
0E3A3C122AAB7C480003A5F6 /* DefaultUpgradeManagerStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultUpgradeManagerStrategy.swift; sourceTree = "<group>"; };
|
|
||||||
0E3A3C152AAB8AB80003A5F6 /* UpgradeManagerStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpgradeManagerStrategy.swift; sourceTree = "<group>"; };
|
|
||||||
0E3A593B2A50975700B3FE40 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
|
0E3A593B2A50975700B3FE40 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
|
||||||
0E3B7FCC27E47B3700C66F13 /* AddHostView+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AddHostView+Name.swift"; sourceTree = "<group>"; };
|
0E3B7FCC27E47B3700C66F13 /* AddHostView+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AddHostView+Name.swift"; sourceTree = "<group>"; };
|
||||||
0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+VPN.swift"; sourceTree = "<group>"; };
|
0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+VPN.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -365,7 +353,6 @@
|
||||||
0E49F6BA27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointAdvancedView+OpenVPN.swift"; sourceTree = "<group>"; };
|
0E49F6BA27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointAdvancedView+OpenVPN.swift"; sourceTree = "<group>"; };
|
||||||
0E49F6BC27D7639000385834 /* EndpointAdvancedView+WireGuard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointAdvancedView+WireGuard.swift"; sourceTree = "<group>"; };
|
0E49F6BC27D7639000385834 /* EndpointAdvancedView+WireGuard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointAdvancedView+WireGuard.swift"; sourceTree = "<group>"; };
|
||||||
0E49F6BE27D764AF00385834 /* EndpointAdvancedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndpointAdvancedView.swift; sourceTree = "<group>"; };
|
0E49F6BE27D764AF00385834 /* EndpointAdvancedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndpointAdvancedView.swift; sourceTree = "<group>"; };
|
||||||
0E53249627D26B51002565C3 /* ProductManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProductManager.swift; sourceTree = "<group>"; };
|
|
||||||
0E5324A827D2AC55002565C3 /* LongContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongContentView.swift; sourceTree = "<group>"; };
|
0E5324A827D2AC55002565C3 /* LongContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongContentView.swift; sourceTree = "<group>"; };
|
||||||
0E5349BD27C16A4500C71BB3 /* StyledPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StyledPicker.swift; sourceTree = "<group>"; };
|
0E5349BD27C16A4500C71BB3 /* StyledPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StyledPicker.swift; sourceTree = "<group>"; };
|
||||||
0E5349C527C176C200C71BB3 /* EndpointView+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointView+OpenVPN.swift"; sourceTree = "<group>"; };
|
0E5349C527C176C200C71BB3 /* EndpointView+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointView+OpenVPN.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -404,7 +391,6 @@
|
||||||
0E7577DE2817E22C00081CBE /* VPNToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNToggle.swift; sourceTree = "<group>"; };
|
0E7577DE2817E22C00081CBE /* VPNToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNToggle.swift; sourceTree = "<group>"; };
|
||||||
0E7A8C072A1D40BA00780F4B /* Picker+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Picker+OpenVPN.swift"; sourceTree = "<group>"; };
|
0E7A8C072A1D40BA00780F4B /* Picker+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Picker+OpenVPN.swift"; sourceTree = "<group>"; };
|
||||||
0E7A8C082A1D40BA00780F4B /* Picker+Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Picker+Network.swift"; sourceTree = "<group>"; };
|
0E7A8C082A1D40BA00780F4B /* Picker+Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Picker+Network.swift"; sourceTree = "<group>"; };
|
||||||
0E7A8C092A1D410400780F4B /* PersistenceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistenceManager.swift; sourceTree = "<group>"; };
|
|
||||||
0E90DFE527BACC1500EF5078 /* AddHostViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddHostViewModel.swift; sourceTree = "<group>"; };
|
0E90DFE527BACC1500EF5078 /* AddHostViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddHostViewModel.swift; sourceTree = "<group>"; };
|
||||||
0E92D7C527F103300033CB7B /* ProfileView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Configuration.swift"; sourceTree = "<group>"; };
|
0E92D7C527F103300033CB7B /* ProfileView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Configuration.swift"; sourceTree = "<group>"; };
|
||||||
0E92D7C827F1042A0033CB7B /* ProfileView+Extra.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Extra.swift"; sourceTree = "<group>"; };
|
0E92D7C827F1042A0033CB7B /* ProfileView+Extra.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Extra.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -428,7 +414,6 @@
|
||||||
0E96D31028721855005EFBCF /* Constants+Mac.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Constants+Mac.swift"; sourceTree = "<group>"; };
|
0E96D31028721855005EFBCF /* Constants+Mac.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Constants+Mac.swift"; sourceTree = "<group>"; };
|
||||||
0E96D31328721FC3005EFBCF /* ObservableProcessTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableProcessTransformer.swift; sourceTree = "<group>"; };
|
0E96D31328721FC3005EFBCF /* ObservableProcessTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableProcessTransformer.swift; sourceTree = "<group>"; };
|
||||||
0E9AA977259F756A003FAFF1 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; };
|
0E9AA977259F756A003FAFF1 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; };
|
||||||
0E9C232F27F47032007D5FC7 /* IntentsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentsManager.swift; sourceTree = "<group>"; };
|
|
||||||
0E9C233227F47E95007D5FC7 /* IntentDispatcher+Activities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IntentDispatcher+Activities.swift"; sourceTree = "<group>"; };
|
0E9C233227F47E95007D5FC7 /* IntentDispatcher+Activities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IntentDispatcher+Activities.swift"; sourceTree = "<group>"; };
|
||||||
0E9C3B6E27FC573E00D0F02E /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
|
0E9C3B6E27FC573E00D0F02E /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
|
||||||
0E9E5AE327B44CF1008C95DA /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
0E9E5AE327B44CF1008C95DA /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
@ -460,10 +445,7 @@
|
||||||
0EA5912C2733DDFC0096F796 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Intents.strings; sourceTree = "<group>"; };
|
0EA5912C2733DDFC0096F796 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Intents.strings; sourceTree = "<group>"; };
|
||||||
0EA5912E2733DDFD0096F796 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Intents.strings; sourceTree = "<group>"; };
|
0EA5912E2733DDFD0096F796 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Intents.strings; sourceTree = "<group>"; };
|
||||||
0EA9030A287045F70087BC73 /* SystemMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemMenu.swift; sourceTree = "<group>"; };
|
0EA9030A287045F70087BC73 /* SystemMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemMenu.swift; sourceTree = "<group>"; };
|
||||||
0EB13BAC290E825E003CB654 /* PassepartoutTests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PassepartoutTests.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
0EB13BBF290E8C8D003CB654 /* PassepartoutTestsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassepartoutTestsApp.swift; sourceTree = "<group>"; };
|
|
||||||
0EB17EA127D2263700D473B5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
0EB17EA127D2263700D473B5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
||||||
0EB17EA327D2263700D473B5 /* LocalProduct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalProduct.swift; sourceTree = "<group>"; };
|
|
||||||
0EB17EA527D2263700D473B5 /* Constants+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Constants+App.swift"; sourceTree = "<group>"; };
|
0EB17EA527D2263700D473B5 /* Constants+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Constants+App.swift"; sourceTree = "<group>"; };
|
||||||
0EB17EB927D2560300D473B5 /* PassepartoutProviders+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PassepartoutProviders+Extensions.swift"; sourceTree = "<group>"; };
|
0EB17EB927D2560300D473B5 /* PassepartoutProviders+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PassepartoutProviders+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
0EB3412F27C7761A00483410 /* Binding+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Binding+Extensions.swift"; sourceTree = "<group>"; };
|
0EB3412F27C7761A00483410 /* Binding+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Binding+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -487,6 +469,8 @@
|
||||||
0EBE2FD72360F89600F0D5AB /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
0EBE2FD72360F89600F0D5AB /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
0EBE2FD82360F89600F0D5AB /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
0EBE2FD82360F89600F0D5AB /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
0EBE880E281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrganizerView+ProfileRow.swift"; sourceTree = "<group>"; };
|
0EBE880E281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrganizerView+ProfileRow.swift"; sourceTree = "<group>"; };
|
||||||
|
0EBF254D2B334C980045C547 /* StoreKitReceiptReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreKitReceiptReader.swift; sourceTree = "<group>"; };
|
||||||
|
0EBF254F2B334CA00045C547 /* DefaultUpgradeManagerStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultUpgradeManagerStrategy.swift; sourceTree = "<group>"; };
|
||||||
0ECB78DA285F52F700B0E460 /* PassepartoutMac.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PassepartoutMac.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
|
0ECB78DA285F52F700B0E460 /* PassepartoutMac.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PassepartoutMac.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0ECB78E1285F53ED00B0E460 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
0ECB78E1285F53ED00B0E460 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||||
0ECB78EA2861D1F300B0E460 /* PassepartoutLibrary */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = PassepartoutLibrary; sourceTree = "<group>"; };
|
0ECB78EA2861D1F300B0E460 /* PassepartoutLibrary */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = PassepartoutLibrary; sourceTree = "<group>"; };
|
||||||
|
@ -553,14 +537,6 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
0EB13BA9290E825E003CB654 /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
0E7A8C0E2A1D4A6E00780F4B /* PassepartoutLibrary in Frameworks */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
0ECB78D7285F52F700B0E460 /* Frameworks */ = {
|
0ECB78D7285F52F700B0E460 /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -624,6 +600,18 @@
|
||||||
path = Reusable;
|
path = Reusable;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
0E2E0B792B335B3C00E3204A /* Managers */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
0EBF254F2B334CA00045C547 /* DefaultUpgradeManagerStrategy.swift */,
|
||||||
|
0E2E0B712B335AAB00E3204A /* IntentsManager.swift */,
|
||||||
|
0E2E0B742B335AAB00E3204A /* PersistenceManager.swift */,
|
||||||
|
0E2E0B732B335AAB00E3204A /* UpgradeManager.swift */,
|
||||||
|
0E2E0B722B335AAB00E3204A /* UpgradeManagerStrategy.swift */,
|
||||||
|
);
|
||||||
|
path = Managers;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
0E34A2B827CAA8EA00C73B67 /* L10n */ = {
|
0E34A2B827CAA8EA00C73B67 /* L10n */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -701,31 +689,17 @@
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
0E3A593F2A54ACC900B3FE40 /* Managers */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
0E3A3C122AAB7C480003A5F6 /* DefaultUpgradeManagerStrategy.swift */,
|
|
||||||
0E9C232F27F47032007D5FC7 /* IntentsManager.swift */,
|
|
||||||
0E7A8C092A1D410400780F4B /* PersistenceManager.swift */,
|
|
||||||
0E53249627D26B51002565C3 /* ProductManager.swift */,
|
|
||||||
0E3A3C112AAB7C470003A5F6 /* UpgradeManager.swift */,
|
|
||||||
0E3A3C152AAB8AB80003A5F6 /* UpgradeManagerStrategy.swift */,
|
|
||||||
);
|
|
||||||
path = Managers;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
0E3A59402A54AD4300B3FE40 /* Domain */ = {
|
0E3A59402A54AD4300B3FE40 /* Domain */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0E1AD5CB2A2682DA002AE6E6 /* AppError.swift */,
|
0E2E0B6E2B335A8E00E3204A /* AppError.swift */,
|
||||||
0E293850285A70AC002A6E0E /* AppPreference.swift */,
|
0E2E0B6D2B335A8E00E3204A /* AppPreference.swift */,
|
||||||
0E0392762818732D00827C10 /* BuildProducts.swift */,
|
|
||||||
0EA591142733DDDA0096F796 /* Intents.intentdefinition */,
|
|
||||||
0EA591122733DD4E0096F796 /* IntentDispatcher.swift */,
|
0EA591122733DD4E0096F796 /* IntentDispatcher.swift */,
|
||||||
0E9C233227F47E95007D5FC7 /* IntentDispatcher+Activities.swift */,
|
0E9C233227F47E95007D5FC7 /* IntentDispatcher+Activities.swift */,
|
||||||
0EB17EA327D2263700D473B5 /* LocalProduct.swift */,
|
0EA591142733DDDA0096F796 /* Intents.intentdefinition */,
|
||||||
0E7A8C072A1D40BA00780F4B /* Picker+OpenVPN.swift */,
|
|
||||||
0E7A8C082A1D40BA00780F4B /* Picker+Network.swift */,
|
0E7A8C082A1D40BA00780F4B /* Picker+Network.swift */,
|
||||||
|
0E7A8C072A1D40BA00780F4B /* Picker+OpenVPN.swift */,
|
||||||
|
0EBF254D2B334C980045C547 /* StoreKitReceiptReader.swift */,
|
||||||
);
|
);
|
||||||
path = Domain;
|
path = Domain;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -830,9 +804,7 @@
|
||||||
children = (
|
children = (
|
||||||
0EE315DB2733104700F5D461 /* Packages */,
|
0EE315DB2733104700F5D461 /* Packages */,
|
||||||
0E23B4A12298559800304C30 /* Config.xcconfig */,
|
0E23B4A12298559800304C30 /* Config.xcconfig */,
|
||||||
0E0480372A1A4AE000462F2F /* Passepartout.xctestplan */,
|
|
||||||
0E9AA982259F7674003FAFF1 /* Passepartout */,
|
0E9AA982259F7674003FAFF1 /* Passepartout */,
|
||||||
0EB13BAD290E825E003CB654 /* PassepartoutTests */,
|
|
||||||
0E57F63920C83FC5008323CF /* Products */,
|
0E57F63920C83FC5008323CF /* Products */,
|
||||||
374B9F085E1148C37CF9117A /* Frameworks */,
|
374B9F085E1148C37CF9117A /* Frameworks */,
|
||||||
);
|
);
|
||||||
|
@ -846,7 +818,6 @@
|
||||||
0ED2B34A27D3C77800FD8EA9 /* PassepartoutWireGuardTunnel.appex */,
|
0ED2B34A27D3C77800FD8EA9 /* PassepartoutWireGuardTunnel.appex */,
|
||||||
0E41BD97286711C3006346B4 /* PassepartoutLauncher.app */,
|
0E41BD97286711C3006346B4 /* PassepartoutLauncher.app */,
|
||||||
0ECB78DA285F52F700B0E460 /* PassepartoutMac.bundle */,
|
0ECB78DA285F52F700B0E460 /* PassepartoutMac.bundle */,
|
||||||
0EB13BAC290E825E003CB654 /* PassepartoutTests.app */,
|
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -902,9 +873,9 @@
|
||||||
0E29385A285A749E002A6E0E /* Context */,
|
0E29385A285A749E002A6E0E /* Context */,
|
||||||
0E3A59402A54AD4300B3FE40 /* Domain */,
|
0E3A59402A54AD4300B3FE40 /* Domain */,
|
||||||
0E49F6C927DB398100385834 /* Extensions */,
|
0E49F6C927DB398100385834 /* Extensions */,
|
||||||
0E3A593F2A54ACC900B3FE40 /* Managers */,
|
|
||||||
0E34A2B827CAA8EA00C73B67 /* L10n */,
|
0E34A2B827CAA8EA00C73B67 /* L10n */,
|
||||||
0E5467F12867A52B00F74D1C /* Mac */,
|
0E5467F12867A52B00F74D1C /* Mac */,
|
||||||
|
0E2E0B792B335B3C00E3204A /* Managers */,
|
||||||
0E2C171C27CB6307007E8488 /* Reusable */,
|
0E2C171C27CB6307007E8488 /* Reusable */,
|
||||||
0E35C0AE280EF8A80071FA35 /* Views */,
|
0E35C0AE280EF8A80071FA35 /* Views */,
|
||||||
0E6059CA27FCC5DE003F4063 /* Assets.xcassets */,
|
0E6059CA27FCC5DE003F4063 /* Assets.xcassets */,
|
||||||
|
@ -925,14 +896,6 @@
|
||||||
path = App;
|
path = App;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
0EB13BAD290E825E003CB654 /* PassepartoutTests */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
0EB13BBF290E8C8D003CB654 /* PassepartoutTestsApp.swift */,
|
|
||||||
);
|
|
||||||
path = PassepartoutTests;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
0ECB78D1285F4F4000B0E460 /* AppShared */ = {
|
0ECB78D1285F4F4000B0E460 /* AppShared */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -1100,27 +1063,6 @@
|
||||||
productReference = 0E57F63820C83FC5008323CF /* Passepartout.app */;
|
productReference = 0E57F63820C83FC5008323CF /* Passepartout.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
0EB13BAB290E825E003CB654 /* PassepartoutTests */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = 0EB13BBA290E8260003CB654 /* Build configuration list for PBXNativeTarget "PassepartoutTests" */;
|
|
||||||
buildPhases = (
|
|
||||||
0EB13BA8290E825E003CB654 /* Sources */,
|
|
||||||
0EB13BA9290E825E003CB654 /* Frameworks */,
|
|
||||||
0EB13BAA290E825E003CB654 /* Resources */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
0EB13BBC290E82F1003CB654 /* PBXTargetDependency */,
|
|
||||||
);
|
|
||||||
name = PassepartoutTests;
|
|
||||||
packageProductDependencies = (
|
|
||||||
0E7A8C0D2A1D4A6E00780F4B /* PassepartoutLibrary */,
|
|
||||||
);
|
|
||||||
productName = PassepartoutTests;
|
|
||||||
productReference = 0EB13BAC290E825E003CB654 /* PassepartoutTests.app */;
|
|
||||||
productType = "com.apple.product-type.application";
|
|
||||||
};
|
|
||||||
0ECB78D9285F52F700B0E460 /* PassepartoutMac */ = {
|
0ECB78D9285F52F700B0E460 /* PassepartoutMac */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 0ECB78DD285F52F700B0E460 /* Build configuration list for PBXNativeTarget "PassepartoutMac" */;
|
buildConfigurationList = 0ECB78DD285F52F700B0E460 /* Build configuration list for PBXNativeTarget "PassepartoutMac" */;
|
||||||
|
@ -1188,7 +1130,7 @@
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = YES;
|
BuildIndependentTargetsInParallel = YES;
|
||||||
LastSwiftUpdateCheck = 1400;
|
LastSwiftUpdateCheck = 1510;
|
||||||
LastUpgradeCheck = 1430;
|
LastUpgradeCheck = 1430;
|
||||||
ORGANIZATIONNAME = "Davide De Rosa";
|
ORGANIZATIONNAME = "Davide De Rosa";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
|
@ -1213,10 +1155,6 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
0EB13BAB290E825E003CB654 = {
|
|
||||||
CreatedOnToolsVersion = 14.0.1;
|
|
||||||
LastSwiftMigration = 1400;
|
|
||||||
};
|
|
||||||
0ECB78D9285F52F700B0E460 = {
|
0ECB78D9285F52F700B0E460 = {
|
||||||
CreatedOnToolsVersion = 13.4;
|
CreatedOnToolsVersion = 13.4;
|
||||||
LastSwiftMigration = 1340;
|
LastSwiftMigration = 1340;
|
||||||
|
@ -1269,7 +1207,6 @@
|
||||||
0E57F63720C83FC5008323CF /* Passepartout */,
|
0E57F63720C83FC5008323CF /* Passepartout */,
|
||||||
0ECB78D9285F52F700B0E460 /* PassepartoutMac */,
|
0ECB78D9285F52F700B0E460 /* PassepartoutMac */,
|
||||||
0E41BD96286711C3006346B4 /* PassepartoutLauncher */,
|
0E41BD96286711C3006346B4 /* PassepartoutLauncher */,
|
||||||
0EB13BAB290E825E003CB654 /* PassepartoutTests */,
|
|
||||||
0EDE8DBE20C86910004C739C /* OpenVPNTunnel */,
|
0EDE8DBE20C86910004C739C /* OpenVPNTunnel */,
|
||||||
0ECF71F327B6D9CD00CDB528 /* WireGuardGo */,
|
0ECF71F327B6D9CD00CDB528 /* WireGuardGo */,
|
||||||
0ED2B33E27D3C77800FD8EA9 /* WireGuardTunnel */,
|
0ED2B33E27D3C77800FD8EA9 /* WireGuardTunnel */,
|
||||||
|
@ -1300,13 +1237,6 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
0EB13BAA290E825E003CB654 /* Resources */ = {
|
|
||||||
isa = PBXResourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
0ECB78D8285F52F700B0E460 /* Resources */ = {
|
0ECB78D8285F52F700B0E460 /* Resources */ = {
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -1439,6 +1369,7 @@
|
||||||
0EA591162733DDDA0096F796 /* Intents.intentdefinition in Sources */,
|
0EA591162733DDDA0096F796 /* Intents.intentdefinition in Sources */,
|
||||||
0E34AC7827F840890042F2AB /* OrganizerView+Scene.swift in Sources */,
|
0E34AC7827F840890042F2AB /* OrganizerView+Scene.swift in Sources */,
|
||||||
0E0BD27927B2EBE500583AC5 /* ShortcutsView.swift in Sources */,
|
0E0BD27927B2EBE500583AC5 /* ShortcutsView.swift in Sources */,
|
||||||
|
0E2E0B762B335AAB00E3204A /* UpgradeManagerStrategy.swift in Sources */,
|
||||||
0E92D7C627F103300033CB7B /* ProfileView+Configuration.swift in Sources */,
|
0E92D7C627F103300033CB7B /* ProfileView+Configuration.swift in Sources */,
|
||||||
0E2DE71C27DCCFE80067B9E1 /* TunnelKit+Extensions.swift in Sources */,
|
0E2DE71C27DCCFE80067B9E1 /* TunnelKit+Extensions.swift in Sources */,
|
||||||
0ED1D6DE27DBA42100983466 /* DiagnosticsView+WireGuard.swift in Sources */,
|
0ED1D6DE27DBA42100983466 /* DiagnosticsView+WireGuard.swift in Sources */,
|
||||||
|
@ -1448,13 +1379,13 @@
|
||||||
0E34AC8227F892C40042F2AB /* OnDemandView+SSID.swift in Sources */,
|
0E34AC8227F892C40042F2AB /* OnDemandView+SSID.swift in Sources */,
|
||||||
0E3B7FCD27E47B3700C66F13 /* AddHostView+Name.swift in Sources */,
|
0E3B7FCD27E47B3700C66F13 /* AddHostView+Name.swift in Sources */,
|
||||||
0EF2212D27E66EB5001D0BD7 /* AddProviderView.swift in Sources */,
|
0EF2212D27E66EB5001D0BD7 /* AddProviderView.swift in Sources */,
|
||||||
|
0EBF25502B334CA00045C547 /* DefaultUpgradeManagerStrategy.swift in Sources */,
|
||||||
0EB90CC129C25BBD00E64628 /* InteractiveConnectionView.swift in Sources */,
|
0EB90CC129C25BBD00E64628 /* InteractiveConnectionView.swift in Sources */,
|
||||||
0E35C09A280E95BB0071FA35 /* ProviderProfileAvailability.swift in Sources */,
|
0E35C09A280E95BB0071FA35 /* ProviderProfileAvailability.swift in Sources */,
|
||||||
0E04F0092883466500BFCE1C /* DefaultLightUtils.swift in Sources */,
|
0E04F0092883466500BFCE1C /* DefaultLightUtils.swift in Sources */,
|
||||||
0E5349C827C176D100C71BB3 /* EndpointView+WireGuard.swift in Sources */,
|
0E5349C827C176D100C71BB3 /* EndpointView+WireGuard.swift in Sources */,
|
||||||
0EBC076027EC587900208AD9 /* SwiftGen+Strings.swift in Sources */,
|
0EBC076027EC587900208AD9 /* SwiftGen+Strings.swift in Sources */,
|
||||||
0E7A8C0F2A1D54DE00780F4B /* Picker+OpenVPN.swift in Sources */,
|
0E7A8C0F2A1D54DE00780F4B /* Picker+OpenVPN.swift in Sources */,
|
||||||
0E0392772818732D00827C10 /* BuildProducts.swift in Sources */,
|
|
||||||
0E0F4C5C29C76B790022E884 /* SceneDelegate+Shortcuts.swift in Sources */,
|
0E0F4C5C29C76B790022E884 /* SceneDelegate+Shortcuts.swift in Sources */,
|
||||||
0E5683B927C2825D00EAF1CD /* DiagnosticsView.swift in Sources */,
|
0E5683B927C2825D00EAF1CD /* DiagnosticsView.swift in Sources */,
|
||||||
0E3FC6862867A3F9009B851C /* AppDelegate.swift in Sources */,
|
0E3FC6862867A3F9009B851C /* AppDelegate.swift in Sources */,
|
||||||
|
@ -1478,7 +1409,6 @@
|
||||||
0ED89C1E27DE3F8D008B36D6 /* IntentAddView.swift in Sources */,
|
0ED89C1E27DE3F8D008B36D6 /* IntentAddView.swift in Sources */,
|
||||||
0E5468002867AC9A00F74D1C /* MacUtils.swift in Sources */,
|
0E5468002867AC9A00F74D1C /* MacUtils.swift in Sources */,
|
||||||
0E0F4C6429C84B5A0022E884 /* LockableView.swift in Sources */,
|
0E0F4C6429C84B5A0022E884 /* LockableView.swift in Sources */,
|
||||||
0E3A3C132AAB7C480003A5F6 /* UpgradeManager.swift in Sources */,
|
|
||||||
0E96D3052872010A005EFBCF /* DefaultLightVPNManager.swift in Sources */,
|
0E96D3052872010A005EFBCF /* DefaultLightVPNManager.swift in Sources */,
|
||||||
0EBE880F281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift in Sources */,
|
0EBE880F281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift in Sources */,
|
||||||
0ED30DCF27EA1EF80057D8A3 /* PaywallView+Restricted.swift in Sources */,
|
0ED30DCF27EA1EF80057D8A3 /* PaywallView+Restricted.swift in Sources */,
|
||||||
|
@ -1494,12 +1424,15 @@
|
||||||
0E0838FA2877325A00A34EC0 /* LightProviderManager.swift in Sources */,
|
0E0838FA2877325A00A34EC0 /* LightProviderManager.swift in Sources */,
|
||||||
0ED7D62F2867328A009F2F8F /* Constants+Library.swift in Sources */,
|
0ED7D62F2867328A009F2F8F /* Constants+Library.swift in Sources */,
|
||||||
0E5467FA2867AA0A00F74D1C /* MacBundleDelegate.swift in Sources */,
|
0E5467FA2867AA0A00F74D1C /* MacBundleDelegate.swift in Sources */,
|
||||||
|
0EBF254E2B334C980045C547 /* StoreKitReceiptReader.swift in Sources */,
|
||||||
0E0BD27327B2EA2C00583AC5 /* MainView.swift in Sources */,
|
0E0BD27327B2EA2C00583AC5 /* MainView.swift in Sources */,
|
||||||
0E0F4C5A29C761850022E884 /* SceneDelegate.swift in Sources */,
|
0E0F4C5A29C761850022E884 /* SceneDelegate.swift in Sources */,
|
||||||
0EB17EBA27D2560300D473B5 /* PassepartoutProviders+Extensions.swift in Sources */,
|
0EB17EBA27D2560300D473B5 /* PassepartoutProviders+Extensions.swift in Sources */,
|
||||||
0E3B7FDA27E51A0200C66F13 /* ProfileView+Provider.swift in Sources */,
|
0E3B7FDA27E51A0200C66F13 /* ProfileView+Provider.swift in Sources */,
|
||||||
|
0E2E0B6F2B335A8E00E3204A /* AppPreference.swift in Sources */,
|
||||||
0E5468062867AEC500F74D1C /* MacMenu.swift in Sources */,
|
0E5468062867AEC500F74D1C /* MacMenu.swift in Sources */,
|
||||||
0E71ACE327C0F2E400F85C4B /* Providers+L10n.swift in Sources */,
|
0E71ACE327C0F2E400F85C4B /* Providers+L10n.swift in Sources */,
|
||||||
|
0E2E0B752B335AAB00E3204A /* IntentsManager.swift in Sources */,
|
||||||
0E71ACF127C1073800F85C4B /* ProviderLocationView.swift in Sources */,
|
0E71ACF127C1073800F85C4B /* ProviderLocationView.swift in Sources */,
|
||||||
0E2A8D4F27B04BBA00207D04 /* OrganizerView.swift in Sources */,
|
0E2A8D4F27B04BBA00207D04 /* OrganizerView.swift in Sources */,
|
||||||
0E49F6BB27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift in Sources */,
|
0E49F6BB27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift in Sources */,
|
||||||
|
@ -1509,13 +1442,11 @@
|
||||||
0E96D2FC2871D94E005EFBCF /* DefaultLightProfileManager.swift in Sources */,
|
0E96D2FC2871D94E005EFBCF /* DefaultLightProfileManager.swift in Sources */,
|
||||||
0EF0FAF627DD0211007EB181 /* PaywallView.swift in Sources */,
|
0EF0FAF627DD0211007EB181 /* PaywallView.swift in Sources */,
|
||||||
0E7A8C102A1D54DE00780F4B /* Picker+Network.swift in Sources */,
|
0E7A8C102A1D54DE00780F4B /* Picker+Network.swift in Sources */,
|
||||||
0E293851285A70AC002A6E0E /* AppPreference.swift in Sources */,
|
0E2E0B702B335A8E00E3204A /* AppError.swift in Sources */,
|
||||||
0E5349BE27C16A4500C71BB3 /* StyledPicker.swift in Sources */,
|
0E5349BE27C16A4500C71BB3 /* StyledPicker.swift in Sources */,
|
||||||
0E2C172B27CB63F9007E8488 /* Reviewer.swift in Sources */,
|
0E2C172B27CB63F9007E8488 /* Reviewer.swift in Sources */,
|
||||||
0E71ACDD27C0295C00F85C4B /* View+Extensions.swift in Sources */,
|
0E71ACDD27C0295C00F85C4B /* View+Extensions.swift in Sources */,
|
||||||
0E3A3C162AAB8AB80003A5F6 /* UpgradeManagerStrategy.swift in Sources */,
|
|
||||||
0E021D9C284E68580077EF5D /* CoreContext.swift in Sources */,
|
0E021D9C284E68580077EF5D /* CoreContext.swift in Sources */,
|
||||||
0E7A8C0A2A1D410500780F4B /* PersistenceManager.swift in Sources */,
|
|
||||||
A38D607728AFCFD20005C271 /* SettingsView.swift in Sources */,
|
A38D607728AFCFD20005C271 /* SettingsView.swift in Sources */,
|
||||||
0E34A2B627CAA8CC00C73B67 /* Core+L10n.swift in Sources */,
|
0E34A2B627CAA8CC00C73B67 /* Core+L10n.swift in Sources */,
|
||||||
0E7577DF2817E22C00081CBE /* VPNToggle.swift in Sources */,
|
0E7577DF2817E22C00081CBE /* VPNToggle.swift in Sources */,
|
||||||
|
@ -1525,7 +1456,7 @@
|
||||||
0EBC075527EBC83800208AD9 /* MailComposerView.swift in Sources */,
|
0EBC075527EBC83800208AD9 /* MailComposerView.swift in Sources */,
|
||||||
0EF0FAF727DD159C007EB181 /* IntentDispatcher.swift in Sources */,
|
0EF0FAF727DD159C007EB181 /* IntentDispatcher.swift in Sources */,
|
||||||
0E0838FD2877334300A34EC0 /* DefaultLightProviderManager.swift in Sources */,
|
0E0838FD2877334300A34EC0 /* DefaultLightProviderManager.swift in Sources */,
|
||||||
0E3A3C142AAB7C480003A5F6 /* DefaultUpgradeManagerStrategy.swift in Sources */,
|
0E2E0B772B335AAB00E3204A /* UpgradeManager.swift in Sources */,
|
||||||
0E0F4C6629C84CF60022E884 /* LogoView.swift in Sources */,
|
0E0F4C6629C84CF60022E884 /* LogoView.swift in Sources */,
|
||||||
0E039279281890B100827C10 /* AddHostView.swift in Sources */,
|
0E039279281890B100827C10 /* AddHostView.swift in Sources */,
|
||||||
0E5467F72867A57000F74D1C /* MacBridge.swift in Sources */,
|
0E5467F72867A57000F74D1C /* MacBridge.swift in Sources */,
|
||||||
|
@ -1536,14 +1467,11 @@
|
||||||
0E71ACF927C12E4800F85C4B /* CreditsView.swift in Sources */,
|
0E71ACF927C12E4800F85C4B /* CreditsView.swift in Sources */,
|
||||||
0ED89C1527DE0A0C008B36D6 /* Shortcut.swift in Sources */,
|
0ED89C1527DE0A0C008B36D6 /* Shortcut.swift in Sources */,
|
||||||
0E34A2B927CAA96A00C73B67 /* OpenVPN+L10n.swift in Sources */,
|
0E34A2B927CAA96A00C73B67 /* OpenVPN+L10n.swift in Sources */,
|
||||||
|
0E2E0B782B335AAB00E3204A /* PersistenceManager.swift in Sources */,
|
||||||
0EF8C5A828213C510053CE89 /* OrganizerView+Profiles.swift in Sources */,
|
0EF8C5A828213C510053CE89 /* OrganizerView+Profiles.swift in Sources */,
|
||||||
0E3CD483280DAE92007075C0 /* ProfileView+MainMenu.swift in Sources */,
|
0E3CD483280DAE92007075C0 /* ProfileView+MainMenu.swift in Sources */,
|
||||||
0EB17EAE27D226CF00D473B5 /* LocalProduct.swift in Sources */,
|
|
||||||
0E71ACEB27C1060D00F85C4B /* EndpointView.swift in Sources */,
|
0E71ACEB27C1060D00F85C4B /* EndpointView.swift in Sources */,
|
||||||
0E293857285A73BC002A6E0E /* AppContext+Shared.swift in Sources */,
|
0E293857285A73BC002A6E0E /* AppContext+Shared.swift in Sources */,
|
||||||
0E1AD5CC2A2682DA002AE6E6 /* AppError.swift in Sources */,
|
|
||||||
0E53249927D26B51002565C3 /* ProductManager.swift in Sources */,
|
|
||||||
0E9C233027F47032007D5FC7 /* IntentsManager.swift in Sources */,
|
|
||||||
0EB4042C27CA0E8C00378B1A /* Unlocalized.swift in Sources */,
|
0EB4042C27CA0E8C00378B1A /* Unlocalized.swift in Sources */,
|
||||||
0EB4042E27CA136300378B1A /* AddingTextField.swift in Sources */,
|
0EB4042E27CA136300378B1A /* AddingTextField.swift in Sources */,
|
||||||
0EE8B7E327FF340F00B68621 /* VPNProtocolType+FileExtensions.swift in Sources */,
|
0EE8B7E327FF340F00B68621 /* VPNProtocolType+FileExtensions.swift in Sources */,
|
||||||
|
@ -1566,14 +1494,6 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
0EB13BA8290E825E003CB654 /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
0EB13BC0290E8C8D003CB654 /* PassepartoutTestsApp.swift in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
0ED2B34027D3C77800FD8EA9 /* Sources */ = {
|
0ED2B34027D3C77800FD8EA9 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -1603,11 +1523,6 @@
|
||||||
target = 0E41BD96286711C3006346B4 /* PassepartoutLauncher */;
|
target = 0E41BD96286711C3006346B4 /* PassepartoutLauncher */;
|
||||||
targetProxy = 0E41BDAA286713F6006346B4 /* PBXContainerItemProxy */;
|
targetProxy = 0E41BDAA286713F6006346B4 /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
0EB13BBC290E82F1003CB654 /* PBXTargetDependency */ = {
|
|
||||||
isa = PBXTargetDependency;
|
|
||||||
target = 0ECF71F327B6D9CD00CDB528 /* WireGuardGo */;
|
|
||||||
targetProxy = 0EB13BBB290E82F1003CB654 /* PBXContainerItemProxy */;
|
|
||||||
};
|
|
||||||
0EB2B14A2733FB6F007705AB /* PBXTargetDependency */ = {
|
0EB2B14A2733FB6F007705AB /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = 0EDE8DBE20C86910004C739C /* OpenVPNTunnel */;
|
target = 0EDE8DBE20C86910004C739C /* OpenVPNTunnel */;
|
||||||
|
@ -1710,7 +1625,6 @@
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = DTDYD63ZX9;
|
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
INFOPLIST_FILE = Passepartout/Launcher/Info.plist;
|
INFOPLIST_FILE = Passepartout/Launcher/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -1735,7 +1649,6 @@
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = DTDYD63ZX9;
|
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
INFOPLIST_FILE = Passepartout/Launcher/Info.plist;
|
INFOPLIST_FILE = Passepartout/Launcher/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -1789,6 +1702,7 @@
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CURRENT_PROJECT_VERSION = 3548;
|
CURRENT_PROJECT_VERSION = 3548;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
DEVELOPMENT_TEAM = "$(CFG_TEAM_ID)";
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
|
@ -1856,6 +1770,7 @@
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CURRENT_PROJECT_VERSION = 3548;
|
CURRENT_PROJECT_VERSION = 3548;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
DEVELOPMENT_TEAM = "$(CFG_TEAM_ID)";
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
@ -1889,7 +1804,6 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = Passepartout/App/App.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Passepartout/App/App.entitlements;
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 3548;
|
CURRENT_PROJECT_VERSION = 3548;
|
||||||
DEVELOPMENT_TEAM = DTDYD63ZX9;
|
|
||||||
INFOPLIST_FILE = Passepartout/App/Info.plist;
|
INFOPLIST_FILE = Passepartout/App/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -1916,7 +1830,6 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = Passepartout/App/App.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Passepartout/App/App.entitlements;
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 3548;
|
CURRENT_PROJECT_VERSION = 3548;
|
||||||
DEVELOPMENT_TEAM = DTDYD63ZX9;
|
|
||||||
INFOPLIST_FILE = Passepartout/App/Info.plist;
|
INFOPLIST_FILE = Passepartout/App/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -1932,75 +1845,6 @@
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
0EB13BB8290E8260003CB654 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEAD_CODE_STRIPPING = YES;
|
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
|
||||||
ENABLE_PREVIEWS = YES;
|
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
|
||||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
|
||||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
|
||||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
|
||||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
|
|
||||||
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
|
|
||||||
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
|
|
||||||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
|
|
||||||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
|
||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
|
||||||
MTL_FAST_MATH = YES;
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.algoritmico.ios.PassepartoutTests;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
|
||||||
SDKROOT = auto;
|
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
|
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
|
||||||
SWIFT_VERSION = 5.0;
|
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
0EB13BB9290E8260003CB654 /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEAD_CODE_STRIPPING = YES;
|
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
|
||||||
ENABLE_PREVIEWS = YES;
|
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
|
||||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
|
||||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
|
||||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
|
||||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
|
|
||||||
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
|
|
||||||
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
|
|
||||||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
|
|
||||||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
|
||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
|
||||||
MTL_FAST_MATH = YES;
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.algoritmico.ios.PassepartoutTests;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
|
||||||
SDKROOT = auto;
|
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
|
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
|
||||||
SWIFT_VERSION = 5.0;
|
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
0ECB78DB285F52F700B0E460 /* Debug */ = {
|
0ECB78DB285F52F700B0E460 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
@ -2012,7 +1856,6 @@
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 3548;
|
CURRENT_PROJECT_VERSION = 3548;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = "";
|
|
||||||
INFOPLIST_FILE = Passepartout/Mac/Info.plist;
|
INFOPLIST_FILE = Passepartout/Mac/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -2046,7 +1889,6 @@
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 3548;
|
CURRENT_PROJECT_VERSION = 3548;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = "";
|
|
||||||
INFOPLIST_FILE = Passepartout/Mac/Info.plist;
|
INFOPLIST_FILE = Passepartout/Mac/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -2076,7 +1918,6 @@
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUGGING_SYMBOLS = YES;
|
DEBUGGING_SYMBOLS = YES;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
DEVELOPMENT_TEAM = DTDYD63ZX9;
|
|
||||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
@ -2094,7 +1935,6 @@
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_TEAM = DTDYD63ZX9;
|
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
OTHER_CFLAGS = "";
|
OTHER_CFLAGS = "";
|
||||||
|
@ -2108,7 +1948,6 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements;
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
DEVELOPMENT_TEAM = DTDYD63ZX9;
|
|
||||||
INFOPLIST_FILE = Passepartout/Tunnel/Info.plist;
|
INFOPLIST_FILE = Passepartout/Tunnel/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -2129,7 +1968,6 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements;
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
DEVELOPMENT_TEAM = DTDYD63ZX9;
|
|
||||||
INFOPLIST_FILE = Passepartout/Tunnel/Info.plist;
|
INFOPLIST_FILE = Passepartout/Tunnel/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -2150,7 +1988,6 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements;
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
DEVELOPMENT_TEAM = DTDYD63ZX9;
|
|
||||||
INFOPLIST_FILE = Passepartout/Tunnel/Info.plist;
|
INFOPLIST_FILE = Passepartout/Tunnel/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -2171,7 +2008,6 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements;
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
DEVELOPMENT_TEAM = DTDYD63ZX9;
|
|
||||||
INFOPLIST_FILE = Passepartout/Tunnel/Info.plist;
|
INFOPLIST_FILE = Passepartout/Tunnel/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -2217,15 +2053,6 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
0EB13BBA290E8260003CB654 /* Build configuration list for PBXNativeTarget "PassepartoutTests" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
0EB13BB8290E8260003CB654 /* Debug */,
|
|
||||||
0EB13BB9290E8260003CB654 /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
0ECB78DD285F52F700B0E460 /* Build configuration list for PBXNativeTarget "PassepartoutMac" */ = {
|
0ECB78DD285F52F700B0E460 /* Build configuration list for PBXNativeTarget "PassepartoutMac" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
|
@ -2285,10 +2112,6 @@
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = PassepartoutLibrary;
|
productName = PassepartoutLibrary;
|
||||||
};
|
};
|
||||||
0E7A8C0D2A1D4A6E00780F4B /* PassepartoutLibrary */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
productName = PassepartoutLibrary;
|
|
||||||
};
|
|
||||||
0ED2B33827D3C49800FD8EA9 /* OpenVPNAppExtension */ = {
|
0ED2B33827D3C49800FD8EA9 /* OpenVPNAppExtension */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = OpenVPNAppExtension;
|
productName = OpenVPNAppExtension;
|
||||||
|
|
|
@ -68,7 +68,8 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
shouldAutocreateTestPlan = "YES">
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
|
@ -78,54 +79,6 @@
|
||||||
ReferencedContainer = "container:Passepartout.xcodeproj">
|
ReferencedContainer = "container:Passepartout.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<TestPlans>
|
|
||||||
<TestPlanReference
|
|
||||||
reference = "container:Passepartout.xctestplan"
|
|
||||||
default = "YES">
|
|
||||||
</TestPlanReference>
|
|
||||||
</TestPlans>
|
|
||||||
<Testables>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutCoreTests"
|
|
||||||
BuildableName = "PassepartoutCoreTests"
|
|
||||||
BlueprintName = "PassepartoutCoreTests"
|
|
||||||
ReferencedContainer = "container:PassepartoutLibrary">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutProvidersTests"
|
|
||||||
BuildableName = "PassepartoutProvidersTests"
|
|
||||||
BlueprintName = "PassepartoutProvidersTests"
|
|
||||||
ReferencedContainer = "container:PassepartoutLibrary">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutServicesTests"
|
|
||||||
BuildableName = "PassepartoutServicesTests"
|
|
||||||
BlueprintName = "PassepartoutServicesTests"
|
|
||||||
ReferencedContainer = "container:PassepartoutLibrary">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutVPNTests"
|
|
||||||
BuildableName = "PassepartoutVPNTests"
|
|
||||||
BlueprintName = "PassepartoutVPNTests"
|
|
||||||
ReferencedContainer = "container:PassepartoutLibrary">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
|
|
|
@ -1,118 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1430"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "0EB13BAB290E825E003CB654"
|
|
||||||
BuildableName = "PassepartoutTests.app"
|
|
||||||
BlueprintName = "PassepartoutTests"
|
|
||||||
ReferencedContainer = "container:Passepartout.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutCoreTests"
|
|
||||||
BuildableName = "PassepartoutCoreTests"
|
|
||||||
BlueprintName = "PassepartoutCoreTests"
|
|
||||||
ReferencedContainer = "container:PassepartoutLibrary">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutProvidersTests"
|
|
||||||
BuildableName = "PassepartoutProvidersTests"
|
|
||||||
BlueprintName = "PassepartoutProvidersTests"
|
|
||||||
ReferencedContainer = "container:PassepartoutLibrary">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutServicesTests"
|
|
||||||
BuildableName = "PassepartoutServicesTests"
|
|
||||||
BlueprintName = "PassepartoutServicesTests"
|
|
||||||
ReferencedContainer = "container:PassepartoutLibrary">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutVPNTests"
|
|
||||||
BuildableName = "PassepartoutVPNTests"
|
|
||||||
BlueprintName = "PassepartoutVPNTests"
|
|
||||||
ReferencedContainer = "container:PassepartoutLibrary">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "0EB13BAB290E825E003CB654"
|
|
||||||
BuildableName = "PassepartoutTests.app"
|
|
||||||
BlueprintName = "PassepartoutTests"
|
|
||||||
ReferencedContainer = "container:Passepartout.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "0EB13BAB290E825E003CB654"
|
|
||||||
BuildableName = "PassepartoutTests.app"
|
|
||||||
BlueprintName = "PassepartoutTests"
|
|
||||||
ReferencedContainer = "container:Passepartout.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
|
@ -1,73 +0,0 @@
|
||||||
{
|
|
||||||
"configurations" : [
|
|
||||||
{
|
|
||||||
"id" : "38DB0D3B-F3E5-4BD4-B413-B9C92AE7A2F5",
|
|
||||||
"name" : "Configuration 1",
|
|
||||||
"options" : {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"defaultOptions" : {
|
|
||||||
"codeCoverage" : false,
|
|
||||||
"commandLineArgumentEntries" : [
|
|
||||||
{
|
|
||||||
"argument" : "-com.apple.CoreData.SQLDebug 0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"argument" : "-com.apple.CoreData.Logging.stderr 0 "
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"argument" : "-com.apple.CoreData.CloudKitDebug 0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"argument" : "-com.apple.CoreData.ConcurrencyDebug 0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"argument" : "-com.apple.CoreData.MigrationDebug 0"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"environmentVariableEntries" : [
|
|
||||||
{
|
|
||||||
"key" : "APP_TYPE",
|
|
||||||
"value" : "2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key" : "LOG_LEVEL",
|
|
||||||
"value" : "0"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"targetForVariableExpansion" : {
|
|
||||||
"containerPath" : "container:Passepartout.xcodeproj",
|
|
||||||
"identifier" : "0E57F63720C83FC5008323CF",
|
|
||||||
"name" : "Passepartout"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"testTargets" : [
|
|
||||||
{
|
|
||||||
"enabled" : false,
|
|
||||||
"target" : {
|
|
||||||
"containerPath" : "container:PassepartoutLibrary",
|
|
||||||
"identifier" : "PassepartoutCoreTests",
|
|
||||||
"name" : "PassepartoutCoreTests"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enabled" : false,
|
|
||||||
"target" : {
|
|
||||||
"containerPath" : "container:PassepartoutLibrary",
|
|
||||||
"identifier" : "PassepartoutProvidersTests",
|
|
||||||
"name" : "PassepartoutProvidersTests"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enabled" : false,
|
|
||||||
"target" : {
|
|
||||||
"containerPath" : "container:PassepartoutLibrary",
|
|
||||||
"identifier" : "PassepartoutVPNTests",
|
|
||||||
"name" : "PassepartoutVPNTests"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
|
@ -52,15 +52,15 @@ extension Constants {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum InApp {
|
enum InApp {
|
||||||
static var overriddenAppType: ProductManager.AppType? {
|
static var overriddenAppType: AppType? {
|
||||||
if let envString = ProcessInfo.processInfo.environment["APP_TYPE"],
|
if let envString = ProcessInfo.processInfo.environment["APP_TYPE"],
|
||||||
let envValue = Int(envString),
|
let envValue = Int(envString),
|
||||||
let testAppType = ProductManager.AppType(rawValue: envValue) {
|
let testAppType = AppType(rawValue: envValue) {
|
||||||
|
|
||||||
return testAppType
|
return testAppType
|
||||||
}
|
}
|
||||||
if let infoValue: Int = bundleConfig("app_type"),
|
if let infoValue: Int = bundleConfig("app_type"),
|
||||||
let testAppType = ProductManager.AppType(rawValue: infoValue) {
|
let testAppType = AppType(rawValue: infoValue) {
|
||||||
|
|
||||||
return testAppType
|
return testAppType
|
||||||
}
|
}
|
||||||
|
@ -228,9 +228,6 @@ extension Constants {
|
||||||
|
|
||||||
// @available(*, deprecated, message: "File importer stops showing again after closing with swipe down")
|
// @available(*, deprecated, message: "File importer stops showing again after closing with swipe down")
|
||||||
static let xxxPresentFileImporter = 200
|
static let xxxPresentFileImporter = 200
|
||||||
|
|
||||||
// @available(*, deprecated, message: "Edited shortcut is outdated in delegate")
|
|
||||||
static let xxxReloadEditedShortcut = 200
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Rating {
|
enum Rating {
|
||||||
|
|
|
@ -57,6 +57,8 @@ final class AppContext {
|
||||||
upgradeManager.migrate(toVersion: Constants.Global.appVersionNumber)
|
upgradeManager.migrate(toVersion: Constants.Global.appVersionNumber)
|
||||||
|
|
||||||
productManager = ProductManager(
|
productManager = ProductManager(
|
||||||
|
inApp: StoreKitInApp<LocalProduct>(),
|
||||||
|
receiptReader: StoreKitReceiptReader(),
|
||||||
overriddenAppType: Constants.InApp.overriddenAppType,
|
overriddenAppType: Constants.InApp.overriddenAppType,
|
||||||
buildProducts: Constants.InApp.buildProducts
|
buildProducts: Constants.InApp.buildProducts
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
//
|
||||||
|
// StoreKitReceiptReader.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 12/20/23.
|
||||||
|
// Copyright (c) 2023 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 Foundation
|
||||||
|
import Kvitto
|
||||||
|
import PassepartoutLibrary
|
||||||
|
|
||||||
|
struct StoreKitReceiptReader: ReceiptReader {
|
||||||
|
func receipt(for appType: AppType) -> InAppReceipt? {
|
||||||
|
guard let url = Bundle.main.appStoreReceiptURL else {
|
||||||
|
pp_log.warning("No App Store receipt found!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let receipt = Receipt(contentsOfURL: url)
|
||||||
|
|
||||||
|
let fallbackReceipt: Receipt? = {
|
||||||
|
|
||||||
|
// in TestFlight, attempt fallback to existing release receipt
|
||||||
|
if appType == .beta {
|
||||||
|
guard let receipt else {
|
||||||
|
let releaseUrl = url.deletingLastPathComponent().appendingPathComponent("receipt")
|
||||||
|
guard releaseUrl != url else {
|
||||||
|
assertionFailure("How can release URL be equal to sandbox URL in TestFlight?")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pp_log.warning("Sandbox receipt not found, falling back to Release receipt")
|
||||||
|
return Receipt(contentsOfURL: releaseUrl)
|
||||||
|
}
|
||||||
|
return receipt
|
||||||
|
}
|
||||||
|
return receipt
|
||||||
|
}()
|
||||||
|
|
||||||
|
return fallbackReceipt?.asInAppReceipt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension Receipt {
|
||||||
|
var asInAppReceipt: InAppReceipt {
|
||||||
|
var originalBuildNumber: Int?
|
||||||
|
var purchaseReceipts: [InAppReceipt.PurchaseReceipt]?
|
||||||
|
if let originalAppVersion, let buildNumber = Int(originalAppVersion) {
|
||||||
|
originalBuildNumber = buildNumber
|
||||||
|
}
|
||||||
|
if let inAppPurchaseReceipts {
|
||||||
|
purchaseReceipts = inAppPurchaseReceipts
|
||||||
|
.map {
|
||||||
|
InAppReceipt.PurchaseReceipt(productIdentifier: $0.productIdentifier,
|
||||||
|
cancellationDate: $0.cancellationDate,
|
||||||
|
originalPurchaseDate: $0.originalPurchaseDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return InAppReceipt(originalBuildNumber: originalBuildNumber,
|
||||||
|
purchaseReceipts: purchaseReceipts)
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,11 +26,11 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import PassepartoutLibrary
|
import PassepartoutLibrary
|
||||||
|
|
||||||
public final class DefaultUpgradeManagerStrategy: UpgradeManagerStrategy {
|
final class DefaultUpgradeManagerStrategy: UpgradeManagerStrategy {
|
||||||
public init() {
|
init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func migrate(store: KeyValueStore, lastVersion: String?) {
|
func migrate(store: KeyValueStore, lastVersion: String?) {
|
||||||
|
|
||||||
// legacy check before lastVersion was even stored
|
// legacy check before lastVersion was even stored
|
||||||
let isUpgradeFromBefore_2_2_0: Bool? = store.value(forLocation: UpgradeManager.StoreKey.existingKeyBefore_2_2_0)
|
let isUpgradeFromBefore_2_2_0: Bool? = store.value(forLocation: UpgradeManager.StoreKey.existingKeyBefore_2_2_0)
|
||||||
|
@ -47,6 +47,6 @@ public final class DefaultUpgradeManagerStrategy: UpgradeManagerStrategy {
|
||||||
pp_log.debug("Upgrade from \(lastVersion)")
|
pp_log.debug("Upgrade from \(lastVersion)")
|
||||||
}
|
}
|
||||||
|
|
||||||
public func migrateData() {
|
func migrateData() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ extension IntentsManager: INUIEditVoiceShortcutViewControllerDelegate {
|
||||||
//
|
//
|
||||||
// so damn it, reload manually after a delay
|
// so damn it, reload manually after a delay
|
||||||
Task {
|
Task {
|
||||||
await Task.maybeWait(forMilliseconds: Constants.Delays.xxxReloadEditedShortcut)
|
await Task.maybeWait(forMilliseconds: 200)
|
||||||
await reloadShortcuts()
|
await reloadShortcuts()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ import Foundation
|
||||||
import PassepartoutLibrary
|
import PassepartoutLibrary
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public final class UpgradeManager: ObservableObject {
|
final class UpgradeManager: ObservableObject {
|
||||||
|
|
||||||
// MARK: Initialization
|
// MARK: Initialization
|
||||||
|
|
||||||
|
@ -37,9 +37,9 @@ public final class UpgradeManager: ObservableObject {
|
||||||
|
|
||||||
// MARK: State
|
// MARK: State
|
||||||
|
|
||||||
@Published public private(set) var isDoingMigrations = true
|
@Published private(set) var isDoingMigrations = true
|
||||||
|
|
||||||
public init(
|
init(
|
||||||
store: KeyValueStore,
|
store: KeyValueStore,
|
||||||
strategy: UpgradeManagerStrategy
|
strategy: UpgradeManagerStrategy
|
||||||
) {
|
) {
|
||||||
|
@ -47,14 +47,14 @@ public final class UpgradeManager: ObservableObject {
|
||||||
self.strategy = strategy
|
self.strategy = strategy
|
||||||
}
|
}
|
||||||
|
|
||||||
public func migrate(toVersion currentVersion: String) {
|
func migrate(toVersion currentVersion: String) {
|
||||||
if let lastVersion, currentVersion > lastVersion {
|
if let lastVersion, currentVersion > lastVersion {
|
||||||
strategy.migrate(store: store, lastVersion: lastVersion)
|
strategy.migrate(store: store, lastVersion: lastVersion)
|
||||||
}
|
}
|
||||||
lastVersion = currentVersion
|
lastVersion = currentVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
public func migrateData(profileManager: ProfileManager) {
|
func migrateData(profileManager: ProfileManager) {
|
||||||
isDoingMigrations = false
|
isDoingMigrations = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import PassepartoutCore
|
import PassepartoutLibrary
|
||||||
|
|
||||||
public protocol UpgradeManagerStrategy {
|
public protocol UpgradeManagerStrategy {
|
||||||
func migrate(store: KeyValueStore, lastVersion: String?)
|
func migrate(store: KeyValueStore, lastVersion: String?)
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import PassepartoutLibrary
|
import PassepartoutLibrary
|
||||||
import StoreKit
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct DonateView: View {
|
struct DonateView: View {
|
||||||
|
@ -113,7 +112,7 @@ private extension DonateView {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func productRow(_ product: SKProduct) -> some View {
|
func productRow(_ product: InAppProduct) -> some View {
|
||||||
HStack {
|
HStack {
|
||||||
Button(product.localizedTitle) {
|
Button(product.localizedTitle) {
|
||||||
purchaseProduct(product)
|
purchaseProduct(product)
|
||||||
|
@ -132,10 +131,10 @@ private extension DonateView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension ProductManager {
|
private extension ProductManager {
|
||||||
var donations: [SKProduct] {
|
var donations: [InAppProduct] {
|
||||||
products.filter { product in
|
products.filter { product in
|
||||||
LocalProduct.allDonations.contains {
|
LocalProduct.allDonations.contains {
|
||||||
$0.matchesStoreKitProduct(product)
|
$0.matchesInAppProduct(product)
|
||||||
}
|
}
|
||||||
}.sorted {
|
}.sorted {
|
||||||
$0.price.decimalValue < $1.price.decimalValue
|
$0.price.decimalValue < $1.price.decimalValue
|
||||||
|
@ -146,7 +145,7 @@ private extension ProductManager {
|
||||||
// MARK: -
|
// MARK: -
|
||||||
|
|
||||||
private extension DonateView {
|
private extension DonateView {
|
||||||
func purchaseProduct(_ product: SKProduct) {
|
func purchaseProduct(_ product: InAppProduct) {
|
||||||
pendingDonationIdentifier = product.productIdentifier
|
pendingDonationIdentifier = product.productIdentifier
|
||||||
productManager.purchase(product, completionHandler: handlePurchaseResult)
|
productManager.purchase(product, completionHandler: handlePurchaseResult)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,12 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import PassepartoutLibrary
|
import PassepartoutLibrary
|
||||||
import StoreKit
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
extension PaywallView {
|
extension PaywallView {
|
||||||
struct PurchaseView: View {
|
struct PurchaseView: View {
|
||||||
fileprivate enum PurchaseState {
|
fileprivate enum PurchaseState {
|
||||||
case purchasing(SKProduct)
|
case purchasing(InAppProduct)
|
||||||
|
|
||||||
case restoring
|
case restoring
|
||||||
}
|
}
|
||||||
|
@ -70,7 +69,7 @@ extension PaywallView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct PurchaseRow: View {
|
private struct PurchaseRow: View {
|
||||||
var product: SKProduct?
|
var product: InAppProduct?
|
||||||
|
|
||||||
let title: String
|
let title: String
|
||||||
|
|
||||||
|
@ -93,7 +92,7 @@ private struct PurchaseRow: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private typealias RowModel = (product: SKProduct, extra: String?)
|
private typealias RowModel = (product: InAppProduct, extra: String?)
|
||||||
|
|
||||||
// MARK: -
|
// MARK: -
|
||||||
|
|
||||||
|
@ -136,14 +135,14 @@ private extension PaywallView.PurchaseView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension PaywallView.PurchaseView {
|
private extension PaywallView.PurchaseView {
|
||||||
var skFeature: SKProduct? {
|
var skFeature: InAppProduct? {
|
||||||
guard let feature = feature else {
|
guard let feature = feature else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return productManager.product(withIdentifier: feature)
|
return productManager.product(withIdentifier: feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
var skPlatformVersion: SKProduct? {
|
var skPlatformVersion: InAppProduct? {
|
||||||
#if targetEnvironment(macCatalyst)
|
#if targetEnvironment(macCatalyst)
|
||||||
productManager.product(withIdentifier: .fullVersion_macOS)
|
productManager.product(withIdentifier: .fullVersion_macOS)
|
||||||
#else
|
#else
|
||||||
|
@ -152,7 +151,7 @@ private extension PaywallView.PurchaseView {
|
||||||
}
|
}
|
||||||
|
|
||||||
// hide full version if already bought the other platform version
|
// hide full version if already bought the other platform version
|
||||||
var skFullVersion: SKProduct? {
|
var skFullVersion: InAppProduct? {
|
||||||
#if targetEnvironment(macCatalyst)
|
#if targetEnvironment(macCatalyst)
|
||||||
guard !productManager.hasPurchased(.fullVersion_iOS) else {
|
guard !productManager.hasPurchased(.fullVersion_iOS) else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -209,14 +208,14 @@ private extension PurchaseRow {
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
var actionButton: some View {
|
var actionButton: some View {
|
||||||
if let product = product {
|
if let product {
|
||||||
purchaseButton(product)
|
purchaseButton(product)
|
||||||
} else {
|
} else {
|
||||||
restoreButton
|
restoreButton
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func purchaseButton(_ product: SKProduct) -> some View {
|
func purchaseButton(_ product: InAppProduct) -> some View {
|
||||||
HStack {
|
HStack {
|
||||||
Button(title, action: action)
|
Button(title, action: action)
|
||||||
Spacer()
|
Spacer()
|
||||||
|
@ -245,7 +244,7 @@ private extension PurchaseRow {
|
||||||
// MARK: -
|
// MARK: -
|
||||||
|
|
||||||
private extension PaywallView.PurchaseView {
|
private extension PaywallView.PurchaseView {
|
||||||
func purchaseProduct(_ product: SKProduct) {
|
func purchaseProduct(_ product: InAppProduct) {
|
||||||
purchaseState = .purchasing(product)
|
purchaseState = .purchasing(product)
|
||||||
|
|
||||||
productManager.purchase(product) {
|
productManager.purchase(product) {
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import PassepartoutLibrary
|
||||||
|
|
||||||
struct PaywallView: View {
|
struct PaywallView: View {
|
||||||
@ObservedObject private var productManager: ProductManager
|
@ObservedObject private var productManager: ProductManager
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
{
|
|
||||||
"configurations" : [
|
|
||||||
{
|
|
||||||
"id" : "2A4B150B-39A2-417B-98FD-1C5C74C26E3E",
|
|
||||||
"name" : "Test Scheme Action",
|
|
||||||
"options" : {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"defaultOptions" : {
|
|
||||||
"codeCoverage" : false
|
|
||||||
},
|
|
||||||
"testTargets" : [
|
|
||||||
{
|
|
||||||
"target" : {
|
|
||||||
"containerPath" : "container:",
|
|
||||||
"identifier" : "PassepartoutCoreTests",
|
|
||||||
"name" : "PassepartoutCoreTests"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"target" : {
|
|
||||||
"containerPath" : "container:",
|
|
||||||
"identifier" : "PassepartoutProvidersTests",
|
|
||||||
"name" : "PassepartoutProvidersTests"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"target" : {
|
|
||||||
"containerPath" : "container:",
|
|
||||||
"identifier" : "PassepartoutServicesTests",
|
|
||||||
"name" : "PassepartoutServicesTests"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"target" : {
|
|
||||||
"containerPath" : "container:",
|
|
||||||
"identifier" : "PassepartoutVPNTests",
|
|
||||||
"name" : "PassepartoutVPNTests"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1430"
|
|
||||||
version = "1.7">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
shouldAutocreateTestPlan = "YES">
|
|
||||||
<Testables>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutCoreTests"
|
|
||||||
BuildableName = "PassepartoutCoreTests"
|
|
||||||
BlueprintName = "PassepartoutCoreTests"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
|
@ -1,39 +1,11 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1430"
|
LastUpgradeVersion = "1510"
|
||||||
version = "1.7">
|
version = "1.7">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
buildImplicitDependencies = "YES">
|
buildImplicitDependencies = "YES">
|
||||||
<BuildActionEntries>
|
<BuildActionEntries>
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutLibrary_PassepartoutVPNImpl"
|
|
||||||
BuildableName = "PassepartoutLibrary_PassepartoutVPNImpl"
|
|
||||||
BlueprintName = "PassepartoutLibrary_PassepartoutVPNImpl"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutLibrary_PassepartoutCoreTests"
|
|
||||||
BuildableName = "PassepartoutLibrary_PassepartoutCoreTests"
|
|
||||||
BlueprintName = "PassepartoutLibrary_PassepartoutCoreTests"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
<BuildActionEntry
|
||||||
buildForTesting = "YES"
|
buildForTesting = "YES"
|
||||||
buildForRunning = "YES"
|
buildForRunning = "YES"
|
||||||
|
@ -56,9 +28,9 @@
|
||||||
buildForAnalyzing = "YES">
|
buildForAnalyzing = "YES">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "PassepartoutVPN"
|
BlueprintIdentifier = "PassepartoutLibrary"
|
||||||
BuildableName = "PassepartoutVPN"
|
BuildableName = "PassepartoutLibrary"
|
||||||
BlueprintName = "PassepartoutVPN"
|
BlueprintName = "PassepartoutLibrary"
|
||||||
ReferencedContainer = "container:">
|
ReferencedContainer = "container:">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
|
@ -76,62 +48,6 @@
|
||||||
ReferencedContainer = "container:">
|
ReferencedContainer = "container:">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutCore"
|
|
||||||
BuildableName = "PassepartoutCore"
|
|
||||||
BlueprintName = "PassepartoutCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutProviders"
|
|
||||||
BuildableName = "PassepartoutProviders"
|
|
||||||
BlueprintName = "PassepartoutProviders"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutServices"
|
|
||||||
BuildableName = "PassepartoutServices"
|
|
||||||
BlueprintName = "PassepartoutServices"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutVPNImpl"
|
|
||||||
BuildableName = "PassepartoutVPNImpl"
|
|
||||||
BlueprintName = "PassepartoutVPNImpl"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
<BuildActionEntry
|
||||||
buildForTesting = "YES"
|
buildForTesting = "YES"
|
||||||
buildForRunning = "YES"
|
buildForRunning = "YES"
|
||||||
|
@ -146,6 +62,20 @@
|
||||||
ReferencedContainer = "container:">
|
ReferencedContainer = "container:">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "NO"
|
||||||
|
buildForArchiving = "NO"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "PassepartoutFrontendTests"
|
||||||
|
BuildableName = "PassepartoutFrontendTests"
|
||||||
|
BlueprintName = "PassepartoutFrontendTests"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
<BuildActionEntry
|
<BuildActionEntry
|
||||||
buildForTesting = "YES"
|
buildForTesting = "YES"
|
||||||
buildForRunning = "YES"
|
buildForRunning = "YES"
|
||||||
|
@ -160,90 +90,6 @@
|
||||||
ReferencedContainer = "container:">
|
ReferencedContainer = "container:">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "NO"
|
|
||||||
buildForArchiving = "NO"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutServicesTests"
|
|
||||||
BuildableName = "PassepartoutServicesTests"
|
|
||||||
BlueprintName = "PassepartoutServicesTests"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "NO"
|
|
||||||
buildForArchiving = "NO"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutVPNTests"
|
|
||||||
BuildableName = "PassepartoutVPNTests"
|
|
||||||
BlueprintName = "PassepartoutVPNTests"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutServicesImpl"
|
|
||||||
BuildableName = "PassepartoutServicesImpl"
|
|
||||||
BlueprintName = "PassepartoutServicesImpl"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutProvidersImpl"
|
|
||||||
BuildableName = "PassepartoutProvidersImpl"
|
|
||||||
BlueprintName = "PassepartoutProvidersImpl"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutLibrary_PassepartoutProvidersImpl"
|
|
||||||
BuildableName = "PassepartoutLibrary_PassepartoutProvidersImpl"
|
|
||||||
BlueprintName = "PassepartoutLibrary_PassepartoutProvidersImpl"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutLibrary"
|
|
||||||
BuildableName = "PassepartoutLibrary"
|
|
||||||
BlueprintName = "PassepartoutLibrary"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
</BuildActionEntries>
|
||||||
</BuildAction>
|
</BuildAction>
|
||||||
<TestAction
|
<TestAction
|
||||||
|
@ -263,6 +109,16 @@
|
||||||
ReferencedContainer = "container:">
|
ReferencedContainer = "container:">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</TestableReference>
|
</TestableReference>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "PassepartoutFrontendTests"
|
||||||
|
BuildableName = "PassepartoutFrontendTests"
|
||||||
|
BlueprintName = "PassepartoutFrontendTests"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
skipped = "NO">
|
skipped = "NO">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
|
@ -283,16 +139,6 @@
|
||||||
ReferencedContainer = "container:">
|
ReferencedContainer = "container:">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</TestableReference>
|
</TestableReference>
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutVPNTests"
|
|
||||||
BuildableName = "PassepartoutVPNTests"
|
|
||||||
BlueprintName = "PassepartoutVPNTests"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
</Testables>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
|
@ -315,9 +161,9 @@
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "PassepartoutLibrary_PassepartoutVPNImpl"
|
BlueprintIdentifier = "OpenVPNAppExtension"
|
||||||
BuildableName = "PassepartoutLibrary_PassepartoutVPNImpl"
|
BuildableName = "OpenVPNAppExtension"
|
||||||
BlueprintName = "PassepartoutLibrary_PassepartoutVPNImpl"
|
BlueprintName = "OpenVPNAppExtension"
|
||||||
ReferencedContainer = "container:">
|
ReferencedContainer = "container:">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1430"
|
LastUpgradeVersion = "1510"
|
||||||
version = "1.7">
|
version = "1.7">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -26,13 +26,8 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
<TestPlans>
|
shouldAutocreateTestPlan = "YES">
|
||||||
<TestPlanReference
|
|
||||||
reference = "container:.swiftpm/PassepartoutLibrary.xctestplan"
|
|
||||||
default = "YES">
|
|
||||||
</TestPlanReference>
|
|
||||||
</TestPlans>
|
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1430"
|
|
||||||
version = "1.7">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
shouldAutocreateTestPlan = "YES">
|
|
||||||
<Testables>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutProvidersTests"
|
|
||||||
BuildableName = "PassepartoutProvidersTests"
|
|
||||||
BlueprintName = "PassepartoutProvidersTests"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
|
@ -1,53 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1430"
|
|
||||||
version = "1.7">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
shouldAutocreateTestPlan = "YES">
|
|
||||||
<Testables>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "PassepartoutVPNTests"
|
|
||||||
BuildableName = "PassepartoutVPNTests"
|
|
||||||
BlueprintName = "PassepartoutVPNTests"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
|
@ -13,6 +13,14 @@ let package = Package(
|
||||||
.library(
|
.library(
|
||||||
name: "PassepartoutLibrary",
|
name: "PassepartoutLibrary",
|
||||||
targets: ["PassepartoutLibrary"]),
|
targets: ["PassepartoutLibrary"]),
|
||||||
|
.library(
|
||||||
|
name: "PassepartoutInterfaces",
|
||||||
|
targets: [
|
||||||
|
"PassepartoutCore",
|
||||||
|
"PassepartoutFrontend",
|
||||||
|
"PassepartoutProviders",
|
||||||
|
"PassepartoutServices"
|
||||||
|
]),
|
||||||
.library(
|
.library(
|
||||||
name: "OpenVPNAppExtension",
|
name: "OpenVPNAppExtension",
|
||||||
targets: ["OpenVPNAppExtension"]),
|
targets: ["OpenVPNAppExtension"]),
|
||||||
|
@ -72,6 +80,11 @@ let package = Package(
|
||||||
.product(name: "TunnelKitOpenVPN", package: "TunnelKit"),
|
.product(name: "TunnelKitOpenVPN", package: "TunnelKit"),
|
||||||
.product(name: "TunnelKitWireGuard", package: "TunnelKit"),
|
.product(name: "TunnelKitWireGuard", package: "TunnelKit"),
|
||||||
]),
|
]),
|
||||||
|
.target(
|
||||||
|
name: "PassepartoutFrontend",
|
||||||
|
dependencies: [
|
||||||
|
"PassepartoutProviders"
|
||||||
|
]),
|
||||||
.target(
|
.target(
|
||||||
name: "PassepartoutProviders",
|
name: "PassepartoutProviders",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
@ -104,17 +117,20 @@ let package = Package(
|
||||||
|
|
||||||
// MARK: Tests
|
// MARK: Tests
|
||||||
|
|
||||||
.testTarget(
|
|
||||||
name: "PassepartoutVPNTests",
|
|
||||||
dependencies: ["PassepartoutVPNImpl"]),
|
|
||||||
.testTarget(
|
|
||||||
name: "PassepartoutProvidersTests",
|
|
||||||
dependencies: ["PassepartoutProvidersImpl"]),
|
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "PassepartoutCoreTests",
|
name: "PassepartoutCoreTests",
|
||||||
dependencies: ["PassepartoutCore"],
|
dependencies: ["PassepartoutCore"],
|
||||||
resources: [
|
resources: [
|
||||||
.process("Resources")
|
.process("Resources")
|
||||||
])
|
]),
|
||||||
|
.testTarget(
|
||||||
|
name: "PassepartoutFrontendTests",
|
||||||
|
dependencies: ["PassepartoutFrontend"]),
|
||||||
|
.testTarget(
|
||||||
|
name: "PassepartoutProvidersTests",
|
||||||
|
dependencies: ["PassepartoutProviders"]),
|
||||||
|
.testTarget(
|
||||||
|
name: "PassepartoutServicesTests",
|
||||||
|
dependencies: ["PassepartoutServices"])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,9 +24,8 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import StoreKit
|
|
||||||
|
|
||||||
public enum InAppPurchaseResult {
|
public enum InAppPurchaseResult: Sendable {
|
||||||
case done
|
case done
|
||||||
|
|
||||||
case cancelled
|
case cancelled
|
||||||
|
@ -36,188 +35,57 @@ public enum InAppError: Error {
|
||||||
case unknown
|
case unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class InApp<PID: Hashable & RawRepresentable>: NSObject,
|
public struct InAppProduct: Sendable {
|
||||||
SKProductsRequestDelegate, SKPaymentTransactionObserver
|
public let productIdentifier: String
|
||||||
where PID.RawValue == String {
|
|
||||||
|
|
||||||
public typealias ProductObserver = ([PID: SKProduct]) -> Void
|
public let localizedTitle: String
|
||||||
|
|
||||||
public typealias TransactionObserver = (Result<InAppPurchaseResult, Error>) -> Void
|
public let localizedDescription: String
|
||||||
|
|
||||||
public typealias RestoreObserver = (Bool, PID?, Error?) -> Void
|
public let price: NSDecimalNumber
|
||||||
|
|
||||||
public typealias FailureObserver = (Error) -> Void
|
public let localizedPrice: String?
|
||||||
|
|
||||||
private var productsMap: [PID: SKProduct]
|
|
||||||
|
|
||||||
public var products: [SKProduct] {
|
|
||||||
[SKProduct](productsMap.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var productObservers: [ProductObserver]
|
|
||||||
|
|
||||||
private var productFailureObserver: FailureObserver?
|
|
||||||
|
|
||||||
private var transactionObservers: [String: TransactionObserver]
|
|
||||||
|
|
||||||
private var restoreObservers: [RestoreObserver]
|
|
||||||
|
|
||||||
public override init() {
|
|
||||||
productsMap = [:]
|
|
||||||
productObservers = []
|
|
||||||
productFailureObserver = nil
|
|
||||||
transactionObservers = [:]
|
|
||||||
restoreObservers = []
|
|
||||||
super.init()
|
|
||||||
|
|
||||||
SKPaymentQueue.default().add(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
SKPaymentQueue.default().remove(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func requestProducts(withIdentifiers identifiers: [PID], completionHandler: ProductObserver?, failureHandler: FailureObserver?) {
|
|
||||||
let req = SKProductsRequest(productIdentifiers: Set(identifiers.map { $0.rawValue }))
|
|
||||||
req.delegate = self
|
|
||||||
if let observer = completionHandler {
|
|
||||||
productObservers.append(observer)
|
|
||||||
}
|
|
||||||
productFailureObserver = failureHandler
|
|
||||||
req.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
public func purchase(productWithIdentifier productIdentifier: PID, completionHandler: @escaping TransactionObserver) -> Bool {
|
|
||||||
guard let product = productsMap[productIdentifier] else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
purchase(product: product, completionHandler: completionHandler)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
public func purchase(product: SKProduct, completionHandler: @escaping TransactionObserver) {
|
|
||||||
let queue = SKPaymentQueue.default()
|
|
||||||
let payment = SKPayment(product: product)
|
|
||||||
transactionObservers[product.productIdentifier] = completionHandler
|
|
||||||
queue.add(payment)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func restorePurchases(completionHandler: @escaping RestoreObserver) {
|
|
||||||
let queue = SKPaymentQueue.default()
|
|
||||||
restoreObservers.append(completionHandler)
|
|
||||||
queue.restoreCompletedTransactions()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func product(withIdentifier productIdentifier: PID) -> SKProduct? {
|
|
||||||
productsMap[productIdentifier]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: SKProductsRequestDelegate
|
|
||||||
|
|
||||||
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.receiveProducts(response.products)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: SKRequest, didFailWithError error: Error) {
|
|
||||||
if request as? SKProductsRequest != nil {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.productFailureObserver?(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.transactionObservers.removeAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func receiveProducts(_ products: [SKProduct]) {
|
|
||||||
productsMap.removeAll()
|
|
||||||
for p in products {
|
|
||||||
guard let pid = PID(rawValue: p.productIdentifier) else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
productsMap[pid] = p
|
|
||||||
}
|
|
||||||
productObservers.forEach { $0(productsMap) }
|
|
||||||
productObservers.removeAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: SKPaymentTransactionObserver
|
|
||||||
|
|
||||||
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
let currentRestoreObservers = self.restoreObservers
|
|
||||||
|
|
||||||
for tx in transactions {
|
|
||||||
let productIdentifier = tx.payment.productIdentifier
|
|
||||||
let observer = self.transactionObservers[productIdentifier]
|
|
||||||
|
|
||||||
switch tx.transactionState {
|
|
||||||
case .purchased:
|
|
||||||
queue.finishTransaction(tx)
|
|
||||||
observer?(.success(.done))
|
|
||||||
|
|
||||||
case .restored:
|
|
||||||
queue.finishTransaction(tx)
|
|
||||||
observer?(.success(.done))
|
|
||||||
for r in currentRestoreObservers {
|
|
||||||
guard let pid = PID(rawValue: productIdentifier) else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r(false, pid, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
case .failed:
|
|
||||||
queue.finishTransaction(tx)
|
|
||||||
if let skError = tx.error as? SKError, skError.code == .paymentCancelled {
|
|
||||||
observer?(.success(.cancelled))
|
|
||||||
} else {
|
|
||||||
observer?(.failure(tx.error ?? InAppError.unknown))
|
|
||||||
for r in currentRestoreObservers {
|
|
||||||
guard let pid = PID(rawValue: productIdentifier) else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r(false, pid, tx.error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
for r in self.restoreObservers {
|
|
||||||
r(true, nil, nil)
|
|
||||||
}
|
|
||||||
self.restoreObservers.removeAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
for r in self.restoreObservers {
|
|
||||||
r(true, nil, error)
|
|
||||||
}
|
|
||||||
self.restoreObservers.removeAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SKProduct {
|
public protocol InAppProtocol {
|
||||||
private var localizedCurrencyFormatter: NumberFormatter {
|
associatedtype ProductIdentifier: Hashable
|
||||||
let fmt = NumberFormatter()
|
|
||||||
fmt.locale = priceLocale
|
func canMakePurchases() -> Bool
|
||||||
fmt.numberStyle = .currency
|
|
||||||
return fmt
|
func requestProducts(withIdentifiers identifiers: [ProductIdentifier]) async throws -> [ProductIdentifier: InAppProduct]
|
||||||
|
|
||||||
|
func purchase(productWithIdentifier productIdentifier: ProductIdentifier) async throws -> InAppPurchaseResult
|
||||||
|
|
||||||
|
func restorePurchases() async throws
|
||||||
|
|
||||||
|
func products() -> [InAppProduct]
|
||||||
|
|
||||||
|
func product(withIdentifier productIdentifier: ProductIdentifier) -> InAppProduct?
|
||||||
|
|
||||||
|
func setTransactionsObserver(_ block: @escaping () -> Void)
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct InAppReceipt: Sendable {
|
||||||
|
public struct PurchaseReceipt: Sendable {
|
||||||
|
public let productIdentifier: String?
|
||||||
|
|
||||||
|
public let cancellationDate: Date?
|
||||||
|
|
||||||
|
public let originalPurchaseDate: Date?
|
||||||
|
|
||||||
|
public init(productIdentifier: String?, cancellationDate: Date?, originalPurchaseDate: Date?) {
|
||||||
|
self.productIdentifier = productIdentifier
|
||||||
|
self.cancellationDate = cancellationDate
|
||||||
|
self.originalPurchaseDate = originalPurchaseDate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var localizedPrice: String? {
|
public let originalBuildNumber: Int?
|
||||||
localizedCurrencyFormatter.string(from: price)
|
|
||||||
|
public let purchaseReceipts: [PurchaseReceipt]?
|
||||||
|
|
||||||
|
public init(originalBuildNumber: Int?, purchaseReceipts: [PurchaseReceipt]?) {
|
||||||
|
self.originalBuildNumber = originalBuildNumber
|
||||||
|
self.purchaseReceipts = purchaseReceipts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,297 @@
|
||||||
|
//
|
||||||
|
// StoreKitInApp.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 12/19/23.
|
||||||
|
// Copyright (c) 2023 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 Foundation
|
||||||
|
import StoreKit
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
public final class StoreKitInApp<PID>: InAppProtocol where PID: Hashable & RawRepresentable,
|
||||||
|
PID.RawValue == String {
|
||||||
|
public typealias ProductIdentifier = PID
|
||||||
|
|
||||||
|
private let impl: StoreKitInAppImpl<PID>
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
impl = StoreKitInAppImpl()
|
||||||
|
}
|
||||||
|
|
||||||
|
public nonisolated func canMakePurchases() -> Bool {
|
||||||
|
SKPaymentQueue.canMakePayments()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func requestProducts(withIdentifiers identifiers: [PID]) async throws -> [PID: InAppProduct] {
|
||||||
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
|
impl.requestProducts(withIdentifiers: identifiers) { products in
|
||||||
|
continuation.resume(returning: products.reduce(into: [:]) { map, pidProduct in
|
||||||
|
map[pidProduct.key] = pidProduct.value.asInAppProduct
|
||||||
|
})
|
||||||
|
} failureHandler: { error in
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func purchase(productWithIdentifier productIdentifier: PID) async throws -> InAppPurchaseResult {
|
||||||
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
|
impl.purchase(productWithIdentifier: productIdentifier) { result in
|
||||||
|
do {
|
||||||
|
let value = try result.get()
|
||||||
|
continuation.resume(returning: value)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func restorePurchases() async throws {
|
||||||
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
|
impl.restorePurchases { finished, _, _ in
|
||||||
|
if finished {
|
||||||
|
continuation.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public nonisolated func products() -> [InAppProduct] {
|
||||||
|
impl.products.map(\.asInAppProduct)
|
||||||
|
}
|
||||||
|
|
||||||
|
public nonisolated func product(withIdentifier productIdentifier: PID) -> InAppProduct? {
|
||||||
|
guard let skProduct = impl.product(withIdentifier: productIdentifier) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return skProduct.asInAppProduct
|
||||||
|
}
|
||||||
|
|
||||||
|
public nonisolated func setTransactionsObserver(_ block: @escaping () -> Void) {
|
||||||
|
impl.onTransactionsUpdated = block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class StoreKitInAppImpl<PID: Hashable & RawRepresentable>: NSObject,
|
||||||
|
SKProductsRequestDelegate, SKPaymentTransactionObserver
|
||||||
|
where PID.RawValue == String {
|
||||||
|
|
||||||
|
typealias ProductObserver = ([PID: SKProduct]) -> Void
|
||||||
|
|
||||||
|
typealias TransactionObserver = (Result<InAppPurchaseResult, Error>) -> Void
|
||||||
|
|
||||||
|
typealias RestoreObserver = (Bool, PID?, Error?) -> Void
|
||||||
|
|
||||||
|
typealias FailureObserver = (Error) -> Void
|
||||||
|
|
||||||
|
private var productsMap: [PID: SKProduct]
|
||||||
|
|
||||||
|
var products: [SKProduct] {
|
||||||
|
Array(productsMap.values)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var productObservers: [ProductObserver]
|
||||||
|
|
||||||
|
private var productFailureObserver: FailureObserver?
|
||||||
|
|
||||||
|
private var transactionObservers: [String: TransactionObserver]
|
||||||
|
|
||||||
|
private var restoreObservers: [RestoreObserver]
|
||||||
|
|
||||||
|
var onTransactionsUpdated: (() -> Void)?
|
||||||
|
|
||||||
|
override init() {
|
||||||
|
productsMap = [:]
|
||||||
|
productObservers = []
|
||||||
|
productFailureObserver = nil
|
||||||
|
transactionObservers = [:]
|
||||||
|
restoreObservers = []
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
SKPaymentQueue.default().add(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
SKPaymentQueue.default().remove(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestProducts(withIdentifiers identifiers: [PID], completionHandler: ProductObserver?, failureHandler: FailureObserver?) {
|
||||||
|
let req = SKProductsRequest(productIdentifiers: Set(identifiers.map { $0.rawValue }))
|
||||||
|
req.delegate = self
|
||||||
|
if let observer = completionHandler {
|
||||||
|
productObservers.append(observer)
|
||||||
|
}
|
||||||
|
productFailureObserver = failureHandler
|
||||||
|
req.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func purchase(productWithIdentifier productIdentifier: PID, completionHandler: @escaping TransactionObserver) -> Bool {
|
||||||
|
guard let product = productsMap[productIdentifier] else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
purchase(product: product, completionHandler: completionHandler)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func purchase(product: SKProduct, completionHandler: @escaping TransactionObserver) {
|
||||||
|
let queue = SKPaymentQueue.default()
|
||||||
|
let payment = SKPayment(product: product)
|
||||||
|
transactionObservers[product.productIdentifier] = completionHandler
|
||||||
|
queue.add(payment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func restorePurchases(completionHandler: @escaping RestoreObserver) {
|
||||||
|
let queue = SKPaymentQueue.default()
|
||||||
|
restoreObservers.append(completionHandler)
|
||||||
|
queue.restoreCompletedTransactions()
|
||||||
|
}
|
||||||
|
|
||||||
|
func product(withIdentifier productIdentifier: PID) -> SKProduct? {
|
||||||
|
productsMap[productIdentifier]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: SKProductsRequestDelegate
|
||||||
|
|
||||||
|
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.receiveProducts(response.products)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func request(_ request: SKRequest, didFailWithError error: Error) {
|
||||||
|
if request as? SKProductsRequest != nil {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.productFailureObserver?(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.transactionObservers.removeAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func receiveProducts(_ products: [SKProduct]) {
|
||||||
|
productsMap.removeAll()
|
||||||
|
for p in products {
|
||||||
|
guard let pid = PID(rawValue: p.productIdentifier) else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
productsMap[pid] = p
|
||||||
|
}
|
||||||
|
productObservers.forEach { $0(productsMap) }
|
||||||
|
productObservers.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: SKPaymentTransactionObserver
|
||||||
|
|
||||||
|
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentRestoreObservers = self.restoreObservers
|
||||||
|
|
||||||
|
for tx in transactions {
|
||||||
|
let productIdentifier = tx.payment.productIdentifier
|
||||||
|
let observer = self.transactionObservers[productIdentifier]
|
||||||
|
|
||||||
|
switch tx.transactionState {
|
||||||
|
case .purchased:
|
||||||
|
queue.finishTransaction(tx)
|
||||||
|
observer?(.success(.done))
|
||||||
|
|
||||||
|
case .restored:
|
||||||
|
queue.finishTransaction(tx)
|
||||||
|
observer?(.success(.done))
|
||||||
|
for r in currentRestoreObservers {
|
||||||
|
guard let pid = PID(rawValue: productIdentifier) else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r(false, pid, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .failed:
|
||||||
|
queue.finishTransaction(tx)
|
||||||
|
if let skError = tx.error as? SKError, skError.code == .paymentCancelled {
|
||||||
|
observer?(.success(.cancelled))
|
||||||
|
} else {
|
||||||
|
observer?(.failure(tx.error ?? InAppError.unknown))
|
||||||
|
for r in currentRestoreObservers {
|
||||||
|
guard let pid = PID(rawValue: productIdentifier) else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r(false, pid, tx.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onTransactionsUpdated?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
for r in self.restoreObservers {
|
||||||
|
r(true, nil, nil)
|
||||||
|
}
|
||||||
|
self.restoreObservers.removeAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
for r in self.restoreObservers {
|
||||||
|
r(true, nil, error)
|
||||||
|
}
|
||||||
|
self.restoreObservers.removeAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SKProduct {
|
||||||
|
public var asInAppProduct: InAppProduct {
|
||||||
|
InAppProduct(productIdentifier: productIdentifier,
|
||||||
|
localizedTitle: localizedTitle,
|
||||||
|
localizedDescription: localizedDescription,
|
||||||
|
price: price,
|
||||||
|
localizedPrice: localizedPrice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension SKProduct {
|
||||||
|
var localizedCurrencyFormatter: NumberFormatter {
|
||||||
|
let fmt = NumberFormatter()
|
||||||
|
fmt.locale = priceLocale
|
||||||
|
fmt.numberStyle = .currency
|
||||||
|
return fmt
|
||||||
|
}
|
||||||
|
|
||||||
|
var localizedPrice: String? {
|
||||||
|
localizedCurrencyFormatter.string(from: price)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
//
|
//
|
||||||
// ProfilesTests.swift
|
// AppType.swift
|
||||||
// Passepartout
|
// Passepartout
|
||||||
//
|
//
|
||||||
// Created by Davide De Rosa on 4/7/22.
|
// Created by Davide De Rosa on 12/19/23.
|
||||||
// Copyright (c) 2023 Davide De Rosa. All rights reserved.
|
// Copyright (c) 2023 Davide De Rosa. All rights reserved.
|
||||||
//
|
//
|
||||||
// https://github.com/passepartoutvpn
|
// https://github.com/passepartoutvpn
|
||||||
|
@ -23,13 +23,24 @@
|
||||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
//
|
//
|
||||||
|
|
||||||
@testable import PassepartoutVPN
|
import Foundation
|
||||||
import XCTest
|
|
||||||
|
|
||||||
final class ProfilesTests: XCTestCase {
|
public enum AppType: Int {
|
||||||
override func setUp() {
|
case undefined = -1
|
||||||
}
|
|
||||||
|
|
||||||
override func tearDown() {
|
case freemium = 0
|
||||||
|
|
||||||
|
case beta = 1
|
||||||
|
|
||||||
|
case fullVersion = 2
|
||||||
|
|
||||||
|
public var isRestricted: Bool {
|
||||||
|
switch self {
|
||||||
|
case .undefined, .beta:
|
||||||
|
return true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,18 +25,18 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct BuildProducts {
|
public struct BuildProducts {
|
||||||
private let productsAtBuild: (Int) -> [LocalProduct]
|
private let productsAtBuild: (Int) -> [LocalProduct]
|
||||||
|
|
||||||
init(productsAtBuild: @escaping (Int) -> [LocalProduct]) {
|
public init(productsAtBuild: @escaping (Int) -> [LocalProduct]) {
|
||||||
self.productsAtBuild = productsAtBuild
|
self.productsAtBuild = productsAtBuild
|
||||||
}
|
}
|
||||||
|
|
||||||
func products(atBuild build: Int) -> [LocalProduct] {
|
public func products(atBuild build: Int) -> [LocalProduct] {
|
||||||
productsAtBuild(build)
|
productsAtBuild(build)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasProduct(_ product: LocalProduct, atBuild build: Int) -> Bool {
|
public func hasProduct(_ product: LocalProduct, atBuild build: Int) -> Bool {
|
||||||
productsAtBuild(build).contains(product)
|
productsAtBuild(build).contains(product)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -24,10 +24,10 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import PassepartoutLibrary
|
import PassepartoutCore
|
||||||
import StoreKit
|
import PassepartoutProviders
|
||||||
|
|
||||||
struct LocalProduct: RawRepresentable, Equatable, Hashable {
|
public struct LocalProduct: RawRepresentable, Hashable, Sendable {
|
||||||
private static let bundleSubdomain = "ios"
|
private static let bundleSubdomain = "ios"
|
||||||
|
|
||||||
private static let bundle = "com.algoritmico.\(bundleSubdomain).Passepartout"
|
private static let bundle = "com.algoritmico.\(bundleSubdomain).Passepartout"
|
||||||
|
@ -40,19 +40,19 @@ struct LocalProduct: RawRepresentable, Equatable, Hashable {
|
||||||
|
|
||||||
// MARK: Donations
|
// MARK: Donations
|
||||||
|
|
||||||
static let tinyDonation = LocalProduct(donationDescription: "Tiny")
|
public static let tinyDonation = LocalProduct(donationDescription: "Tiny")
|
||||||
|
|
||||||
static let smallDonation = LocalProduct(donationDescription: "Small")
|
public static let smallDonation = LocalProduct(donationDescription: "Small")
|
||||||
|
|
||||||
static let mediumDonation = LocalProduct(donationDescription: "Medium")
|
public static let mediumDonation = LocalProduct(donationDescription: "Medium")
|
||||||
|
|
||||||
static let bigDonation = LocalProduct(donationDescription: "Big")
|
public static let bigDonation = LocalProduct(donationDescription: "Big")
|
||||||
|
|
||||||
static let hugeDonation = LocalProduct(donationDescription: "Huge")
|
public static let hugeDonation = LocalProduct(donationDescription: "Huge")
|
||||||
|
|
||||||
static let maxiDonation = LocalProduct(donationDescription: "Maxi")
|
public static let maxiDonation = LocalProduct(donationDescription: "Maxi")
|
||||||
|
|
||||||
static let allDonations: [LocalProduct] = [
|
public static let allDonations: [LocalProduct] = [
|
||||||
.tinyDonation,
|
.tinyDonation,
|
||||||
.smallDonation,
|
.smallDonation,
|
||||||
.mediumDonation,
|
.mediumDonation,
|
||||||
|
@ -67,19 +67,19 @@ struct LocalProduct: RawRepresentable, Equatable, Hashable {
|
||||||
|
|
||||||
// MARK: Features
|
// MARK: Features
|
||||||
|
|
||||||
static let allProviders = LocalProduct(featureId: "all_providers")
|
public static let allProviders = LocalProduct(featureId: "all_providers")
|
||||||
|
|
||||||
static let networkSettings = LocalProduct(featureId: "network_settings")
|
public static let networkSettings = LocalProduct(featureId: "network_settings")
|
||||||
|
|
||||||
static let trustedNetworks = LocalProduct(featureId: "trusted_networks")
|
public static let trustedNetworks = LocalProduct(featureId: "trusted_networks")
|
||||||
|
|
||||||
static let siriShortcuts = LocalProduct(featureId: "siri")
|
public static let siriShortcuts = LocalProduct(featureId: "siri")
|
||||||
|
|
||||||
static let fullVersion_iOS = LocalProduct(featureId: "full_version")
|
public static let fullVersion_iOS = LocalProduct(featureId: "full_version")
|
||||||
|
|
||||||
static let fullVersion_macOS = LocalProduct(featureId: "full_mac_version")
|
public static let fullVersion_macOS = LocalProduct(featureId: "full_mac_version")
|
||||||
|
|
||||||
static let fullVersion = LocalProduct(featureId: "full_multi_version")
|
public static let fullVersion = LocalProduct(featureId: "full_multi_version")
|
||||||
|
|
||||||
static let allFeatures: [LocalProduct] = [
|
static let allFeatures: [LocalProduct] = [
|
||||||
.allProviders,
|
.allProviders,
|
||||||
|
@ -101,35 +101,45 @@ struct LocalProduct: RawRepresentable, Equatable, Hashable {
|
||||||
allDonations + allFeatures// + allProviders
|
allDonations + allFeatures// + allProviders
|
||||||
}
|
}
|
||||||
|
|
||||||
var isDonation: Bool {
|
public var isDonation: Bool {
|
||||||
rawValue.hasPrefix(LocalProduct.donationsBundle)
|
rawValue.hasPrefix(LocalProduct.donationsBundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
var isFeature: Bool {
|
public var isFeature: Bool {
|
||||||
rawValue.hasPrefix(LocalProduct.featuresBundle)
|
rawValue.hasPrefix(LocalProduct.featuresBundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
var isProvider: Bool {
|
public var isProvider: Bool {
|
||||||
rawValue.hasPrefix(LocalProduct.providersBundle)
|
rawValue.hasPrefix(LocalProduct.providersBundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var isPlatformVersion: Bool {
|
||||||
|
switch self {
|
||||||
|
case .fullVersion_iOS, .fullVersion_macOS:
|
||||||
|
return true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: RawRepresentable
|
// MARK: RawRepresentable
|
||||||
|
|
||||||
let rawValue: String
|
public let rawValue: String
|
||||||
|
|
||||||
init?(rawValue: String) {
|
public init?(rawValue: String) {
|
||||||
self.rawValue = rawValue
|
self.rawValue = rawValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LocalProduct {
|
extension LocalProduct {
|
||||||
func matchesStoreKitProduct(_ skProduct: SKProduct) -> Bool {
|
public func matchesInAppProduct(_ iaProduct: InAppProduct) -> Bool {
|
||||||
skProduct.productIdentifier == rawValue
|
iaProduct.productIdentifier == rawValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ProviderName {
|
extension ProviderName {
|
||||||
var product: LocalProduct {
|
public var product: LocalProduct {
|
||||||
.init(rawValue: "\(LocalProduct.providersBundle).\(inApp)")!
|
.init(rawValue: "\(LocalProduct.providersBundle).\(inApp)")!
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
//
|
//
|
||||||
// PassepartoutTestsApp.swift
|
// ReceiptReader.swift
|
||||||
// Passepartout
|
// Passepartout
|
||||||
//
|
//
|
||||||
// Created by Davide De Rosa on 10/30/22.
|
// Created by Davide De Rosa on 12/19/23.
|
||||||
// Copyright (c) 2023 Davide De Rosa. All rights reserved.
|
// Copyright (c) 2023 Davide De Rosa. All rights reserved.
|
||||||
//
|
//
|
||||||
// https://github.com/passepartoutvpn
|
// https://github.com/passepartoutvpn
|
||||||
|
@ -23,13 +23,9 @@
|
||||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import Foundation
|
||||||
|
import PassepartoutCore
|
||||||
|
|
||||||
@main
|
public protocol ReceiptReader {
|
||||||
struct PassepartoutTestsApp: App {
|
func receipt(for appType: AppType) -> InAppReceipt?
|
||||||
var body: some Scene {
|
|
||||||
WindowGroup {
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -25,48 +25,35 @@
|
||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
import Kvitto
|
import PassepartoutCore
|
||||||
import PassepartoutLibrary
|
import PassepartoutProviders
|
||||||
import StoreKit
|
|
||||||
|
public protocol LocalInApp: InAppProtocol where ProductIdentifier == LocalProduct {
|
||||||
|
}
|
||||||
|
|
||||||
|
extension StoreKitInApp: LocalInApp where ProductIdentifier == LocalProduct {
|
||||||
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
final class ProductManager: NSObject, ObservableObject {
|
public final class ProductManager: NSObject, ObservableObject {
|
||||||
enum AppType: Int {
|
private let inApp: any LocalInApp
|
||||||
case undefined = -1
|
|
||||||
|
|
||||||
case freemium = 0
|
private let receiptReader: ReceiptReader
|
||||||
|
|
||||||
case beta = 1
|
|
||||||
|
|
||||||
case fullVersion = 2
|
|
||||||
|
|
||||||
var isRestricted: Bool {
|
|
||||||
switch self {
|
|
||||||
case .undefined, .beta:
|
|
||||||
return true
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private let overriddenAppType: AppType?
|
private let overriddenAppType: AppType?
|
||||||
|
|
||||||
let buildProducts: BuildProducts
|
public let buildProducts: BuildProducts
|
||||||
|
|
||||||
let didRefundProducts = PassthroughSubject<Void, Never>()
|
public let didRefundProducts = PassthroughSubject<Void, Never>()
|
||||||
|
|
||||||
@Published private(set) var appType: AppType
|
@Published public private(set) var appType: AppType
|
||||||
|
|
||||||
@Published private(set) var isRefreshingProducts = false
|
@Published public private(set) var isRefreshingProducts = false
|
||||||
|
|
||||||
@Published private(set) var products: [SKProduct]
|
@Published public private(set) var products: [InAppProduct]
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
private let inApp: InApp<LocalProduct>
|
|
||||||
|
|
||||||
private var purchasedAppBuild: Int?
|
private var purchasedAppBuild: Int?
|
||||||
|
|
||||||
private var purchasedFeatures: Set<LocalProduct>
|
private var purchasedFeatures: Set<LocalProduct>
|
||||||
|
@ -86,15 +73,17 @@ final class ProductManager: NSObject, ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var refreshRequest: SKReceiptRefreshRequest?
|
public init(inApp: any LocalInApp,
|
||||||
|
receiptReader: ReceiptReader,
|
||||||
init(overriddenAppType: AppType?, buildProducts: BuildProducts) {
|
overriddenAppType: AppType? = nil,
|
||||||
|
buildProducts: BuildProducts) {
|
||||||
self.overriddenAppType = overriddenAppType
|
self.overriddenAppType = overriddenAppType
|
||||||
|
self.receiptReader = receiptReader
|
||||||
self.buildProducts = buildProducts
|
self.buildProducts = buildProducts
|
||||||
appType = .undefined
|
appType = .undefined
|
||||||
|
|
||||||
products = []
|
products = []
|
||||||
inApp = InApp()
|
self.inApp = inApp
|
||||||
purchasedAppBuild = nil
|
purchasedAppBuild = nil
|
||||||
purchasedFeatures = []
|
purchasedFeatures = []
|
||||||
purchaseDates = [:]
|
purchaseDates = [:]
|
||||||
|
@ -102,8 +91,10 @@ final class ProductManager: NSObject, ObservableObject {
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
inApp.setTransactionsObserver { [weak self] in
|
||||||
|
self?.reloadReceipt()
|
||||||
|
}
|
||||||
reloadReceipt()
|
reloadReceipt()
|
||||||
SKPaymentQueue.default().add(self)
|
|
||||||
refreshProducts()
|
refreshProducts()
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
|
@ -114,15 +105,11 @@ final class ProductManager: NSObject, ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
public func canMakePayments() -> Bool {
|
||||||
SKPaymentQueue.default().remove(self)
|
inApp.canMakePurchases()
|
||||||
}
|
}
|
||||||
|
|
||||||
func canMakePayments() -> Bool {
|
public func refreshProducts() {
|
||||||
SKPaymentQueue.canMakePayments()
|
|
||||||
}
|
|
||||||
|
|
||||||
func refreshProducts() {
|
|
||||||
let ids = LocalProduct.all
|
let ids = LocalProduct.all
|
||||||
guard !ids.isEmpty else {
|
guard !ids.isEmpty else {
|
||||||
return
|
return
|
||||||
|
@ -132,23 +119,26 @@ final class ProductManager: NSObject, ObservableObject {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
isRefreshingProducts = true
|
isRefreshingProducts = true
|
||||||
inApp.requestProducts(withIdentifiers: ids, completionHandler: { _ in
|
Task {
|
||||||
pp_log.debug("In-app products: \(self.inApp.products.map { $0.productIdentifier })")
|
do {
|
||||||
|
let productsMap = try await inApp.requestProducts(withIdentifiers: ids)
|
||||||
|
pp_log.debug("In-app products: \(productsMap.keys.map(\.rawValue))")
|
||||||
|
|
||||||
self.products = self.inApp.products
|
products = Array(productsMap.values)
|
||||||
self.isRefreshingProducts = false
|
isRefreshingProducts = false
|
||||||
}, failureHandler: {
|
} catch {
|
||||||
pp_log.warning("Unable to list products: \($0)")
|
pp_log.warning("Unable to list products: \(error)")
|
||||||
self.isRefreshingProducts = false
|
isRefreshingProducts = false
|
||||||
})
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func product(withIdentifier identifier: LocalProduct) -> SKProduct? {
|
public func product(withIdentifier identifier: LocalProduct) -> InAppProduct? {
|
||||||
inApp.product(withIdentifier: identifier)
|
inApp.product(withIdentifier: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
func featureProducts(including: [LocalProduct]) -> [SKProduct] {
|
public func featureProducts(including: [LocalProduct]) -> [InAppProduct] {
|
||||||
inApp.products.filter {
|
inApp.products().filter {
|
||||||
guard let p = LocalProduct(rawValue: $0.productIdentifier) else {
|
guard let p = LocalProduct(rawValue: $0.productIdentifier) else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -162,8 +152,8 @@ final class ProductManager: NSObject, ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func featureProducts(excluding: [LocalProduct]) -> [SKProduct] {
|
public func featureProducts(excluding: [LocalProduct]) -> [InAppProduct] {
|
||||||
inApp.products.filter {
|
inApp.products().filter {
|
||||||
guard let p = LocalProduct(rawValue: $0.productIdentifier) else {
|
guard let p = LocalProduct(rawValue: $0.productIdentifier) else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -177,35 +167,42 @@ final class ProductManager: NSObject, ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func purchase(_ product: SKProduct, completionHandler: @escaping (Result<InAppPurchaseResult, Error>) -> Void) {
|
public func purchase(_ product: InAppProduct, completionHandler: @escaping (Result<InAppPurchaseResult, Error>) -> Void) {
|
||||||
inApp.purchase(product: product) { result in
|
guard let pid = LocalProduct(rawValue: product.productIdentifier) else {
|
||||||
if case .success = result {
|
pp_log.warning("Unrecognized product: \(product)")
|
||||||
self.reloadReceipt()
|
return
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async {
|
Task {
|
||||||
completionHandler(result)
|
do {
|
||||||
|
let result = try await inApp.purchase(productWithIdentifier: pid)
|
||||||
|
reloadReceipt()
|
||||||
|
completionHandler(.success(result))
|
||||||
|
} catch {
|
||||||
|
completionHandler(.failure(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func restorePurchases(completionHandler: @escaping (Error?) -> Void) {
|
public func restorePurchases(completionHandler: @escaping (Error?) -> Void) {
|
||||||
inApp.restorePurchases { (finished, _, error) in
|
Task {
|
||||||
guard finished else {
|
do {
|
||||||
return
|
try await inApp.restorePurchases()
|
||||||
}
|
completionHandler(nil)
|
||||||
DispatchQueue.main.async {
|
} catch {
|
||||||
completionHandler(error)
|
completionHandler(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: In-app eligibility
|
// MARK: In-app eligibility
|
||||||
|
|
||||||
private func isCurrentPlatformVersion() -> Bool {
|
extension ProductManager {
|
||||||
|
public func isCurrentPlatformVersion() -> Bool {
|
||||||
purchasedFeatures.contains(isMac ? .fullVersion_macOS : .fullVersion_iOS)
|
purchasedFeatures.contains(isMac ? .fullVersion_macOS : .fullVersion_iOS)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isFullVersion() -> Bool {
|
public func isFullVersion() -> Bool {
|
||||||
if appType == .fullVersion {
|
if appType == .fullVersion {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -215,42 +212,47 @@ final class ProductManager: NSObject, ObservableObject {
|
||||||
return purchasedFeatures.contains(.fullVersion)
|
return purchasedFeatures.contains(.fullVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEligible(forFeature feature: LocalProduct) -> Bool {
|
public func isEligible(forFeature feature: LocalProduct) -> Bool {
|
||||||
if let purchasedAppBuild = purchasedAppBuild {
|
if let purchasedAppBuild = purchasedAppBuild {
|
||||||
if feature == .networkSettings && buildProducts.hasProduct(.networkSettings, atBuild: purchasedAppBuild) {
|
if feature == .networkSettings && buildProducts.hasProduct(.networkSettings, atBuild: purchasedAppBuild) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if feature.isPlatformVersion {
|
||||||
|
return purchasedFeatures.contains(feature)
|
||||||
|
}
|
||||||
return isFullVersion() || purchasedFeatures.contains(feature)
|
return isFullVersion() || purchasedFeatures.contains(feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEligible(forProvider providerName: ProviderName) -> Bool {
|
public func isEligible(forProvider providerName: ProviderName) -> Bool {
|
||||||
guard providerName != .oeck else {
|
guard providerName != .oeck else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return isEligible(forFeature: providerName.product)
|
return isEligible(forFeature: providerName.product)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEligibleForFeedback() -> Bool {
|
public func isEligibleForFeedback() -> Bool {
|
||||||
appType == .beta || !purchasedFeatures.isEmpty
|
appType == .beta || !purchasedFeatures.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasPurchased(_ product: LocalProduct) -> Bool {
|
public func hasPurchased(_ product: LocalProduct) -> Bool {
|
||||||
purchasedFeatures.contains(product)
|
purchasedFeatures.contains(product)
|
||||||
}
|
}
|
||||||
|
|
||||||
func purchaseDate(forProduct product: LocalProduct) -> Date? {
|
public func purchaseDate(forProduct product: LocalProduct) -> Date? {
|
||||||
purchaseDates[product]
|
purchaseDates[product]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func reloadReceipt(andNotify: Bool = true) {
|
extension ProductManager {
|
||||||
guard let receipt else {
|
public func reloadReceipt(andNotify: Bool = true) {
|
||||||
|
guard let receipt = receiptReader.receipt(for: appType) else {
|
||||||
pp_log.error("Could not parse App Store receipt!")
|
pp_log.error("Could not parse App Store receipt!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let originalAppVersion = receipt.originalAppVersion, let buildNumber = Int(originalAppVersion) {
|
if let originalBuildNumber = receipt.originalBuildNumber {
|
||||||
purchasedAppBuild = buildNumber
|
purchasedAppBuild = originalBuildNumber
|
||||||
}
|
}
|
||||||
purchasedFeatures.removeAll()
|
purchasedFeatures.removeAll()
|
||||||
var newCancelledPurchases: Set<LocalProduct> = []
|
var newCancelledPurchases: Set<LocalProduct> = []
|
||||||
|
@ -263,7 +265,7 @@ final class ProductManager: NSObject, ObservableObject {
|
||||||
purchasedFeatures.insert($0)
|
purchasedFeatures.insert($0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let iapReceipts = receipt.inAppPurchaseReceipts {
|
if let iapReceipts = receipt.purchaseReceipts {
|
||||||
purchaseDates.removeAll()
|
purchaseDates.removeAll()
|
||||||
|
|
||||||
pp_log.debug("In-app receipts:")
|
pp_log.debug("In-app receipts:")
|
||||||
|
@ -291,14 +293,6 @@ final class ProductManager: NSObject, ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ProductManager: SKPaymentTransactionObserver {
|
|
||||||
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
|
||||||
self?.reloadReceipt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension ProductManager {
|
private extension ProductManager {
|
||||||
var isMac: Bool {
|
var isMac: Bool {
|
||||||
#if targetEnvironment(macCatalyst)
|
#if targetEnvironment(macCatalyst)
|
||||||
|
@ -308,30 +302,6 @@ private extension ProductManager {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
var receipt: Receipt? {
|
|
||||||
guard let url = Bundle.main.appStoreReceiptURL else {
|
|
||||||
pp_log.warning("No App Store receipt found!")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
let receipt = Receipt(contentsOfURL: url)
|
|
||||||
|
|
||||||
// in TestFlight, attempt fallback to existing release receipt
|
|
||||||
if appType == .beta {
|
|
||||||
guard let receipt else {
|
|
||||||
let releaseUrl = url.deletingLastPathComponent().appendingPathComponent("receipt")
|
|
||||||
guard releaseUrl != url else {
|
|
||||||
assertionFailure("How can release URL be equal to sandbox URL in TestFlight?")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
pp_log.warning("Sandbox receipt not found, falling back to Release receipt")
|
|
||||||
return Receipt(contentsOfURL: releaseUrl)
|
|
||||||
}
|
|
||||||
return receipt
|
|
||||||
}
|
|
||||||
|
|
||||||
return receipt
|
|
||||||
}
|
|
||||||
|
|
||||||
func detectRefunds(_ refunds: Set<LocalProduct>) {
|
func detectRefunds(_ refunds: Set<LocalProduct>) {
|
||||||
let isEligibleForFullVersion = isFullVersion()
|
let isEligibleForFullVersion = isFullVersion()
|
||||||
let hasCancelledFullVersion: Bool
|
let hasCancelledFullVersion: Bool
|
|
@ -1,4 +1,5 @@
|
||||||
@_exported import PassepartoutCore
|
@_exported import PassepartoutCore
|
||||||
|
@_exported import PassepartoutFrontend
|
||||||
@_exported import PassepartoutProviders
|
@_exported import PassepartoutProviders
|
||||||
@_exported import PassepartoutProvidersImpl
|
@_exported import PassepartoutProvidersImpl
|
||||||
@_exported import PassepartoutVPN
|
@_exported import PassepartoutVPN
|
||||||
|
|
|
@ -58,9 +58,12 @@ private extension Profile.OnDemand {
|
||||||
|
|
||||||
// apply exceptions (unless .any)
|
// apply exceptions (unless .any)
|
||||||
if withCustomRules && policy != .any {
|
if withCustomRules && policy != .any {
|
||||||
|
#if os(iOS)
|
||||||
if Utils.hasCellularData() && withMobileNetwork {
|
if Utils.hasCellularData() && withMobileNetwork {
|
||||||
rules.append(cellularRule())
|
rules.append(cellularRule())
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#if os(macOS)
|
||||||
if Utils.hasEthernet() && withEthernetNetwork {
|
if Utils.hasEthernet() && withEthernetNetwork {
|
||||||
if let rule = ethernetRule() {
|
if let rule = ethernetRule() {
|
||||||
rules.append(rule)
|
rules.append(rule)
|
||||||
|
@ -68,6 +71,7 @@ private extension Profile.OnDemand {
|
||||||
pp_log.warning("Unable to add rule for NEOnDemandRuleInterfaceType.ethernet (not compatible)")
|
pp_log.warning("Unable to add rule for NEOnDemandRuleInterfaceType.ethernet (not compatible)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
let SSIDs = Array(withSSIDs.filter { $1 }.keys)
|
let SSIDs = Array(withSSIDs.filter { $1 }.keys)
|
||||||
if !SSIDs.isEmpty {
|
if !SSIDs.isEmpty {
|
||||||
rules.append(wifiRule(SSIDs: SSIDs))
|
rules.append(wifiRule(SSIDs: SSIDs))
|
||||||
|
@ -108,16 +112,20 @@ private extension Profile.OnDemand {
|
||||||
return rule
|
return rule
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
func cellularRule() -> NEOnDemandRule {
|
func cellularRule() -> NEOnDemandRule {
|
||||||
networkRule(matchingInterface: .cellular)
|
networkRule(matchingInterface: .cellular)
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
func ethernetRule() -> NEOnDemandRule? {
|
func ethernetRule() -> NEOnDemandRule? {
|
||||||
guard let compatibleEthernet = NEOnDemandRuleInterfaceType.compatibleEthernet else {
|
guard let compatibleEthernet = NEOnDemandRuleInterfaceType.compatibleEthernet else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return networkRule(matchingInterface: compatibleEthernet)
|
return networkRule(matchingInterface: compatibleEthernet)
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
func wifiRule(SSIDs: [String]) -> NEOnDemandRule {
|
func wifiRule(SSIDs: [String]) -> NEOnDemandRule {
|
||||||
let rule = networkRule(matchingInterface: .wiFi)
|
let rule = networkRule(matchingInterface: .wiFi)
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
//
|
||||||
|
// MockInApp.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 12/19/23.
|
||||||
|
// Copyright (c) 2023 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 Foundation
|
||||||
|
@testable import PassepartoutCore
|
||||||
|
@testable import PassepartoutFrontend
|
||||||
|
|
||||||
|
final class MockInApp: LocalInApp {
|
||||||
|
var allProducts: [LocalProduct: InAppProduct]
|
||||||
|
|
||||||
|
init() {
|
||||||
|
allProducts = [:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func canMakePurchases() -> Bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestProducts(withIdentifiers identifiers: [LocalProduct]) async throws -> [LocalProduct: InAppProduct] {
|
||||||
|
allProducts = LocalProduct.all.reduce(into: [:]) {
|
||||||
|
$0[$1] = InAppProduct(productIdentifier: $1.rawValue,
|
||||||
|
localizedTitle: $1.rawValue,
|
||||||
|
localizedDescription: $1.rawValue,
|
||||||
|
price: 10.0,
|
||||||
|
localizedPrice: "10.0")
|
||||||
|
}
|
||||||
|
return allProducts
|
||||||
|
}
|
||||||
|
|
||||||
|
func purchase(productWithIdentifier productIdentifier: LocalProduct) async throws -> InAppPurchaseResult {
|
||||||
|
.done
|
||||||
|
}
|
||||||
|
|
||||||
|
func restorePurchases() async throws {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
func products() -> [InAppProduct] {
|
||||||
|
Array(allProducts.values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func product(withIdentifier productIdentifier: LocalProduct) -> InAppProduct? {
|
||||||
|
allProducts[productIdentifier]
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTransactionsObserver(_ block: @escaping () -> Void) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
//
|
//
|
||||||
// VPNTests.swift
|
// MockReceiptReader.swift
|
||||||
// Passepartout
|
// Passepartout
|
||||||
//
|
//
|
||||||
// Created by Davide De Rosa on 10/29/22.
|
// Created by Davide De Rosa on 12/19/23.
|
||||||
// Copyright (c) 2023 Davide De Rosa. All rights reserved.
|
// Copyright (c) 2023 Davide De Rosa. All rights reserved.
|
||||||
//
|
//
|
||||||
// https://github.com/passepartoutvpn
|
// https://github.com/passepartoutvpn
|
||||||
|
@ -23,14 +23,26 @@
|
||||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
//
|
//
|
||||||
|
|
||||||
@testable import PassepartoutVPN
|
import Foundation
|
||||||
import XCTest
|
import PassepartoutCore
|
||||||
|
@testable import PassepartoutFrontend
|
||||||
|
|
||||||
final class VPNTests: XCTestCase {
|
final class MockReceiptReader: ReceiptReader {
|
||||||
override func setUp() {
|
var receipt: InAppReceipt?
|
||||||
|
|
||||||
|
init(receipt: InAppReceipt? = nil) {
|
||||||
|
self.receipt = receipt
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDown() {
|
func setReceipt(withBuild build: Int, products: [LocalProduct], cancelledProducts: Set<LocalProduct> = []) {
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
receipt = InAppReceipt(originalBuildNumber: build, purchaseReceipts: products.map {
|
||||||
|
.init(productIdentifier: $0.rawValue,
|
||||||
|
cancellationDate: cancelledProducts.contains($0) ? Date() : nil,
|
||||||
|
originalPurchaseDate: nil)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
func receipt(for appType: AppType) -> InAppReceipt? {
|
||||||
|
receipt
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
//
|
||||||
|
// ProductManagerTests.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 12/19/23.
|
||||||
|
// Copyright (c) 2023 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 Foundation
|
||||||
|
@testable import PassepartoutFrontend
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class ProductManagerTests: XCTestCase {
|
||||||
|
private let inApp = MockInApp()
|
||||||
|
|
||||||
|
private let noBuildProducts = BuildProducts { _ in [] }
|
||||||
|
|
||||||
|
func test_givenBuildProducts_whenOlder_thenFullVersion() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
reader.setReceipt(withBuild: 500, products: [])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: BuildProducts { build in
|
||||||
|
if build <= 1000 {
|
||||||
|
return [.fullVersion]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
XCTAssertTrue(sut.isEligible(forFeature: .fullVersion))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_givenBuildProducts_whenNewer_thenFreeVersion() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: BuildProducts { build in
|
||||||
|
if build <= 1000 {
|
||||||
|
return [.fullVersion]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_givenPurchase_whenReload_thenCredited() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion))
|
||||||
|
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [.fullVersion])
|
||||||
|
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion))
|
||||||
|
|
||||||
|
sut.reloadReceipt()
|
||||||
|
XCTAssertTrue(sut.isEligible(forFeature: .fullVersion))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_givenPurchase_whenCancelled_thenRevoke() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [.fullVersion])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
XCTAssertTrue(sut.isEligible(forFeature: .fullVersion))
|
||||||
|
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [.fullVersion], cancelledProducts: [.fullVersion])
|
||||||
|
XCTAssertTrue(sut.isEligible(forFeature: .fullVersion))
|
||||||
|
|
||||||
|
sut.reloadReceipt()
|
||||||
|
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_givenFeature_thenIsOnlyEligibleForFeature() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [.siriShortcuts, .networkSettings])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
|
||||||
|
XCTAssertTrue(sut.isEligible(forFeature: .siriShortcuts))
|
||||||
|
XCTAssertTrue(sut.isEligible(forFeature: .networkSettings))
|
||||||
|
XCTAssertFalse(sut.isEligible(forFeature: .trustedNetworks))
|
||||||
|
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion))
|
||||||
|
XCTAssertFalse(sut.isFullVersion())
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_givenPlatformVersion_thenIsFullVersionForPlatform() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [.fullVersion_macOS, .networkSettings])
|
||||||
|
sut.reloadReceipt()
|
||||||
|
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion_iOS))
|
||||||
|
XCTAssertTrue(sut.isEligible(forFeature: .fullVersion_macOS))
|
||||||
|
#else
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [.fullVersion_iOS, .networkSettings])
|
||||||
|
sut.reloadReceipt()
|
||||||
|
XCTAssertTrue(sut.isEligible(forFeature: .fullVersion_iOS))
|
||||||
|
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion_macOS))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
XCTAssertTrue(sut.isCurrentPlatformVersion())
|
||||||
|
XCTAssertTrue(sut.isFullVersion())
|
||||||
|
XCTAssertTrue(sut.isEligible(forFeature: .fullVersion))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_givenFullVersion_thenIsEligibleForAnyFeature() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [.fullVersion])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
|
||||||
|
XCTAssertTrue(LocalProduct
|
||||||
|
.allFeatures
|
||||||
|
.filter { !$0.isPlatformVersion }
|
||||||
|
.allSatisfy(sut.isEligible(forFeature:))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_givenFreeVersion_thenIsNotEligibleForAnyFeature() {
|
||||||
|
let reader = MockReceiptReader()
|
||||||
|
reader.setReceipt(withBuild: 1500, products: [])
|
||||||
|
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
|
||||||
|
|
||||||
|
XCTAssertFalse(LocalProduct
|
||||||
|
.allFeatures
|
||||||
|
.allSatisfy(sut.isEligible(forFeature:))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
//
|
||||||
|
// ProviderManagerTests.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 3/13/22.
|
||||||
|
// Copyright (c) 2023 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 Combine
|
||||||
|
//import CoreData
|
||||||
|
//import PassepartoutCore
|
||||||
|
//import PassepartoutProviders
|
||||||
|
//@testable import PassepartoutProvidersImpl
|
||||||
|
//import XCTest
|
||||||
|
//
|
||||||
|
//@MainActor
|
||||||
|
//final class ProvidersTests: XCTestCase {
|
||||||
|
// private var persistence: ProvidersPersistence!
|
||||||
|
//
|
||||||
|
// private var manager: ProviderManager!
|
||||||
|
//
|
||||||
|
// private var cancellables: Set<AnyCancellable> = []
|
||||||
|
//
|
||||||
|
// override func setUp() {
|
||||||
|
// persistence = ProvidersPersistence(withName: "ProvidersTests", cloudKit: false, author: nil)
|
||||||
|
//
|
||||||
|
// let remoteStrategy = APIRemoteProvidersStrategy(
|
||||||
|
// appBuild: 10000,
|
||||||
|
// bundleServices: APIWebServices.bundledServices(withVersion: "v5"),
|
||||||
|
// remoteServices: APIWebServices("v5", URL(string: "https://passepartoutvpn.app/api/")!, timeout: nil),
|
||||||
|
// webServicesRepository: persistence.webServicesRepository()
|
||||||
|
// )
|
||||||
|
// manager = ProviderManager(
|
||||||
|
// localProvidersRepository: persistence.localProvidersRepository(),
|
||||||
|
// remoteProvidersStrategy: remoteStrategy
|
||||||
|
// )
|
||||||
|
//// persistence.truncate()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override func tearDown() {
|
||||||
|
//// persistence.truncate()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func testFetchLocalIndex() throws {
|
||||||
|
// let exp = expectation(description: "Local index")
|
||||||
|
//
|
||||||
|
// manager.fetchProvidersIndexPublisher(priority: .bundle)
|
||||||
|
// .sink {
|
||||||
|
// switch $0 {
|
||||||
|
// case .finished:
|
||||||
|
// exp.fulfill()
|
||||||
|
//
|
||||||
|
// case .failure(let error):
|
||||||
|
// pp_log.error("Unable to load remote provider: \(error)")
|
||||||
|
// exp.fulfill()
|
||||||
|
// }
|
||||||
|
// } receiveValue: {
|
||||||
|
// pp_log.debug("Loaded index")
|
||||||
|
// }.store(in: &cancellables)
|
||||||
|
//
|
||||||
|
// waitForExpectations(timeout: 10.0, handler: nil)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func testFetchRemoteIndex() throws {
|
||||||
|
// let exp = expectation(description: "Remote index")
|
||||||
|
//
|
||||||
|
// manager.fetchProvidersIndexPublisher(priority: .remote)
|
||||||
|
// .sink {
|
||||||
|
// switch $0 {
|
||||||
|
// case .finished:
|
||||||
|
// exp.fulfill()
|
||||||
|
//
|
||||||
|
// case .failure(let error):
|
||||||
|
// pp_log.error("Unable to load remote provider: \(error)")
|
||||||
|
// exp.fulfill()
|
||||||
|
// }
|
||||||
|
// } receiveValue: {
|
||||||
|
// pp_log.debug("Loaded index")
|
||||||
|
// }.store(in: &cancellables)
|
||||||
|
//
|
||||||
|
// waitForExpectations(timeout: 10.0, handler: nil)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func testFetchRemoteProvider() async {
|
||||||
|
// do {
|
||||||
|
// try await manager.fetchProviderPublisher(withName: .hideme, vpnProtocol: .openVPN, priority: .remote).async()
|
||||||
|
// pp_log.debug("Loaded provider")
|
||||||
|
// } catch {
|
||||||
|
// XCTFail("Unable to load remote provider: \(error)")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func testListProviders() {
|
||||||
|
// let providers = manager.allProviders()
|
||||||
|
// providers.forEach {
|
||||||
|
// pp_log.debug("\($0.name) -> \($0.fullName)")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func testListCategories() async {
|
||||||
|
// await fetchProvider(.surfshark)
|
||||||
|
// let categories = manager.categories(.surfshark, vpnProtocol: .openVPN)
|
||||||
|
// categories.forEach {
|
||||||
|
// pp_log.debug("Category: \($0.name)")
|
||||||
|
// $0.locations.forEach {
|
||||||
|
// pp_log.debug("\t\($0)")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func testListServers() async {
|
||||||
|
// await fetchProvider(.nordvpn)
|
||||||
|
// manager.allProviders().filter({ $0.name == .nordvpn }).forEach {
|
||||||
|
// let location = ProviderLocation(
|
||||||
|
// providerMetadata: $0,
|
||||||
|
// vpnProtocol: .openVPN,
|
||||||
|
// categoryName: "",
|
||||||
|
// countryCode: "ES",
|
||||||
|
// servers: nil
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// let servers = manager.servers(forLocation: location)
|
||||||
|
// pp_log.debug("\($0.fullName): Servers [\(location.countryCode)] (\(servers.count)): \(servers)")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func testServerId() async {
|
||||||
|
// await fetchProvider(.nordvpn)
|
||||||
|
// guard let server = manager.server(.nordvpn, vpnProtocol: .openVPN, apiId: "es143") else {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// pp_log.debug(server)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func testDefaultServer() async {
|
||||||
|
// await fetchProvider(.protonvpn)
|
||||||
|
// guard let server = manager.anyDefaultServer(.protonvpn, vpnProtocol: .openVPN) else {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// pp_log.debug(server)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func testServerUniqueId() async {
|
||||||
|
// await fetchProvider(.nordvpn)
|
||||||
|
// guard let server = manager.server(withId: "BEA03D24A5854DD17395057DEFBE7D6BEEA981227ACF8949E487443E6B5EF9C7") else {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// pp_log.debug(server)
|
||||||
|
// XCTAssertEqual(server.apiId, "es143")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private func fetchProvider(_ name: ProviderName) async {
|
||||||
|
// try? await manager.fetchProvidersIndexPublisher(priority: .bundle).async()
|
||||||
|
// try? await manager.fetchProviderPublisher(withName: name, vpnProtocol: .openVPN, priority: .bundle).async()
|
||||||
|
// }
|
||||||
|
//}
|
|
@ -1,173 +0,0 @@
|
||||||
//
|
|
||||||
// ProvidersTests.swift
|
|
||||||
// Passepartout
|
|
||||||
//
|
|
||||||
// Created by Davide De Rosa on 3/13/22.
|
|
||||||
// Copyright (c) 2023 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 Combine
|
|
||||||
import CoreData
|
|
||||||
import PassepartoutCore
|
|
||||||
import PassepartoutProviders
|
|
||||||
@testable import PassepartoutProvidersImpl
|
|
||||||
import XCTest
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
final class ProvidersTests: XCTestCase {
|
|
||||||
private var persistence: ProvidersPersistence!
|
|
||||||
|
|
||||||
private var manager: ProviderManager!
|
|
||||||
|
|
||||||
private var cancellables: Set<AnyCancellable> = []
|
|
||||||
|
|
||||||
override func setUp() {
|
|
||||||
persistence = ProvidersPersistence(withName: "ProvidersTests", cloudKit: false, author: nil)
|
|
||||||
|
|
||||||
let remoteStrategy = APIRemoteProvidersStrategy(
|
|
||||||
appBuild: 10000,
|
|
||||||
bundleServices: APIWebServices.bundledServices(withVersion: "v5"),
|
|
||||||
remoteServices: APIWebServices("v5", URL(string: "https://passepartoutvpn.app/api/")!, timeout: nil),
|
|
||||||
webServicesRepository: persistence.webServicesRepository()
|
|
||||||
)
|
|
||||||
manager = ProviderManager(
|
|
||||||
localProvidersRepository: persistence.localProvidersRepository(),
|
|
||||||
remoteProvidersStrategy: remoteStrategy
|
|
||||||
)
|
|
||||||
// persistence.truncate()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tearDown() {
|
|
||||||
// persistence.truncate()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testFetchLocalIndex() throws {
|
|
||||||
let exp = expectation(description: "Local index")
|
|
||||||
|
|
||||||
manager.fetchProvidersIndexPublisher(priority: .bundle)
|
|
||||||
.sink {
|
|
||||||
switch $0 {
|
|
||||||
case .finished:
|
|
||||||
exp.fulfill()
|
|
||||||
|
|
||||||
case .failure(let error):
|
|
||||||
pp_log.error("Unable to load remote provider: \(error)")
|
|
||||||
exp.fulfill()
|
|
||||||
}
|
|
||||||
} receiveValue: {
|
|
||||||
pp_log.debug("Loaded index")
|
|
||||||
}.store(in: &cancellables)
|
|
||||||
|
|
||||||
waitForExpectations(timeout: 10.0, handler: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testFetchRemoteIndex() throws {
|
|
||||||
let exp = expectation(description: "Remote index")
|
|
||||||
|
|
||||||
manager.fetchProvidersIndexPublisher(priority: .remote)
|
|
||||||
.sink {
|
|
||||||
switch $0 {
|
|
||||||
case .finished:
|
|
||||||
exp.fulfill()
|
|
||||||
|
|
||||||
case .failure(let error):
|
|
||||||
pp_log.error("Unable to load remote provider: \(error)")
|
|
||||||
exp.fulfill()
|
|
||||||
}
|
|
||||||
} receiveValue: {
|
|
||||||
pp_log.debug("Loaded index")
|
|
||||||
}.store(in: &cancellables)
|
|
||||||
|
|
||||||
waitForExpectations(timeout: 10.0, handler: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testFetchRemoteProvider() async {
|
|
||||||
do {
|
|
||||||
try await manager.fetchProviderPublisher(withName: .hideme, vpnProtocol: .openVPN, priority: .remote).async()
|
|
||||||
pp_log.debug("Loaded provider")
|
|
||||||
} catch {
|
|
||||||
XCTFail("Unable to load remote provider: \(error)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testListProviders() {
|
|
||||||
let providers = manager.allProviders()
|
|
||||||
providers.forEach {
|
|
||||||
pp_log.debug("\($0.name) -> \($0.fullName)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testListCategories() async {
|
|
||||||
await fetchProvider(.surfshark)
|
|
||||||
let categories = manager.categories(.surfshark, vpnProtocol: .openVPN)
|
|
||||||
categories.forEach {
|
|
||||||
pp_log.debug("Category: \($0.name)")
|
|
||||||
$0.locations.forEach {
|
|
||||||
pp_log.debug("\t\($0)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testListServers() async {
|
|
||||||
await fetchProvider(.nordvpn)
|
|
||||||
manager.allProviders().filter({ $0.name == .nordvpn }).forEach {
|
|
||||||
let location = ProviderLocation(
|
|
||||||
providerMetadata: $0,
|
|
||||||
vpnProtocol: .openVPN,
|
|
||||||
categoryName: "",
|
|
||||||
countryCode: "ES",
|
|
||||||
servers: nil
|
|
||||||
)
|
|
||||||
|
|
||||||
let servers = manager.servers(forLocation: location)
|
|
||||||
pp_log.debug("\($0.fullName): Servers [\(location.countryCode)] (\(servers.count)): \(servers)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testServerId() async {
|
|
||||||
await fetchProvider(.nordvpn)
|
|
||||||
guard let server = manager.server(.nordvpn, vpnProtocol: .openVPN, apiId: "es143") else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pp_log.debug(server)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testDefaultServer() async {
|
|
||||||
await fetchProvider(.protonvpn)
|
|
||||||
guard let server = manager.anyDefaultServer(.protonvpn, vpnProtocol: .openVPN) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pp_log.debug(server)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testServerUniqueId() async {
|
|
||||||
await fetchProvider(.nordvpn)
|
|
||||||
guard let server = manager.server(withId: "BEA03D24A5854DD17395057DEFBE7D6BEEA981227ACF8949E487443E6B5EF9C7") else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pp_log.debug(server)
|
|
||||||
XCTAssertEqual(server.apiId, "es143")
|
|
||||||
}
|
|
||||||
|
|
||||||
private func fetchProvider(_ name: ProviderName) async {
|
|
||||||
try? await manager.fetchProvidersIndexPublisher(priority: .bundle).async()
|
|
||||||
try? await manager.fetchProviderPublisher(withName: name, vpnProtocol: .openVPN, priority: .bundle).async()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
//
|
|
||||||
// ServicesTests.swift
|
|
||||||
// Passepartout
|
|
||||||
//
|
|
||||||
// Created by Davide De Rosa on 6/11/18.
|
|
||||||
// Copyright (c) 2023 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 Combine
|
|
||||||
import PassepartoutCore
|
|
||||||
@testable import PassepartoutProvidersImpl
|
|
||||||
import XCTest
|
|
||||||
|
|
||||||
final class ServicesTests: XCTestCase {
|
|
||||||
let wsLocal = APIWebServices.bundledServices(withVersion: "v5")
|
|
||||||
|
|
||||||
let wsRemote = APIWebServices("v5", URL(string: "https://passepartoutvpn.app/api/")!, timeout: nil)
|
|
||||||
|
|
||||||
private var cancellables: Set<AnyCancellable> = []
|
|
||||||
|
|
||||||
override func setUp() {
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tearDown() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func testLastModified() {
|
|
||||||
let fmt = DateFormatter()
|
|
||||||
fmt.timeZone = TimeZone(abbreviation: "GMT")
|
|
||||||
fmt.dateFormat = "EEE, dd LLL yyyy HH:mm:ss zzz"
|
|
||||||
|
|
||||||
let lmString = "Wed, 23 Oct 2019 17:06:54 GMT"
|
|
||||||
|
|
||||||
fmt.locale = Locale(identifier: "en")
|
|
||||||
XCTAssertNotNil(fmt.date(from: lmString))
|
|
||||||
fmt.locale = Locale(identifier: "fr-FR")
|
|
||||||
XCTAssertNil(fmt.date(from: lmString))
|
|
||||||
}
|
|
||||||
|
|
||||||
func testLocalIndex() {
|
|
||||||
let exp = expectation(description: "")
|
|
||||||
wsLocal.providersIndex()
|
|
||||||
.sink {
|
|
||||||
switch $0 {
|
|
||||||
case .finished:
|
|
||||||
break
|
|
||||||
|
|
||||||
case .failure(let error):
|
|
||||||
pp_log.debug(error)
|
|
||||||
exp.fulfill()
|
|
||||||
}
|
|
||||||
} receiveValue: {
|
|
||||||
pp_log.debug($0)
|
|
||||||
exp.fulfill()
|
|
||||||
}.store(in: &cancellables)
|
|
||||||
|
|
||||||
waitForExpectations(timeout: 10.0, handler: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testRemoteIndex() {
|
|
||||||
let exp = expectation(description: "")
|
|
||||||
wsRemote.providersIndex()
|
|
||||||
.sink {
|
|
||||||
switch $0 {
|
|
||||||
case .finished:
|
|
||||||
break
|
|
||||||
|
|
||||||
case .failure(let error):
|
|
||||||
pp_log.debug(error)
|
|
||||||
exp.fulfill()
|
|
||||||
}
|
|
||||||
} receiveValue: {
|
|
||||||
pp_log.debug($0)
|
|
||||||
exp.fulfill()
|
|
||||||
}.store(in: &cancellables)
|
|
||||||
|
|
||||||
waitForExpectations(timeout: 10.0, handler: nil)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
//
|
||||||
|
// WebServicesTests.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 6/11/18.
|
||||||
|
// Copyright (c) 2023 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 Combine
|
||||||
|
//import PassepartoutCore
|
||||||
|
//@testable import PassepartoutProvidersImpl
|
||||||
|
//import XCTest
|
||||||
|
//
|
||||||
|
//final class ServicesTests: XCTestCase {
|
||||||
|
// let wsLocal = APIWebServices.bundledServices(withVersion: "v5")
|
||||||
|
//
|
||||||
|
// let wsRemote = APIWebServices("v5", URL(string: "https://passepartoutvpn.app/api/")!, timeout: nil)
|
||||||
|
//
|
||||||
|
// private var cancellables: Set<AnyCancellable> = []
|
||||||
|
//
|
||||||
|
// override func setUp() {
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override func tearDown() {
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func testLastModified() {
|
||||||
|
// let fmt = DateFormatter()
|
||||||
|
// fmt.timeZone = TimeZone(abbreviation: "GMT")
|
||||||
|
// fmt.dateFormat = "EEE, dd LLL yyyy HH:mm:ss zzz"
|
||||||
|
//
|
||||||
|
// let lmString = "Wed, 23 Oct 2019 17:06:54 GMT"
|
||||||
|
//
|
||||||
|
// fmt.locale = Locale(identifier: "en")
|
||||||
|
// XCTAssertNotNil(fmt.date(from: lmString))
|
||||||
|
// fmt.locale = Locale(identifier: "fr-FR")
|
||||||
|
// XCTAssertNil(fmt.date(from: lmString))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func testLocalIndex() {
|
||||||
|
// let exp = expectation(description: "")
|
||||||
|
// wsLocal.providersIndex()
|
||||||
|
// .sink {
|
||||||
|
// switch $0 {
|
||||||
|
// case .finished:
|
||||||
|
// break
|
||||||
|
//
|
||||||
|
// case .failure(let error):
|
||||||
|
// pp_log.debug(error)
|
||||||
|
// exp.fulfill()
|
||||||
|
// }
|
||||||
|
// } receiveValue: {
|
||||||
|
// pp_log.debug($0)
|
||||||
|
// exp.fulfill()
|
||||||
|
// }.store(in: &cancellables)
|
||||||
|
//
|
||||||
|
// waitForExpectations(timeout: 10.0, handler: nil)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func testRemoteIndex() {
|
||||||
|
// let exp = expectation(description: "")
|
||||||
|
// wsRemote.providersIndex()
|
||||||
|
// .sink {
|
||||||
|
// switch $0 {
|
||||||
|
// case .finished:
|
||||||
|
// break
|
||||||
|
//
|
||||||
|
// case .failure(let error):
|
||||||
|
// pp_log.debug(error)
|
||||||
|
// exp.fulfill()
|
||||||
|
// }
|
||||||
|
// } receiveValue: {
|
||||||
|
// pp_log.debug($0)
|
||||||
|
// exp.fulfill()
|
||||||
|
// }.store(in: &cancellables)
|
||||||
|
//
|
||||||
|
// waitForExpectations(timeout: 10.0, handler: nil)
|
||||||
|
// }
|
||||||
|
//}
|
|
@ -1,2 +0,0 @@
|
||||||
scheme "PassepartoutTests"
|
|
||||||
device "iPhone 14"
|
|
Loading…
Reference in New Issue