summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2025-11-10 16:00:57 +0100
committerBug Magnet <marco.nikic@mullvad.net>2025-11-10 16:00:57 +0100
commit869b59e08c224f0d7e6300b9c0715a76df5ad3d9 (patch)
treed10a27825d4604c2f2dcf61f9019c07651ca5216
parent206775e7e641cf5098d29241e672689010f449ea (diff)
parentb0a04e382faa3b1b63b6c503bb66485ac9b9424c (diff)
downloadmullvadvpn-confirm-ipv6-external-source-for-flakiness.tar.xz
mullvadvpn-confirm-ipv6-external-source-for-flakiness.zip
Merge branch 'bump-min-deployment-target-ios-1374'confirm-ipv6-external-source-for-flakiness
-rw-r--r--ios/CHANGELOG.md1
-rw-r--r--ios/MullvadREST/RetryStrategy/ExponentialBackoff.swift11
-rw-r--r--ios/MullvadRESTTests/ExponentialBackoffTests.swift6
-rw-r--r--ios/MullvadTypes/Duration+Extensions.swift17
-rw-r--r--ios/MullvadTypes/Duration.swift93
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj116
-rw-r--r--ios/MullvadVPN/Classes/AutomaticKeyboardResponder.swift35
-rw-r--r--ios/MullvadVPN/Containers/Root/RootContainerViewController.swift2
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodView.swift8
-rw-r--r--ios/MullvadVPN/Extensions/View+Modifier.swift10
-rw-r--r--ios/MullvadVPN/GeneralAPIs/OutgoingConnectionProxy.swift26
-rw-r--r--ios/MullvadVPN/Presentation controllers/FormsheetPresentationController.swift12
-rw-r--r--ios/MullvadVPN/View controllers/RelayFilter/ChipFlowLayout.swift4
-rw-r--r--ios/MullvadVPN/View controllers/RevokedDevice/RevokedDeviceView.swift9
-rw-r--r--ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift2
-rw-r--r--ios/MullvadVPN/View controllers/TermsOfService/TermsOfServiceView.swift7
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionView.swift12
-rw-r--r--ios/MullvadVPN/View controllers/VPNSettings/CustomDNSViewController.swift6
-rw-r--r--ios/MullvadVPN/Views/List/MullvadListNavigationItemView.swift8
-rw-r--r--ios/MullvadVPNTests/MullvadTypes/DurationTests.swift72
-rw-r--r--ios/MullvadVPNUITests/Screenshots/SnapshotHelper.swift14
-rw-r--r--ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift4
-rw-r--r--ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift4
-rw-r--r--ios/PacketTunnelCore/Actor/Task+Duration.swift67
-rw-r--r--ios/PacketTunnelCoreTests/TaskSleepTests.swift57
-rw-r--r--ios/Shared/ApplicationLanguage.swift27
26 files changed, 173 insertions, 457 deletions
diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md
index 09e7fae1e3..9b983220ba 100644
--- a/ios/CHANGELOG.md
+++ b/ios/CHANGELOG.md
@@ -24,6 +24,7 @@ Line wrap the file at 100 chars. Th
## UNRELEASED
### Changed
+- Bump minimum version to iOS 17.0
- Quantum-resistant tunnel setting is now on by default through the "Automatic" setting.
- Add a checkbox that lets users include their account token in problem reports.
diff --git a/ios/MullvadREST/RetryStrategy/ExponentialBackoff.swift b/ios/MullvadREST/RetryStrategy/ExponentialBackoff.swift
index 8505e092b2..349716e0ea 100644
--- a/ios/MullvadREST/RetryStrategy/ExponentialBackoff.swift
+++ b/ios/MullvadREST/RetryStrategy/ExponentialBackoff.swift
@@ -23,12 +23,19 @@ struct ExponentialBackoff: IteratorProtocol {
mutating func next() -> Duration? {
let next = _next
- if next > maxDelay {
+ if next >= maxDelay {
return maxDelay
}
- _next = next * Int(multiplier)
+ let nextMilliseconds = next.milliseconds
+ let maxMilliseconds = maxDelay.milliseconds
+ let (value, overflow) = nextMilliseconds.multipliedReportingOverflow(by: Int(multiplier))
+ if overflow {
+ _next = .milliseconds(maxMilliseconds)
+ } else {
+ _next = .milliseconds(value)
+ }
return next
}
}
diff --git a/ios/MullvadRESTTests/ExponentialBackoffTests.swift b/ios/MullvadRESTTests/ExponentialBackoffTests.swift
index 668d33ddc5..5b8cd88290 100644
--- a/ios/MullvadRESTTests/ExponentialBackoffTests.swift
+++ b/ios/MullvadRESTTests/ExponentialBackoffTests.swift
@@ -23,9 +23,9 @@ final class ExponentialBackoffTests: XCTestCase {
func testAtMaximumValue() {
var backoff = ExponentialBackoff(initial: .milliseconds(.max - 1), multiplier: 2, maxDelay: .seconds(.max - 1))
- XCTAssertEqual(backoff.next(), .milliseconds(.max - 1))
- XCTAssertEqual(backoff.next(), .milliseconds(.max))
- XCTAssertEqual(backoff.next(), .milliseconds(.max))
+ XCTAssertEqual(backoff.next(), .milliseconds(Int.max - 1))
+ XCTAssertEqual(backoff.next(), .milliseconds(Int.max))
+ XCTAssertEqual(backoff.next(), .milliseconds(Int.max))
}
func testMaximumBound() {
diff --git a/ios/MullvadTypes/Duration+Extensions.swift b/ios/MullvadTypes/Duration+Extensions.swift
index e01fa4f883..38c70943a2 100644
--- a/ios/MullvadTypes/Duration+Extensions.swift
+++ b/ios/MullvadTypes/Duration+Extensions.swift
@@ -77,4 +77,21 @@ extension Duration {
public static func > (lhs: TimeInterval, rhs: Duration) -> Bool {
return lhs > rhs.timeInterval
}
+
+ public func logFormat() -> String {
+ let timeInterval = timeInterval
+
+ guard timeInterval >= 1 else {
+ return "\(milliseconds)ms"
+ }
+
+ let trailingZeroesSuffix = ".00"
+ var string = String(format: "%.2f", timeInterval)
+
+ if string.hasSuffix(trailingZeroesSuffix) {
+ string.removeLast(trailingZeroesSuffix.count)
+ }
+
+ return "\(string)s"
+ }
}
diff --git a/ios/MullvadTypes/Duration.swift b/ios/MullvadTypes/Duration.swift
deleted file mode 100644
index 8beeeaa321..0000000000
--- a/ios/MullvadTypes/Duration.swift
+++ /dev/null
@@ -1,93 +0,0 @@
-//
-// Duration.swift
-// MullvadTypes
-//
-// Created by Jon Petersson on 2023-08-16.
-// Copyright © 2025 Mullvad VPN AB. All rights reserved.
-//
-
-import Foundation
-
-/// Custom implementation of iOS native `Duration` (available from iOS16). Meant as a
-/// drop-in replacement until the app supports iOS16. Ideally this whole file can
-/// then be deleted without affecting the rest of the code base.
-@available(iOS, introduced: 15.0, obsoleted: 16.0, message: "Replace with native Duration type.")
-public struct Duration {
- private(set) var components: (seconds: Int64, attoseconds: Int64)
-
- public init(secondsComponent: Int64, attosecondsComponent: Int64 = 0) {
- components = (
- seconds: Int64(secondsComponent),
- attoseconds: Int64(attosecondsComponent)
- )
- }
-
- public static func milliseconds(_ milliseconds: Int) -> Duration {
- let subSeconds = milliseconds % 1000
- let seconds = (milliseconds - subSeconds) / 1000
-
- return Duration(
- secondsComponent: Int64(seconds),
- attosecondsComponent: Int64(subSeconds) * Int64(1e15)
- )
- }
-
- public static func seconds(_ seconds: Int) -> Duration {
- return Duration(secondsComponent: Int64(seconds))
- }
-
- public func logFormat() -> String {
- let timeInterval = timeInterval
-
- guard timeInterval >= 1 else {
- return "\(milliseconds)ms"
- }
-
- let trailingZeroesSuffix = ".00"
- var string = String(format: "%.2f", timeInterval)
-
- if string.hasSuffix(trailingZeroesSuffix) {
- string.removeLast(trailingZeroesSuffix.count)
- }
-
- return "\(string)s"
- }
-}
-
-extension Duration: DurationProtocol {
- public static var zero: Duration {
- return .seconds(0)
- }
-
- public static func / (lhs: Duration, rhs: Int) -> Duration {
- return .milliseconds(lhs.milliseconds / max(rhs, 1))
- }
-
- public static func * (lhs: Duration, rhs: Int) -> Duration {
- return .milliseconds(lhs.milliseconds.saturatingMultiplication(rhs))
- }
-
- public static func / (lhs: Duration, rhs: Duration) -> Double {
- guard rhs != .zero else {
- return lhs.timeInterval
- }
-
- return lhs.timeInterval / rhs.timeInterval
- }
-
- public static func + (lhs: Duration, rhs: Duration) -> Duration {
- return .milliseconds(lhs.milliseconds.saturatingAddition(rhs.milliseconds))
- }
-
- public static func - (lhs: Duration, rhs: Duration) -> Duration {
- return .milliseconds(lhs.milliseconds.saturatingSubtraction(rhs.milliseconds))
- }
-
- public static func < (lhs: Duration, rhs: Duration) -> Bool {
- return lhs.timeInterval < rhs.timeInterval
- }
-
- public static func == (lhs: Duration, rhs: Duration) -> Bool {
- return lhs.timeInterval == rhs.timeInterval
- }
-}
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index dbcb0d37b3..94d4071706 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -142,7 +142,6 @@
58342C042AAB61FB003BA12D /* State+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58342C032AAB61FB003BA12D /* State+Extensions.swift */; };
5835B7CC233B76CB0096D79F /* TunnelManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5835B7CB233B76CB0096D79F /* TunnelManager.swift */; };
5838321B2AC1B18400EA2071 /* PacketTunnelActor+Mocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5838321A2AC1B18400EA2071 /* PacketTunnelActor+Mocks.swift */; };
- 5838321D2AC1C54600EA2071 /* TaskSleepTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5838321C2AC1C54600EA2071 /* TaskSleepTests.swift */; };
5838321F2AC3160A00EA2071 /* PacketTunnelActor+KeyPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5838321E2AC3160A00EA2071 /* PacketTunnelActor+KeyPolicy.swift */; };
583832232AC3181400EA2071 /* PacketTunnelActor+ErrorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583832222AC3181400EA2071 /* PacketTunnelActor+ErrorState.swift */; };
583832252AC318A100EA2071 /* PacketTunnelActor+ConnectionMonitoring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583832242AC318A100EA2071 /* PacketTunnelActor+ConnectionMonitoring.swift */; };
@@ -194,7 +193,6 @@
586C14582AC463BB00245C01 /* EventChannelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C14572AC463BB00245C01 /* EventChannelTests.swift */; };
586C145A2AC4735F00245C01 /* PacketTunnelActor+Public.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C14592AC4735F00245C01 /* PacketTunnelActor+Public.swift */; };
586E54FB27A2DF6D0029B88B /* SendTunnelProviderMessageOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586E54FA27A2DF6D0029B88B /* SendTunnelProviderMessageOperation.swift */; };
- 586E8DB82AAF4AC4007BF3DA /* Task+Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586E8DB72AAF4AC4007BF3DA /* Task+Duration.swift */; };
5871167F2910035700D41AAC /* VPNSettingsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5871167E2910035700D41AAC /* VPNSettingsInteractor.swift */; };
5871FB96254ADE4E0051A0A4 /* ConsolidatedApplicationLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5871FB95254ADE4E0051A0A4 /* ConsolidatedApplicationLog.swift */; };
5871FBA0254C26C00051A0A4 /* NSRegularExpression+IPAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5871FB9F254C26BF0051A0A4 /* NSRegularExpression+IPAddress.swift */; };
@@ -494,7 +492,6 @@
7A2E7B722D6C9FE5009EF2C3 /* APIRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2E7B6C2D6C9E53009EF2C3 /* APIRequest.swift */; };
7A2E7B732D6C9FEB009EF2C3 /* APIRequestProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A95B6742D5DF86400687524 /* APIRequestProxy.swift */; };
7A2E7B752D6CA0B1009EF2C3 /* APITransportProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2E7B742D6CA0AC009EF2C3 /* APITransportProvider.swift */; };
- 7A307AD92A8CD8DA0017618B /* Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A307AD82A8CD8DA0017618B /* Duration.swift */; };
7A307ADB2A8F56DF0017618B /* Duration+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A307ADA2A8F56DF0017618B /* Duration+Extensions.swift */; };
7A3215722D3934E6005DF395 /* MullvadApiCompletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3215702D392F0B005DF395 /* MullvadApiCompletion.swift */; };
7A33538F2AA9FF1600F0A71C /* SimulatorTunnelProviderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A33538E2AA9FF1600F0A71C /* SimulatorTunnelProviderManager.swift */; };
@@ -885,7 +882,6 @@
A9A5FA2A2ACB05160083449F /* CoordinatesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EC20E72A5D3A8C0040D56E /* CoordinatesTests.swift */; };
A9A5FA2B2ACB05160083449F /* CustomDateComponentsFormattingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5896AE85246D6AD8005B36CB /* CustomDateComponentsFormattingTests.swift */; };
A9A5FA2C2ACB05160083449F /* DeviceCheckOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58915D622A25F8400066445B /* DeviceCheckOperationTests.swift */; };
- A9A5FA2D2ACB05160083449F /* DurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FBFBF0291630700020E046 /* DurationTests.swift */; };
A9A5FA2E2ACB05160083449F /* FileCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C3FA672A385C89006A450A /* FileCacheTests.swift */; };
A9A5FA2F2ACB05160083449F /* FixedWidthIntegerArithmeticsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582A8A3928BCE19B00D0F9FB /* FixedWidthIntegerArithmeticsTests.swift */; };
A9A5FA302ACB05160083449F /* InputTextFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F07BF2572A26112D00042943 /* InputTextFormatterTests.swift */; };
@@ -1761,7 +1757,6 @@
58342C032AAB61FB003BA12D /* State+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "State+Extensions.swift"; sourceTree = "<group>"; };
5835B7CB233B76CB0096D79F /* TunnelManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelManager.swift; sourceTree = "<group>"; };
5838321A2AC1B18400EA2071 /* PacketTunnelActor+Mocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PacketTunnelActor+Mocks.swift"; sourceTree = "<group>"; };
- 5838321C2AC1C54600EA2071 /* TaskSleepTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskSleepTests.swift; sourceTree = "<group>"; };
5838321E2AC3160A00EA2071 /* PacketTunnelActor+KeyPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PacketTunnelActor+KeyPolicy.swift"; sourceTree = "<group>"; };
583832222AC3181400EA2071 /* PacketTunnelActor+ErrorState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PacketTunnelActor+ErrorState.swift"; sourceTree = "<group>"; };
583832242AC318A100EA2071 /* PacketTunnelActor+ConnectionMonitoring.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PacketTunnelActor+ConnectionMonitoring.swift"; sourceTree = "<group>"; };
@@ -1832,7 +1827,6 @@
586C14592AC4735F00245C01 /* PacketTunnelActor+Public.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PacketTunnelActor+Public.swift"; sourceTree = "<group>"; };
586E54FA27A2DF6D0029B88B /* SendTunnelProviderMessageOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendTunnelProviderMessageOperation.swift; sourceTree = "<group>"; };
586E7A2C2A987689006DAB1B /* SettingsReaderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsReaderProtocol.swift; sourceTree = "<group>"; };
- 586E8DB72AAF4AC4007BF3DA /* Task+Duration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Task+Duration.swift"; sourceTree = "<group>"; };
5871167E2910035700D41AAC /* VPNSettingsInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNSettingsInteractor.swift; sourceTree = "<group>"; };
5871FB95254ADE4E0051A0A4 /* ConsolidatedApplicationLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsolidatedApplicationLog.swift; sourceTree = "<group>"; };
5871FB9F254C26BF0051A0A4 /* NSRegularExpression+IPAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSRegularExpression+IPAddress.swift"; sourceTree = "<group>"; };
@@ -2033,7 +2027,6 @@
58FB865926EA214400F188BC /* RelayCacheTrackerObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayCacheTrackerObserver.swift; sourceTree = "<group>"; };
58FBFBE6291622580020E046 /* MullvadRESTTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MullvadRESTTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
58FBFBE8291622580020E046 /* ExponentialBackoffTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExponentialBackoffTests.swift; sourceTree = "<group>"; };
- 58FBFBF0291630700020E046 /* DurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DurationTests.swift; sourceTree = "<group>"; };
58FC040927B3EE03001C21F0 /* TunnelMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitor.swift; sourceTree = "<group>"; };
58FD5BEF24238EB300112C88 /* SKProduct+Formatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SKProduct+Formatting.swift"; sourceTree = "<group>"; };
58FD5BF32428C67600112C88 /* InAppPurchaseButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppPurchaseButton.swift; sourceTree = "<group>"; };
@@ -2083,7 +2076,6 @@
7A2E7B6C2D6C9E53009EF2C3 /* APIRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIRequest.swift; sourceTree = "<group>"; };
7A2E7B6E2D6C9ED9009EF2C3 /* APIError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIError.swift; sourceTree = "<group>"; };
7A2E7B742D6CA0AC009EF2C3 /* APITransportProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APITransportProvider.swift; sourceTree = "<group>"; };
- 7A307AD82A8CD8DA0017618B /* Duration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Duration.swift; sourceTree = "<group>"; };
7A307ADA2A8F56DF0017618B /* Duration+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Duration+Extensions.swift"; sourceTree = "<group>"; };
7A3215702D392F0B005DF395 /* MullvadApiCompletion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MullvadApiCompletion.swift; sourceTree = "<group>"; };
7A33538E2AA9FF1600F0A71C /* SimulatorTunnelProviderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatorTunnelProviderManager.swift; sourceTree = "<group>"; };
@@ -2908,7 +2900,6 @@
isa = PBXGroup;
children = (
7AF36A992CA2964000E1D497 /* AnyIPAddressTests.swift */,
- 58FBFBF0291630700020E046 /* DurationTests.swift */,
58C3FA672A385C89006A450A /* FileCacheTests.swift */,
582A8A3928BCE19B00D0F9FB /* FixedWidthIntegerArithmeticsTests.swift */,
58C3FA652A38549D006A450A /* MockFileCache.swift */,
@@ -3084,7 +3075,6 @@
06AC113628F83FD70037AF9A /* Cancellable.swift */,
58E511E328DDDE8900B0BCDE /* CustomErrorDescriptionProtocol.swift */,
586168682976F6BD00EF8598 /* DisplayError.swift */,
- 7A307AD82A8CD8DA0017618B /* Duration.swift */,
7A307ADA2A8F56DF0017618B /* Duration+Extensions.swift */,
58E511EA28DDE18400B0BCDE /* Error+Chain.swift */,
A9A8A8EA2A262AB30086D569 /* FileCache.swift */,
@@ -3620,7 +3610,6 @@
58ED3A132A7C199C0085CE65 /* StartOptions.swift */,
5824030C2A811B0000163DE8 /* State.swift */,
58342C032AAB61FB003BA12D /* State+Extensions.swift */,
- 586E8DB72AAF4AC4007BF3DA /* Task+Duration.swift */,
58DDA18E2ABC32380039C360 /* Timings.swift */,
F04DD3D72C130DF600E03E28 /* TunnelSettingsManager.swift */,
);
@@ -3891,7 +3880,6 @@
58FE25D32AA729B5003D1918 /* PacketTunnelActorTests.swift */,
A97D25B12B0CB02D00946B2D /* ProtocolObfuscatorTests.swift */,
F0A163882C47B46300592300 /* SingleHopEphemeralPeerExchangerTests.swift */,
- 5838321C2AC1C54600EA2071 /* TaskSleepTests.swift */,
58092E532A8B832E00C3CC72 /* TunnelMonitorTests.swift */,
F062B94C2C16E09700B6D47A /* TunnelSettingsManagerTests.swift */,
);
@@ -6201,7 +6189,6 @@
A9A5FA2B2ACB05160083449F /* CustomDateComponentsFormattingTests.swift in Sources */,
440870842D809C980038972F /* UIImage+Helpers.swift in Sources */,
A9A5FA2C2ACB05160083449F /* DeviceCheckOperationTests.swift in Sources */,
- A9A5FA2D2ACB05160083449F /* DurationTests.swift in Sources */,
A9A5FA2E2ACB05160083449F /* FileCacheTests.swift in Sources */,
F0FA16112D7F2FE8007E2546 /* RelayFilterViewModel.swift in Sources */,
F0D5591F2D38051C0072B63F /* LatestChangesNotificationProvider.swift in Sources */,
@@ -6322,7 +6309,6 @@
580D6B8A2AB31AB400B2D6E0 /* NetworkPath+NetworkReachability.swift in Sources */,
7AD0AA1D2AD6A86700119E10 /* PacketTunnelActorProtocol.swift in Sources */,
5826B6CB2ABD83E200B1CA13 /* PacketTunnelOptions.swift in Sources */,
- 586E8DB82AAF4AC4007BF3DA /* Task+Duration.swift in Sources */,
5838322B2AC3EF9600EA2071 /* EventChannel.swift in Sources */,
586C145A2AC4735F00245C01 /* PacketTunnelActor+Public.swift in Sources */,
F0DAC8AD2C16EFE400F80144 /* TunnelSettingsManager.swift in Sources */,
@@ -6353,7 +6339,6 @@
581F23AD2A8CF92100788AB6 /* DefaultPathObserverFake.swift in Sources */,
F07751582C50F149006E6A12 /* MultiHopEphemeralPeerExchangerTests.swift in Sources */,
5838321B2AC1B18400EA2071 /* PacketTunnelActor+Mocks.swift in Sources */,
- 5838321D2AC1C54600EA2071 /* TaskSleepTests.swift in Sources */,
58092E542A8B832E00C3CC72 /* TunnelMonitorTests.swift in Sources */,
7AD0AA212AD6CB0000119E10 /* URLRequestProxyStub.swift in Sources */,
581F23AF2A8CF94D00788AB6 /* PingerMock.swift in Sources */,
@@ -6875,7 +6860,6 @@
A97FF5502A0D2FFC00900996 /* NSFileCoordinator+Extensions.swift in Sources */,
58D22408294C90210029F5F8 /* AnyIPEndpoint.swift in Sources */,
58D22409294C90210029F5F8 /* AnyIPAddress.swift in Sources */,
- 7A307AD92A8CD8DA0017618B /* Duration.swift in Sources */,
58D2240A294C90210029F5F8 /* IPAddress+Codable.swift in Sources */,
58E45A5729F12C5100281ECF /* Result+Extensions.swift in Sources */,
F924C5A42DA65F28001F4660 /* Storekit2.swift in Sources */,
@@ -7396,6 +7380,7 @@
INFOPLIST_FILE = MullvadREST/Info.plist;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -7434,6 +7419,7 @@
INFOPLIST_FILE = MullvadREST/Info.plist;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -7465,6 +7451,7 @@
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.OperationsTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -7486,6 +7473,7 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
"DEVELOPMENT_TEAM[sdk=macosx*]" = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.OperationsTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -7502,6 +7490,7 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
INFOPLIST_FILE = MullvadVPNTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -7522,6 +7511,7 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
INFOPLIST_FILE = MullvadVPNTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -7552,6 +7542,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -7589,6 +7580,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -7627,6 +7619,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -7667,6 +7660,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -7698,6 +7692,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.PacketTunnelCoreTests;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -7719,6 +7714,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.PacketTunnelCoreTests;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -7783,7 +7779,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
@@ -7846,7 +7842,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
@@ -7877,6 +7873,7 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "MullvadVPN/Supporting Files/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -7905,6 +7902,7 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "MullvadVPN/Supporting Files/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -7925,6 +7923,7 @@
CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = PacketTunnel/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -7946,6 +7945,7 @@
CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = PacketTunnel/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -7974,6 +7974,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8009,6 +8010,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8043,6 +8045,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8079,6 +8082,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8114,6 +8118,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8150,6 +8155,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8209,6 +8215,7 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
"DEVELOPMENT_TEAM[sdk=macosx*]" = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -8228,6 +8235,7 @@
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -8260,6 +8268,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8300,6 +8309,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8333,7 +8343,7 @@
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.RoutingTests;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -8358,7 +8368,7 @@
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.RoutingTests;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -8394,7 +8404,7 @@
GENERATE_INFOPLIST_FILE = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/mullvad-api/include";
INFOPLIST_FILE = MullvadVPNUITests/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/debug";
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/debug";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
@@ -8428,7 +8438,7 @@
GENERATE_INFOPLIST_FILE = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/mullvad-api/include";
INFOPLIST_FILE = MullvadVPNUITests/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/release";
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/release";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
@@ -8499,7 +8509,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
@@ -8533,6 +8543,7 @@
"DEBUG=1",
);
INFOPLIST_FILE = "MullvadVPN/Supporting Files/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8572,6 +8583,7 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = PacketTunnel/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8592,6 +8604,7 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
INFOPLIST_FILE = MullvadVPNTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8622,6 +8635,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8650,6 +8664,7 @@
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.OperationsTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -8677,6 +8692,7 @@
INFOPLIST_FILE = MullvadREST/Info.plist;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8708,6 +8724,7 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
"DEVELOPMENT_TEAM[sdk=macosx*]" = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -8734,6 +8751,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8769,6 +8787,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8805,6 +8824,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8836,6 +8856,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.PacketTunnelCoreTests;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -8864,6 +8885,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8907,6 +8929,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -8940,7 +8963,7 @@
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.RoutingTests;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -8965,7 +8988,7 @@
GENERATE_INFOPLIST_FILE = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../mullvad-api/include";
INFOPLIST_FILE = MullvadVPNUITests/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/debug";
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/debug";
PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadVPNUITests";
@@ -8991,7 +9014,7 @@
GENERATE_INFOPLIST_FILE = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../mullvad-api/include";
INFOPLIST_FILE = MullvadVPNUITests/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/release";
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/release";
PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadVPNUITests";
@@ -9030,7 +9053,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9082,7 +9105,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9134,7 +9157,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9184,7 +9207,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9223,7 +9246,7 @@
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadRustRuntimeTests;
@@ -9249,7 +9272,7 @@
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadRustRuntimeTests;
@@ -9274,7 +9297,7 @@
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadRustRuntimeTests;
@@ -9300,7 +9323,7 @@
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadRustRuntimeTests;
@@ -9361,7 +9384,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
@@ -9391,6 +9414,7 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "MullvadVPN/Supporting Files/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9426,6 +9450,7 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = PacketTunnel/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9445,6 +9470,7 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
INFOPLIST_FILE = MullvadVPNTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9475,6 +9501,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9503,6 +9530,7 @@
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.OperationsTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -9530,6 +9558,7 @@
INFOPLIST_FILE = MullvadREST/Info.plist;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9561,6 +9590,7 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
"DEVELOPMENT_TEAM[sdk=macosx*]" = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -9587,6 +9617,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9622,6 +9653,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9658,6 +9690,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9689,6 +9722,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.PacketTunnelCoreTests;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -9717,6 +9751,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9757,6 +9792,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9790,7 +9826,7 @@
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
GENERATE_INFOPLIST_FILE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.RoutingTests;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -9825,7 +9861,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9873,7 +9909,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9921,7 +9957,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -9970,7 +10006,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mullvad VPN AB. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
diff --git a/ios/MullvadVPN/Classes/AutomaticKeyboardResponder.swift b/ios/MullvadVPN/Classes/AutomaticKeyboardResponder.swift
index 00b83d3ed5..9de1a7827d 100644
--- a/ios/MullvadVPN/Classes/AutomaticKeyboardResponder.swift
+++ b/ios/MullvadVPN/Classes/AutomaticKeyboardResponder.swift
@@ -49,34 +49,23 @@ class AutomaticKeyboardResponder {
guard let userInfo = notification.userInfo,
let targetView
else { return }
- // In iOS 16.1 and later, the keyboard notification object is the screen the keyboard appears on.
- if #available(iOS 16.1, *) {
- guard let screen = notification.object as? UIScreen,
- // Get the keyboard’s frame at the end of its animation.
- let keyboardFrameEnd = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
- else { return }
-
- // Use that screen to get the coordinate space to convert from.
- let fromCoordinateSpace = screen.coordinateSpace
+ guard let screen = notification.object as? UIScreen,
+ // Get the keyboard’s frame at the end of its animation.
+ let keyboardFrameEnd = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
+ else { return }
- // Get your view's coordinate space.
- let toCoordinateSpace: UICoordinateSpace = targetView
+ // Use that screen to get the coordinate space to convert from.
+ let fromCoordinateSpace = screen.coordinateSpace
- // Convert the keyboard's frame from the screen's coordinate space to your view's coordinate space.
- let convertedKeyboardFrameEnd = fromCoordinateSpace.convert(keyboardFrameEnd, to: toCoordinateSpace)
+ // Get your view's coordinate space.
+ let toCoordinateSpace: UICoordinateSpace = targetView
- lastKeyboardRect = convertedKeyboardFrameEnd
+ // Convert the keyboard's frame from the screen's coordinate space to your view's coordinate space.
+ let convertedKeyboardFrameEnd = fromCoordinateSpace.convert(keyboardFrameEnd, to: toCoordinateSpace)
- adjustContentInsets(convertedKeyboardFrameEnd: convertedKeyboardFrameEnd)
- } else {
- guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue
- else { return }
- let keyboardFrameEnd = keyboardValue.cgRectValue
- let convertedKeyboardFrameEnd = targetView.convert(keyboardFrameEnd, from: targetView.window)
- lastKeyboardRect = convertedKeyboardFrameEnd
+ lastKeyboardRect = convertedKeyboardFrameEnd
- adjustContentInsets(convertedKeyboardFrameEnd: convertedKeyboardFrameEnd)
- }
+ adjustContentInsets(convertedKeyboardFrameEnd: convertedKeyboardFrameEnd)
}
private func adjustContentInsets(convertedKeyboardFrameEnd: CGRect) {
diff --git a/ios/MullvadVPN/Containers/Root/RootContainerViewController.swift b/ios/MullvadVPN/Containers/Root/RootContainerViewController.swift
index 9f9f050797..8458521da0 100644
--- a/ios/MullvadVPN/Containers/Root/RootContainerViewController.swift
+++ b/ios/MullvadVPN/Containers/Root/RootContainerViewController.swift
@@ -753,7 +753,7 @@ class RootContainerViewController: UIViewController {
// Tell UIKit to update the interface orientation
if attemptRotateToDeviceOrientation {
- Self.attemptRotationToDeviceOrientation()
+ setNeedsUpdateOfSupportedInterfaceOrientations()
}
}
}
diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodView.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodView.swift
index b6c305e338..8eef58257e 100644
--- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodView.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodView.swift
@@ -86,13 +86,7 @@ struct ListAccessMethodView<ViewModel>: View where ViewModel: ListAccessViewMode
.accessibilityIdentifier(
AccessibilityIdentifier.apiAccessListView.asString
)
- .apply {
- if #available(iOS 16.4, *) {
- $0.scrollBounceBehavior(.basedOnSize)
- } else {
- $0
- }
- }
+ .scrollBounceBehavior(.basedOnSize)
Spacer()
}
.background(Color.mullvadBackground)
diff --git a/ios/MullvadVPN/Extensions/View+Modifier.swift b/ios/MullvadVPN/Extensions/View+Modifier.swift
index 7cb201f01f..73f59e1e7c 100644
--- a/ios/MullvadVPN/Extensions/View+Modifier.swift
+++ b/ios/MullvadVPN/Extensions/View+Modifier.swift
@@ -11,16 +11,6 @@ import SwiftUI
extension View {
/**
A view modifier that can be used to conditionally apply other view modifiers.
- # Example #
- ```
- .apply {
- if #available(iOS 16.4, *) {
- $0.scrollBounceBehavior(.basedOnSize)
- } else {
- $0
- }
- }
- ```
*/
func apply<V: View>(@ViewBuilder _ block: (Self) -> V) -> V { block(self) }
diff --git a/ios/MullvadVPN/GeneralAPIs/OutgoingConnectionProxy.swift b/ios/MullvadVPN/GeneralAPIs/OutgoingConnectionProxy.swift
index ed54e4743b..0c2e281ba1 100644
--- a/ios/MullvadVPN/GeneralAPIs/OutgoingConnectionProxy.swift
+++ b/ios/MullvadVPN/GeneralAPIs/OutgoingConnectionProxy.swift
@@ -45,18 +45,20 @@ final class OutgoingConnectionProxy: OutgoingConnectionHandling {
private func perform<T: Decodable>(retryStrategy: REST.RetryStrategy, version: ExitIPVersion) async throws -> T {
let delayIterator = retryStrategy.makeDelayIterator()
for _ in 0..<retryStrategy.maxRetryCount {
- do {
- return try await perform(host: version.host(hostname: hostname))
- } catch {
- // ignore if request is cancelled
- if case URLError.cancelled = error {
- throw error
- } else {
- // retry with the delay
- guard let delay = delayIterator.next() else { throw error }
- let mills = UInt64(max(0, delay.milliseconds))
- let nanos = mills.saturatingMultiplication(1_000_000)
- try await Task.sleep(nanoseconds: nanos)
+ if !Task.isCancelled {
+ do {
+ return try await perform(host: version.host(hostname: hostname))
+ } catch {
+ // ignore if request is cancelled
+ if case URLError.cancelled = error {
+ throw error
+ } else {
+ // retry with the delay
+ guard let delay = delayIterator.next() else { throw error }
+ let mills = UInt64(max(0, delay.milliseconds))
+ let nanos = mills.saturatingMultiplication(1_000_000)
+ try await Task.sleep(nanoseconds: nanos)
+ }
}
}
}
diff --git a/ios/MullvadVPN/Presentation controllers/FormsheetPresentationController.swift b/ios/MullvadVPN/Presentation controllers/FormsheetPresentationController.swift
index 06bfcd60fd..cced1e8c8c 100644
--- a/ios/MullvadVPN/Presentation controllers/FormsheetPresentationController.swift
+++ b/ios/MullvadVPN/Presentation controllers/FormsheetPresentationController.swift
@@ -75,6 +75,12 @@ class FormSheetPresentationController: UIPresentationController {
self.options = options
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
addKeyboardResponderIfNeeded()
+
+ registerForTraitChanges(
+ [UITraitUserInterfaceStyle.self],
+ handler: { (self: Self, previousTraitCollection: UITraitCollection) in
+ self.postFullscreenPresentationWillChangeIfNeeded()
+ })
}
override var frameOfPresentedViewInContainerView: CGRect {
@@ -152,12 +158,6 @@ class FormSheetPresentationController: UIPresentationController {
}
}
- override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
- super.traitCollectionDidChange(previousTraitCollection)
-
- postFullscreenPresentationWillChangeIfNeeded()
- }
-
private func postFullscreenPresentationWillChangeIfNeeded() {
let currentIsInFullScreen = isInFullScreenPresentation
diff --git a/ios/MullvadVPN/View controllers/RelayFilter/ChipFlowLayout.swift b/ios/MullvadVPN/View controllers/RelayFilter/ChipFlowLayout.swift
index 7bcd500a7a..46ac7354a6 100644
--- a/ios/MullvadVPN/View controllers/RelayFilter/ChipFlowLayout.swift
+++ b/ios/MullvadVPN/View controllers/RelayFilter/ChipFlowLayout.swift
@@ -30,8 +30,8 @@ class ChipFlowLayout: UICollectionViewFlowLayout {
let attributes = originalAttributes.compactMap { $0.copy() as? UICollectionViewLayoutAttributes }
// Detect RTL
- let languageCode = Locale.current.languageCode ?? "en"
- let isRTL = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft
+ let languageCode = Locale.current.language.languageCode?.identifier ?? "en"
+ let isRTL = Locale.Language(identifier: languageCode).characterDirection == .rightToLeft
var currentLineY: CGFloat = -1
var currentLineAttributes: [UICollectionViewLayoutAttributes] = []
diff --git a/ios/MullvadVPN/View controllers/RevokedDevice/RevokedDeviceView.swift b/ios/MullvadVPN/View controllers/RevokedDevice/RevokedDeviceView.swift
index 5f7723fdd8..1cf55562d4 100644
--- a/ios/MullvadVPN/View controllers/RevokedDevice/RevokedDeviceView.swift
+++ b/ios/MullvadVPN/View controllers/RevokedDevice/RevokedDeviceView.swift
@@ -40,14 +40,7 @@ struct RevokedDeviceView: View {
}
.padding(.top, 24)
}
- .apply {
- if #available(iOS 16.4, *) {
- $0.scrollBounceBehavior(.automatic)
- } else {
- $0
- }
- }
-
+ .scrollBounceBehavior(.automatic)
MainButton(text: "Go to login", style: viewModel.tunnelState.isSecured ? .danger : .default) {
onLogout?()
}
diff --git a/ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift b/ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift
index 7204fa4e03..d2560893ff 100644
--- a/ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift
+++ b/ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift
@@ -275,7 +275,7 @@ struct SingleChoiceList<Value>: View where Value: Equatable {
)
)
.focused($customValueIsFocused)
- .onChange(of: customValueInput) { _ in
+ .onChange(of: customValueInput) {
if let maxInputLength {
if customValueInput.count > maxInputLength {
customValueInput = String(customValueInput.prefix(maxInputLength))
diff --git a/ios/MullvadVPN/View controllers/TermsOfService/TermsOfServiceView.swift b/ios/MullvadVPN/View controllers/TermsOfService/TermsOfServiceView.swift
index 1f71ba4303..cd52037fc5 100644
--- a/ios/MullvadVPN/View controllers/TermsOfService/TermsOfServiceView.swift
+++ b/ios/MullvadVPN/View controllers/TermsOfService/TermsOfServiceView.swift
@@ -43,12 +43,7 @@ struct TermsOfServiceView: View {
var body: some View {
VStack(alignment: .leading) {
- // Disable scrolling if the contents do not overflow
- if #available(iOS 16.4, *) {
- scrollableContent.scrollBounceBehavior(.basedOnSize)
- } else {
- scrollableContent
- }
+ scrollableContent.scrollBounceBehavior(.basedOnSize)
HStack {
Text(privacyPolicyLink)
.font(.mullvadSmall)
diff --git a/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionView.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionView.swift
index d00eb5e49e..adaa600810 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionView.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionView.swift
@@ -83,13 +83,7 @@ struct ConnectionView: View {
}
}
.frame(maxHeight: scrollViewHeight)
- .apply {
- if #available(iOS 16.4, *) {
- $0.scrollBounceBehavior(.basedOnSize)
- } else {
- $0
- }
- }
+ .scrollBounceBehavior(.basedOnSize)
}
.transformEffect(.identity)
.animation(.default, value: hasFeatureIndicators)
@@ -99,8 +93,8 @@ struct ConnectionView: View {
.background(BlurView(style: .dark))
.cornerRadius(12)
.padding(EdgeInsets(top: 16, leading: 16, bottom: 24, trailing: 16))
- .onChange(of: connectionViewModel.showsConnectionDetails) { showsConnectionDetails in
- if !showsConnectionDetails {
+ .onChange(of: connectionViewModel.showsConnectionDetails) {
+ if !connectionViewModel.showsConnectionDetails {
withAnimation {
isExpanded = false
}
diff --git a/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSViewController.swift b/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSViewController.swift
index 29bab0ec63..e651ee53c5 100644
--- a/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSViewController.swift
+++ b/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSViewController.swift
@@ -70,11 +70,7 @@ class CustomDNSViewController: UITableViewController {
navigationItem.setHidesBackButton(editing, animated: animated)
if navigationItem.rightBarButtonItem != editButtonItem {
- if #available(iOS 16.0, *) {
- navigationItem.rightBarButtonItem?.isHidden = editing
- } else {
- navigationItem.rightBarButtonItem?.isEnabled = !editing
- }
+ navigationItem.rightBarButtonItem?.isHidden = editing
}
// Disable swipe to dismiss when editing
diff --git a/ios/MullvadVPN/Views/List/MullvadListNavigationItemView.swift b/ios/MullvadVPN/Views/List/MullvadListNavigationItemView.swift
index 8f494786cf..0509ecd906 100644
--- a/ios/MullvadVPN/Views/List/MullvadListNavigationItemView.swift
+++ b/ios/MullvadVPN/Views/List/MullvadListNavigationItemView.swift
@@ -91,9 +91,11 @@ private struct MullvadListButtonStyle: ButtonStyle {
let onButtonPressedChange: (Bool) -> Void
func makeBody(configuration: Configuration) -> some View {
configuration.label
- .onChange(of: configuration.isPressed) { newValue in
- onButtonPressedChange(newValue)
- }
+ .onChange(
+ of: configuration.isPressed,
+ {
+ onButtonPressedChange(configuration.isPressed)
+ })
}
}
diff --git a/ios/MullvadVPNTests/MullvadTypes/DurationTests.swift b/ios/MullvadVPNTests/MullvadTypes/DurationTests.swift
deleted file mode 100644
index efeaa0645f..0000000000
--- a/ios/MullvadVPNTests/MullvadTypes/DurationTests.swift
+++ /dev/null
@@ -1,72 +0,0 @@
-//
-// DurationTests.swift
-// MullvadRESTTests
-//
-// Created by pronebird on 05/11/2022.
-// Copyright © 2025 Mullvad VPN AB. All rights reserved.
-//
-
-import MullvadTypes
-import XCTest
-
-@testable import MullvadREST
-
-final class DurationTests: XCTestCase {
- func testComparable() throws {
- XCTAssertEqual(Duration.milliseconds(1000), .seconds(1))
- XCTAssertEqual(Duration.seconds(60), .minutes(1))
- XCTAssertEqual(Duration.minutes(60), .hours(1))
- XCTAssertEqual(Duration.hours(24), .days(1))
-
- XCTAssertTrue(Duration.days(1) == 86400.0)
- XCTAssertTrue(Duration.milliseconds(1234) == 1.234)
-
- XCTAssertTrue(Duration.milliseconds(.max) == 9.223372036854775e+15)
- XCTAssertEqual(Duration.milliseconds(.max).milliseconds, 9_223_372_036_854_775_807)
- XCTAssertEqual(Duration.seconds(.max).milliseconds, 9_223_372_036_854_775_807)
-
- XCTAssertLessThan(Duration.milliseconds(999), .seconds(1))
- XCTAssertGreaterThan(Duration.milliseconds(1001), .seconds(1))
- XCTAssertTrue(1.1 > Duration.milliseconds(1001))
- XCTAssertTrue(1.0 < Duration.milliseconds(1001))
- }
-
- func testAddition() throws {
- XCTAssertEqual(Duration.seconds(4) + .seconds(116), .minutes(2))
- XCTAssertEqual(Duration.minutes(4) + .seconds(.max), .milliseconds(.max))
- }
-
- func testSubtraction() throws {
- XCTAssertEqual(Duration.minutes(4) - .minutes(1), .seconds(180))
- XCTAssertEqual(Duration.seconds(4) - .seconds(64), .minutes(-1))
- }
-
- func testMultiplication() throws {
- XCTAssertEqual(Duration.seconds(4) * 2.0, .seconds(8))
- XCTAssertEqual(Duration.seconds(4) * 4, .seconds(16))
- XCTAssertEqual(Duration.milliseconds(20000) * 3, .minutes(1))
- XCTAssertEqual(Duration.milliseconds(.max - 1) * 2, .milliseconds(.max))
- XCTAssertEqual(
- Duration.milliseconds(.max) * (Double(Int.max) + 1.0),
- .milliseconds(Int(Double(Int.max).nextDown))
- )
- }
-
- func testDivision() throws {
- XCTAssertEqual(Duration.seconds(4) / 4, .seconds(1))
- XCTAssertEqual(Duration.milliseconds(21000) / 3, .seconds(7))
- XCTAssertEqual(Duration.minutes(3) / 3, .seconds(60))
- }
-
- func testLogFormat() throws {
- XCTAssertEqual(Duration.milliseconds(999).logFormat(), "999ms")
- XCTAssertEqual(Duration.milliseconds(1000).logFormat(), "1s")
- XCTAssertEqual(Duration.milliseconds(1200).logFormat(), "1.20s")
- }
-}
-
-private extension Duration {
- static func == (lhs: Duration, rhs: Double) -> Bool {
- return lhs.timeInterval == rhs
- }
-}
diff --git a/ios/MullvadVPNUITests/Screenshots/SnapshotHelper.swift b/ios/MullvadVPNUITests/Screenshots/SnapshotHelper.swift
index c8d7fd2bf8..efd432f755 100644
--- a/ios/MullvadVPNUITests/Screenshots/SnapshotHelper.swift
+++ b/ios/MullvadVPNUITests/Screenshots/SnapshotHelper.swift
@@ -208,15 +208,11 @@ open class Snapshot: NSObject {
#if os(watchOS)
return image
#else
- if #available(iOS 10.0, *) {
- let format = UIGraphicsImageRendererFormat()
- format.scale = image.scale
- let renderer = UIGraphicsImageRenderer(size: image.size, format: format)
- return renderer.image { _ in
- image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
- }
- } else {
- return image
+ let format = UIGraphicsImageRendererFormat()
+ format.scale = image.scale
+ let renderer = UIGraphicsImageRenderer(size: image.size, format: format)
+ return renderer.image { _ in
+ image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
}
#endif
}
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift
index 683cc9c596..dfead154f6 100644
--- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift
+++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift
@@ -152,7 +152,9 @@ extension PacketTunnelActor {
while !Task.isCancelled {
guard let self else { return }
- try await Task.sleepUsingContinuousClock(for: timings.bootRecoveryPeriodicity)
+ try await Task.sleep(for: timings.bootRecoveryPeriodicity)
+
+ guard !Task.isCancelled else { return }
// Schedule task to reconnect.
eventChannel.send(.reconnect(.random))
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift
index 8bff739d93..a2c8ab9aff 100644
--- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift
+++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+KeyPolicy.swift
@@ -51,7 +51,9 @@ extension PacketTunnelActor {
guard let self else { return }
// Wait for key to propagate across relays.
- try await Task.sleepUsingContinuousClock(for: timings.wgKeyPropagationDelay)
+ try await Task.sleep(for: timings.wgKeyPropagationDelay)
+
+ guard !Task.isCancelled else { return }
// Enqueue task to change key policy.
eventChannel.send(.switchKey)
diff --git a/ios/PacketTunnelCore/Actor/Task+Duration.swift b/ios/PacketTunnelCore/Actor/Task+Duration.swift
deleted file mode 100644
index fb1b65c030..0000000000
--- a/ios/PacketTunnelCore/Actor/Task+Duration.swift
+++ /dev/null
@@ -1,67 +0,0 @@
-//
-// Task+.swift
-// PacketTunnelCore
-//
-// Created by pronebird on 11/09/2023.
-// Copyright © 2025 Mullvad VPN AB. All rights reserved.
-//
-
-import Foundation
-import MullvadTypes
-
-private typealias TaskCancellationError = CancellationError
-
-extension Task where Success == Never, Failure == Never {
- /**
- Suspends the current task for at least the given duration.
-
- Negative durations are clamped to zero.
-
- - Parameter duration: duration that determines how long the task should be suspended.
- */
- @available(iOS, introduced: 15.0, obsoleted: 16.0, message: "Replace with Task.sleep(for:tolerance:clock:).")
- static func sleep(duration: Duration) async throws {
- let millis = UInt64(max(0, duration.milliseconds))
- let nanos = millis.saturatingMultiplication(1_000_000)
-
- try await Task.sleep(nanoseconds: nanos)
- }
-
- /**
- Suspends the current task for the given duration.
-
- Negative durations are clamped to zero.
-
- - Parameter duration: duration that determines how long the task should be suspended.
- */
- @available(iOS, introduced: 15.0, obsoleted: 16.0, message: "Replace with Task.sleep(for:tolerance:clock:).")
- static func sleepUsingContinuousClock(for duration: Duration) async throws {
- nonisolated(unsafe) let timer = DispatchSource.makeTimerSource()
-
- try await withTaskCancellationHandler {
- try await withCheckedThrowingContinuation { continuation in
- // The `continuation` handler should never be `resume`'d more than once.
- // Setting the eventHandler on the timer after it has been cancelled will be ignored.
- // https://github.com/apple/swift-corelibs-libdispatch/blob/77766345740dfe3075f2f60bead854b29b0cfa24/src/source.c#L338
- // Therefore, set a flag indicating `resume` has already been called to avoid `resume`ing more than once.
- // Cancelling the timer does not cancel an event handler that is already running however,
- // the cancel handler will be scheduled after the event handler has finished.
- // https://developer.apple.com/documentation/dispatch/1385604-dispatch_source_cancel
- // Therefore, there it is safe to do this here.
- var hasResumed = false
- timer.setEventHandler {
- hasResumed = true
- continuation.resume()
- }
- timer.setCancelHandler {
- guard hasResumed == false else { return }
- continuation.resume(throwing: TaskCancellationError())
- }
- timer.schedule(wallDeadline: .now() + DispatchTimeInterval.milliseconds(duration.milliseconds))
- timer.activate()
- }
- } onCancel: {
- timer.cancel()
- }
- }
-}
diff --git a/ios/PacketTunnelCoreTests/TaskSleepTests.swift b/ios/PacketTunnelCoreTests/TaskSleepTests.swift
deleted file mode 100644
index 20aee5006d..0000000000
--- a/ios/PacketTunnelCoreTests/TaskSleepTests.swift
+++ /dev/null
@@ -1,57 +0,0 @@
-//
-// TaskSleepTests.swift
-// PacketTunnelCoreTests
-//
-// Created by pronebird on 25/09/2023.
-// Copyright © 2025 Mullvad VPN AB. All rights reserved.
-//
-
-import XCTest
-
-@testable import PacketTunnelCore
-
-final class TaskSleepTests: XCTestCase {
- func testCancellation() async throws {
- let task = Task {
- try await Task.sleepUsingContinuousClock(for: .seconds(1))
- }
-
- task.cancel()
-
- do {
- try await task.value
-
- XCTFail("Task must be cancelled.")
- } catch {
- XCTAssert(error is CancellationError)
- }
- }
-
- /// This test triggers a race condition in `sleepUsingContinuousClock` where an `AutoCancellingTask` will
- /// cancel a `DispatchSourceTimer` in a `Task` trying to call `resume` on its continuation handler more than once
- func testSuccessfulEventHandlerRemovesCancellation() async throws {
- for _ in 0...20 {
- let task = recoveryTask()
- try await Task.sleep(duration: .milliseconds(10))
- task.callDummyFunctionToForceConcurrencyWait()
- }
- }
-
- private func recoveryTask() -> AutoCancellingTask {
- AutoCancellingTask(
- Task.detached {
- while Task.isCancelled == false {
- try await Task.sleepUsingContinuousClock(for: .milliseconds(10))
- }
- })
- }
-}
-
-private extension AutoCancellingTask {
- /// This function is here to silence a warning about unused variables in `testSuccessfulEventHandlerRemovesCancellation`
- /// The following construct `_ = recoveryTask()` cannot be used as the resulting `AutoCancellingTask`
- /// would immediately get `deinit`ed, changing the test scenario.
- /// A dummy function is needed to make sure the task is not cancelled before concurrency is forced
- /// by having a call to `Task.sleep`
- func callDummyFunctionToForceConcurrencyWait() {}
-}
diff --git a/ios/Shared/ApplicationLanguage.swift b/ios/Shared/ApplicationLanguage.swift
index c460f92123..e51eef8fff 100644
--- a/ios/Shared/ApplicationLanguage.swift
+++ b/ios/Shared/ApplicationLanguage.swift
@@ -107,29 +107,18 @@ enum ApplicationLanguage: String, CaseIterable, Identifiable {
let defaultCode = ApplicationLanguage.english.rawValue
let fullCode = Locale.preferredLanguages.first ?? defaultCode
- if #available(iOS 16, *) {
- let locale = Locale(identifier: fullCode)
- if let script = locale.language.script?.identifier {
- switch script {
- case "Hans":
- return .chineseSimplified
- case "Hant":
- return .chineseTraditional
- default:
- break
- }
- }
- } else {
- if fullCode.contains("Hans") {
+ let locale = Locale(identifier: fullCode)
+ if let script = locale.language.script?.identifier {
+ switch script {
+ case "Hans":
return .chineseSimplified
- } else if fullCode.contains("Hant") {
+ case "Hant":
return .chineseTraditional
+ default:
+ break
}
}
-
- // Otherwise, try to get languageCode (e.g., "en", "fr")
- let locale = Locale(identifier: fullCode)
- let langCode = locale.languageCode ?? defaultCode
+ let langCode = locale.language.languageCode?.identifier ?? defaultCode
return ApplicationLanguage(rawValue: langCode) ?? .english
}