summaryrefslogtreecommitdiffhomepage
path: root/ios
diff options
context:
space:
mode:
Diffstat (limited to 'ios')
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj185
-rw-r--r--ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme87
-rw-r--r--ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPNSendableTests.xcscheme55
-rw-r--r--ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPNUITests.xcscheme11
-rw-r--r--ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/PacketTunnel.xcscheme14
-rw-r--r--ios/MullvadVPNSendableTests/MullvadVPNSendableTests.swift221
-rw-r--r--ios/TestPlans/MullvadVPNApp.xctestplan30
7 files changed, 513 insertions, 90 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index ac7b7bc054..01df7fa0bb 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 54;
+ objectVersion = 70;
objects = {
/* Begin PBXBuildFile section */
@@ -1322,6 +1322,13 @@
remoteGlobalIDString = 58D223D4294C8E5E0029F5F8;
remoteInfo = MullvadTypes;
};
+ A955EF252D43E5C50025E1B1 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 58CE5E58224146200008646E /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 58CE5E5F224146200008646E;
+ remoteInfo = MullvadVPN;
+ };
A9609B6D2D004D1F0065A3D3 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 58CE5E58224146200008646E /* Project object */;
@@ -2144,6 +2151,7 @@
A944F2712B8E02E800473F4C /* libmullvad_ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmullvad_ios.a; path = "../target/aarch64-apple-ios/debug/libmullvad_ios.a"; sourceTree = "<group>"; };
A9467E7E2A29DEFE000DC21F /* RelayCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayCacheTests.swift; sourceTree = "<group>"; };
A948809A2BC9308D0090A44C /* EphemeralPeerExchangeActor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EphemeralPeerExchangeActor.swift; sourceTree = "<group>"; };
+ A955EF212D43E5C50025E1B1 /* MullvadVPNSendableTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MullvadVPNSendableTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
A95EEE352B722CD600A8A39B /* TunnelMonitorState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitorState.swift; sourceTree = "<group>"; };
A95EEE372B722DFC00A8A39B /* PingStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PingStats.swift; sourceTree = "<group>"; };
A970C89C2B29E38C000A7684 /* Socks5UsernamePasswordCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Socks5UsernamePasswordCommand.swift; sourceTree = "<group>"; };
@@ -2321,6 +2329,10 @@
F998EFF92D3656B100D88D01 /* SKProduct+Sorting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SKProduct+Sorting.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */
+/* Begin PBXFileSystemSynchronizedRootGroup section */
+ A955EF222D43E5C50025E1B1 /* MullvadVPNSendableTests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = MullvadVPNSendableTests; sourceTree = "<group>"; };
+/* End PBXFileSystemSynchronizedRootGroup section */
+
/* Begin PBXFrameworksBuildPhase section */
06799AB928F98E1D00ACD94E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
@@ -2470,6 +2482,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ A955EF1E2D43E5C50025E1B1 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
A992DA1A2C24709F00DE7CE5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -3678,6 +3697,7 @@
58695A9E2A4ADA9200328DB3 /* TunnelObfuscationTests */,
A992DA1E2C24709F00DE7CE5 /* MullvadRustRuntime */,
A9D9A4C12C36D53C004088DD /* MullvadRustRuntimeTests */,
+ A955EF222D43E5C50025E1B1 /* MullvadVPNSendableTests */,
58CE5E61224146200008646E /* Products */,
584F991F2902CBDD001F858D /* Frameworks */,
);
@@ -3704,6 +3724,7 @@
F0ACE3082BE4E478006D5333 /* MullvadMockData.framework */,
A992DA1D2C24709F00DE7CE5 /* MullvadRustRuntime.framework */,
A9D9A4C02C36D53C004088DD /* MullvadRustRuntimeTests.xctest */,
+ A955EF212D43E5C50025E1B1 /* MullvadVPNSendableTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -5034,6 +5055,29 @@
productReference = 852969252B4D9C1F007EAD4C /* MullvadVPNUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
+ A955EF202D43E5C50025E1B1 /* MullvadVPNSendableTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = A955EF2B2D43E5C50025E1B1 /* Build configuration list for PBXNativeTarget "MullvadVPNSendableTests" */;
+ buildPhases = (
+ A955EF1D2D43E5C50025E1B1 /* Sources */,
+ A955EF1E2D43E5C50025E1B1 /* Frameworks */,
+ A955EF1F2D43E5C50025E1B1 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ A955EF262D43E5C50025E1B1 /* PBXTargetDependency */,
+ );
+ fileSystemSynchronizedGroups = (
+ A955EF222D43E5C50025E1B1 /* MullvadVPNSendableTests */,
+ );
+ name = MullvadVPNSendableTests;
+ packageProductDependencies = (
+ );
+ productName = MullvadVPNSendableTests;
+ productReference = A955EF212D43E5C50025E1B1 /* MullvadVPNSendableTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
A992DA1C2C24709F00DE7CE5 /* MullvadRustRuntime */ = {
isa = PBXNativeTarget;
buildConfigurationList = A992DA252C24709F00DE7CE5 /* Build configuration list for PBXNativeTarget "MullvadRustRuntime" */;
@@ -5108,7 +5152,7 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
- LastSwiftUpdateCheck = 1520;
+ LastSwiftUpdateCheck = 1620;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "Mullvad VPN AB";
TargetAttributes = {
@@ -5176,6 +5220,10 @@
CreatedOnToolsVersion = 15.1;
TestTargetID = 58CE5E5F224146200008646E;
};
+ A955EF202D43E5C50025E1B1 = {
+ CreatedOnToolsVersion = 16.2;
+ TestTargetID = 58CE5E5F224146200008646E;
+ };
A992DA1C2C24709F00DE7CE5 = {
CreatedOnToolsVersion = 15.2;
LastSwiftMigration = 1520;
@@ -5224,6 +5272,7 @@
F0ACE3072BE4E478006D5333 /* MullvadMockData */,
A992DA1C2C24709F00DE7CE5 /* MullvadRustRuntime */,
A9D9A4BF2C36D53C004088DD /* MullvadRustRuntimeTests */,
+ A955EF202D43E5C50025E1B1 /* MullvadVPNSendableTests */,
);
};
/* End PBXProject section */
@@ -5337,6 +5386,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ A955EF1F2D43E5C50025E1B1 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
A992DA1B2C24709F00DE7CE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -6097,7 +6153,6 @@
5868585524054096000B8131 /* CustomButton.swift in Sources */,
58E25F812837BBBB002CFB2C /* SceneDelegate.swift in Sources */,
7A1A26492A29D48A00B978AA /* RelayFilterCellFactory.swift in Sources */,
- 5867771629097C5B006F721F /* ProductState.swift in Sources */,
F0D5591E2D38051C0072B63F /* LatestChangesNotificationProvider.swift in Sources */,
7A28826A2BA8336600FD9F20 /* VPNSettingsCoordinator.swift in Sources */,
7A6389DE2B7E3BD6008E77E1 /* CustomListItemIdentifier.swift in Sources */,
@@ -6521,6 +6576,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ A955EF1D2D43E5C50025E1B1 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
A992DA192C24709F00DE7CE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -6763,6 +6825,11 @@
target = 58D223D4294C8E5E0029F5F8 /* MullvadTypes */;
targetProxy = A9173C332C36CCFB00F6A08C /* PBXContainerItemProxy */;
};
+ A955EF262D43E5C50025E1B1 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 58CE5E5F224146200008646E /* MullvadVPN */;
+ targetProxy = A955EF252D43E5C50025E1B1 /* PBXContainerItemProxy */;
+ };
A9609B6E2D004D1F0065A3D3 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 58FBDA9722A519BC00EB69A3 /* WireGuardGoBridge */;
@@ -8412,6 +8479,107 @@
};
name = MockRelease;
};
+ A955EF272D43E5C50025E1B1 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ BUNDLE_LOADER = "";
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = CKG9MXH72F;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPNSendableTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 6.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "";
+ };
+ name = Debug;
+ };
+ A955EF282D43E5C50025E1B1 /* Staging */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ BUNDLE_LOADER = "";
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = CKG9MXH72F;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPNSendableTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 6.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "";
+ };
+ name = Staging;
+ };
+ A955EF292D43E5C50025E1B1 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ BUNDLE_LOADER = "";
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = CKG9MXH72F;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPNSendableTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 6.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "";
+ };
+ name = Release;
+ };
+ A955EF2A2D43E5C50025E1B1 /* MockRelease */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ BUNDLE_LOADER = "";
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = CKG9MXH72F;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPNSendableTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 6.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "";
+ };
+ name = MockRelease;
+ };
A992DA262C24709F00DE7CE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */;
@@ -9585,6 +9753,17 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ A955EF2B2D43E5C50025E1B1 /* Build configuration list for PBXNativeTarget "MullvadVPNSendableTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ A955EF272D43E5C50025E1B1 /* Debug */,
+ A955EF282D43E5C50025E1B1 /* Staging */,
+ A955EF292D43E5C50025E1B1 /* Release */,
+ A955EF2A2D43E5C50025E1B1 /* MockRelease */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
A992DA252C24709F00DE7CE5 /* Build configuration list for PBXNativeTarget "MullvadRustRuntime" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme
index 8e3306fc2b..ff88bf15a5 100644
--- a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme
+++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme
@@ -70,20 +70,6 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "063F02722902B63F001FA09F"
- BuildableName = "RelayCache.framework"
- BlueprintName = "RelayCache"
- ReferencedContainer = "container:MullvadVPN.xcodeproj">
- </BuildableReference>
- </BuildActionEntry>
- <BuildActionEntry
- buildForTesting = "YES"
- buildForRunning = "YES"
- buildForProfiling = "YES"
- buildForArchiving = "YES"
- buildForAnalyzing = "YES">
- <BuildableReference
- BuildableIdentifier = "primary"
BlueprintIdentifier = "58D223D4294C8E5E0029F5F8"
BuildableName = "MullvadTypes.framework"
BlueprintName = "MullvadTypes"
@@ -118,48 +104,6 @@
ReferencedContainer = "container:MullvadVPN.xcodeproj">
</BuildableReference>
</BuildActionEntry>
- <BuildActionEntry
- buildForTesting = "YES"
- buildForRunning = "YES"
- buildForProfiling = "YES"
- buildForArchiving = "YES"
- buildForAnalyzing = "YES">
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "5898D28829017BD300EB5EBA"
- BuildableName = "libTunnelProviderMessaging.a"
- BlueprintName = "TunnelProviderMessaging"
- ReferencedContainer = "container:MullvadVPN.xcodeproj">
- </BuildableReference>
- </BuildActionEntry>
- <BuildActionEntry
- buildForTesting = "YES"
- buildForRunning = "YES"
- buildForProfiling = "YES"
- buildForArchiving = "YES"
- buildForAnalyzing = "YES">
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "5898D29729017DAC00EB5EBA"
- BuildableName = "libRelaySelector.a"
- BlueprintName = "RelaySelector"
- ReferencedContainer = "container:MullvadVPN.xcodeproj">
- </BuildableReference>
- </BuildActionEntry>
- <BuildActionEntry
- buildForTesting = "YES"
- buildForRunning = "NO"
- buildForProfiling = "NO"
- buildForArchiving = "NO"
- buildForAnalyzing = "NO">
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "A98F1B4D2C19C48D003C869E"
- BuildableName = "MullvadPostQuantumTests.xctest"
- BlueprintName = "MullvadPostQuantumTests"
- ReferencedContainer = "container:MullvadVPN.xcodeproj">
- </BuildableReference>
- </BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
@@ -211,26 +155,6 @@
</BuildableReference>
</TestableReference>
<TestableReference
- skipped = "NO">
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "58D0C79223F1CE7000FE9BA7"
- BuildableName = "MullvadVPNScreenshots.xctest"
- BlueprintName = "MullvadVPNScreenshots"
- ReferencedContainer = "container:MullvadVPN.xcodeproj">
- </BuildableReference>
- </TestableReference>
- <TestableReference
- skipped = "NO">
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "58695A9C2A4ADA9100328DB3"
- BuildableName = "TunnelObfuscationTests.xctest"
- BlueprintName = "TunnelObfuscationTests"
- ReferencedContainer = "container:MullvadVPN.xcodeproj">
- </BuildableReference>
- </TestableReference>
- <TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
@@ -282,6 +206,17 @@
ReferencedContainer = "container:MullvadVPN.xcodeproj">
</BuildableReference>
</TestableReference>
+ <TestableReference
+ skipped = "NO"
+ parallelizable = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "A955EF202D43E5C50025E1B1"
+ BuildableName = "MullvadVPNSendableTests.xctest"
+ BlueprintName = "MullvadVPNSendableTests"
+ ReferencedContainer = "container:MullvadVPN.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
</Testables>
</TestAction>
<LaunchAction
diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPNSendableTests.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPNSendableTests.xcscheme
new file mode 100644
index 0000000000..10b2f4b13d
--- /dev/null
+++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPNSendableTests.xcscheme
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1620"
+ version = "1.7">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES"
+ buildArchitectures = "Automatic">
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ shouldAutocreateTestPlan = "YES">
+ <Testables>
+ <TestableReference
+ skipped = "NO"
+ parallelizable = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "A955EF202D43E5C50025E1B1"
+ BuildableName = "MullvadVPNSendableTests.xctest"
+ BlueprintName = "MullvadVPNSendableTests"
+ ReferencedContainer = "container:MullvadVPN.xcodeproj">
+ </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>
diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPNUITests.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPNUITests.xcscheme
index c7c011fb01..2dc3edef06 100644
--- a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPNUITests.xcscheme
+++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPNUITests.xcscheme
@@ -28,6 +28,17 @@
ReferencedContainer = "container:MullvadVPN.xcodeproj">
</BuildableReference>
</TestableReference>
+ <TestableReference
+ skipped = "NO"
+ parallelizable = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "A955EF202D43E5C50025E1B1"
+ BuildableName = "MullvadVPNSendableTests.xctest"
+ BlueprintName = "MullvadVPNSendableTests"
+ ReferencedContainer = "container:MullvadVPN.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
</Testables>
</TestAction>
<LaunchAction
diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/PacketTunnel.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/PacketTunnel.xcscheme
index e3d0f107cf..18e34fd94f 100644
--- a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/PacketTunnel.xcscheme
+++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/PacketTunnel.xcscheme
@@ -51,6 +51,19 @@
reference = "container:TestPlans/MullvadVPNCI.xctestplan">
</TestPlanReference>
</TestPlans>
+ <Testables>
+ <TestableReference
+ skipped = "NO"
+ parallelizable = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "A955EF202D43E5C50025E1B1"
+ BuildableName = "MullvadVPNSendableTests.xctest"
+ BlueprintName = "MullvadVPNSendableTests"
+ ReferencedContainer = "container:MullvadVPN.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@@ -80,6 +93,7 @@
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
+ askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
diff --git a/ios/MullvadVPNSendableTests/MullvadVPNSendableTests.swift b/ios/MullvadVPNSendableTests/MullvadVPNSendableTests.swift
new file mode 100644
index 0000000000..1a5c39e831
--- /dev/null
+++ b/ios/MullvadVPNSendableTests/MullvadVPNSendableTests.swift
@@ -0,0 +1,221 @@
+//
+// SendableTests.swift
+// MullvadVPNTests
+//
+// Created by Marco Nikic on 2025-01-24.
+// Copyright © 2025 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import Testing
+
+// Cannot be sendable unless marked `final`
+class NonSendable {
+ var name = "Do not send me"
+
+ func doNonSendableThings() {
+ print("really \(name)")
+ }
+
+ deinit {
+ print("bye \(name)")
+ }
+}
+
+struct SendableButNonCopyableValue: Sendable, ~Copyable {
+ let name = "Please send me senpai :3"
+}
+
+nonisolated(unsafe) var plainSendableTypeCounter = 0
+
+class PlainSendableType {
+ var name = "I have been sent here"
+
+ init() {
+ plainSendableTypeCounter += 1
+
+ print("I am instance #\(plainSendableTypeCounter) of PlainSendableType")
+ }
+
+ deinit {
+ print("Instance #\(plainSendableTypeCounter) of PlainSendableType has been terminated")
+ plainSendableTypeCounter -= 1
+ }
+}
+
+struct InvalidUse {
+ private let ns: NonSendable
+
+ init(nonSendable: NonSendable) {
+ self.ns = nonSendable
+ }
+
+ func sendValue() async -> sending NonSendable {
+ ns.doNonSendableThings()
+ // return ns // Sending 'self.ns' risks causing data races
+ return NonSendable()
+ }
+}
+
+struct ValidUse {
+ func sendValue() async -> sending NonSendable {
+ NonSendable()
+ }
+}
+
+struct SendableTests {
+ @Test func firstExample() async {
+ let nonSendable = NonSendable()
+ let invalid = InvalidUse(nonSendable: nonSendable)
+
+ let valid = ValidUse()
+
+ let invalidUse = await invalid.sendValue()
+ let validUse = await valid.sendValue()
+ print(invalidUse)
+ print(validUse)
+ }
+}
+
+struct ReceivingSendable {
+ func receiveSending(_ nonSendable: sending NonSendable) async {
+ print(nonSendable.name)
+ }
+
+ /// `borrowing + sending` is technically valid,
+ /// but the Language steering group decided to ban it for now, preferring internal compiler exclusivity
+ /// to preserve ABI compatibility
+ /// https://forums.swift.org/t/borrowing-sending-not-allowed/74711/2
+ func borrowSendingSendable(_ sendableValue: borrowing /* sending */ SendableButNonCopyableValue) async {
+ print(sendableValue.name)
+ }
+
+ func consumeSendingSendable(_ sendableValue: consuming sending SendableButNonCopyableValue) async {
+ print(sendableValue.name)
+ }
+
+ @Test
+ mutating func secondExample() async {
+ /// `nonSendable` is in a disconnected isolation region from `ReceivingSendable`, the current test instance
+ let nonSendable = NonSendable()
+
+ /// `nonSendable` is being sent into `ReceivingSendable`'s isolation region, it cannot be used anymore in the disconnected region
+ /// because that is a potential race condition
+ await receiveSending(nonSendable) // ❌ Sending 'nonSendable' risks causing data races
+ /// Uncommenting the next line will trigger the error in the comment above
+// nonSendable.name = "Invalid operation"
+
+ /// Is it okay to borrow `~Copyable & Sendable` values between isolated regions, no risk of race conditions occur
+ let sendableValue = SendableButNonCopyableValue() // ❌'sendableValue' used after consume
+ await borrowSendingSendable(sendableValue)
+ print(sendableValue.name)
+
+ /// However, even if a value is `Sendable`, if it is `~Copyable`
+ /// It cannot be used across different isolated regions once it is consumed
+ await consumeSendingSendable(sendableValue)
+ /// Uncommenting the next line triggers the error at L82
+// print(sendableValue.name)
+ }
+}
+
+class HasDelegate {
+ var borrowingDelegateCallback: ((borrowing SendableButNonCopyableValue) -> Void)?
+
+ var impossibleSendingDelegateCallback: ((consuming sending SendableButNonCopyableValue) -> Void)?
+
+ typealias CompletionHandler = (sending Result<NonSendable, Never>) -> Void
+
+ var completionHandler: CompletionHandler?
+
+ func callBorrowingDelegate() {
+ let argument = SendableButNonCopyableValue()
+ borrowingDelegateCallback?(argument)
+ }
+
+ func callSendingDelegate() {
+ let argument = SendableButNonCopyableValue()
+ impossibleSendingDelegateCallback?(argument)
+ }
+
+ func callCompletionHandler() {
+ let result: Result<NonSendable, Never> = Result.success(NonSendable())
+ completionHandler?(result) // ❌ Sending 'result' risks causing data races
+
+// _ = result.map { print($0) }
+ }
+}
+
+class HasDifferentDelegate {
+ typealias OtherCompletionHandler = () -> sending PlainSendableType
+
+ var otherHandler: OtherCompletionHandler?
+
+ let plainSendableType: PlainSendableType
+
+ init(plainSendableType: PlainSendableType) {
+ self.plainSendableType = plainSendableType
+ }
+
+ func callOtherHandler() {
+ let value = otherHandler!()
+ print(value.name)
+ }
+}
+
+class SendingClosures {
+ func extractNameFrom(_ argument: borrowing SendableButNonCopyableValue) -> String {
+ argument.name
+ }
+
+ func stealNameFrom(_ argument: consuming sending SendableButNonCopyableValue) -> String {
+ argument.name
+ }
+
+ @Test func sendCannotBorrowAndConsume() {
+ let hasDelegate = HasDelegate()
+
+ hasDelegate.borrowingDelegateCallback = { argument in // ❌ 'argument' is borrowed and cannot be consumed
+ let borrowedName = self.extractNameFrom(argument)
+ print(borrowedName)
+
+// let stolenName = self.stealNameFrom(argument)
+// print(stolenName)
+ }
+
+ hasDelegate.callBorrowingDelegate()
+ }
+
+ @Test func nonsenseSendingCallback() {
+ let hasDelegate = HasDelegate()
+
+ hasDelegate.impossibleSendingDelegateCallback = { _ in // ❌ 'argument' is borrowed and cannot be consumed
+// let stolenName = self.stealNameFrom(argument)
+// print(stolenName)
+ }
+
+ hasDelegate.callSendingDelegate()
+ }
+
+ @Test func sendCustomCallback() {
+ let hasDelegate = HasDelegate()
+
+ hasDelegate.completionHandler = { maybeResult in
+ print(maybeResult.get().name)
+ }
+
+ hasDelegate.callCompletionHandler()
+ }
+
+ @Test func sendCustomCallbackDifferently() {
+ let plainType = PlainSendableType()
+ let hasDelegate = HasDifferentDelegate(plainSendableType: plainType)
+
+ hasDelegate.otherHandler = {
+// return plainType // ❌ Sending 'plainType' risks causing data races
+ PlainSendableType()
+ }
+
+ plainType.name = "hello"
+ hasDelegate.callOtherHandler()
+ }
+}
diff --git a/ios/TestPlans/MullvadVPNApp.xctestplan b/ios/TestPlans/MullvadVPNApp.xctestplan
index 5a849a2e94..70e5780c9e 100644
--- a/ios/TestPlans/MullvadVPNApp.xctestplan
+++ b/ios/TestPlans/MullvadVPNApp.xctestplan
@@ -9,6 +9,7 @@
}
],
"defaultOptions" : {
+ "codeCoverage" : false,
"targetForVariableExpansion" : {
"containerPath" : "container:MullvadVPN.xcodeproj",
"identifier" : "58CE5E5F224146200008646E",
@@ -21,16 +22,22 @@
"parallelizable" : true,
"target" : {
"containerPath" : "container:MullvadVPN.xcodeproj",
- "identifier" : "58FBFBE5291622580020E046",
- "name" : "MullvadRESTTests"
+ "identifier" : "58C7A43C2A863F450060C66F",
+ "name" : "PacketTunnelCoreTests"
}
},
{
- "parallelizable" : true,
"target" : {
"containerPath" : "container:MullvadVPN.xcodeproj",
- "identifier" : "58B0A29F238EE67E00BC001D",
- "name" : "MullvadVPNTests"
+ "identifier" : "A9D9A4BF2C36D53C004088DD",
+ "name" : "MullvadRustRuntimeTests"
+ }
+ },
+ {
+ "target" : {
+ "containerPath" : "container:MullvadVPN.xcodeproj",
+ "identifier" : "A955EF202D43E5C50025E1B1",
+ "name" : "MullvadVPNSendableTests"
}
},
{
@@ -45,23 +52,24 @@
"parallelizable" : true,
"target" : {
"containerPath" : "container:MullvadVPN.xcodeproj",
- "identifier" : "58C7A43C2A863F450060C66F",
- "name" : "PacketTunnelCoreTests"
+ "identifier" : "58B0A29F238EE67E00BC001D",
+ "name" : "MullvadVPNTests"
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:MullvadVPN.xcodeproj",
- "identifier" : "7A88DCD62A8FABBE00D2FF0E",
- "name" : "RoutingTests"
+ "identifier" : "58FBFBE5291622580020E046",
+ "name" : "MullvadRESTTests"
}
},
{
+ "parallelizable" : true,
"target" : {
"containerPath" : "container:MullvadVPN.xcodeproj",
- "identifier" : "A9D9A4BF2C36D53C004088DD",
- "name" : "MullvadRustRuntimeTests"
+ "identifier" : "7A88DCD62A8FABBE00D2FF0E",
+ "name" : "RoutingTests"
}
}
],