Skip to content

Commit 8e5e27c

Browse files
committed
Implement IAN activation flow
1 parent 79b7235 commit 8e5e27c

File tree

64 files changed

+1886
-393
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1886
-393
lines changed

ios/Assets/Localizable.xcstrings

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@
359359
}
360360
},
361361
"%@ “Local network sharing” requires restarting the VPN connection, which will disconnect you and briefly expose your traffic.\nTo prevent this, manually enable Airplane Mode and turn off Wi-Fi before continuing.\nWould you like to continue to enable “Local network sharing”?" : {
362+
"extractionState" : "stale",
362363
"localizations" : {
363364
"da" : {
364365
"stringUnit" : {
@@ -7574,6 +7575,14 @@
75747575
}
75757576
}
75767577
},
7578+
"Be cautious when using automatic updates as this will trigger the network connectivity loss." : {
7579+
"comment" : "Text displayed in a settings page explaining the potential consequences of using automatic updates when the IAN is enabled.",
7580+
"isCommentAutoGenerated" : true
7581+
},
7582+
"Because of these iOS limitations, you will lose network connectivity if Mullvad VPN is updated when this is enabled and you are connected to the VPN. Network connectivity can only be restored by rebooting the device." : {
7583+
"comment" : "Text describing the potential network connectivity loss when the IAN feature is enabled and the device is connected to the VPN.",
7584+
"isCommentAutoGenerated" : true
7585+
},
75777586
"Belgium" : {
75787587
"localizations" : {
75797588
"da" : {
@@ -14542,6 +14551,10 @@
1454214551
}
1454314552
}
1454414553
},
14554+
"Currently there is no way to work around this behaviour, but you can avoid losing network connectivity by disabling this feature or disconnecting before updating Mullvad VPN." : {
14555+
"comment" : "Text displayed on the last page of the IAN settings info view, explaining that there is no way to work around the current behaviour, but that they can avoid losing network connectivity by disabling the feature or disconnecting before",
14556+
"isCommentAutoGenerated" : true
14557+
},
1454514558
"Custom" : {
1454614559
"localizations" : {
1454714560
"da" : {
@@ -17729,6 +17742,7 @@
1772917742
}
1773017743
},
1773117744
"Disabling" : {
17745+
"extractionState" : "stale",
1773217746
"localizations" : {
1773317747
"da" : {
1773417748
"stringUnit" : {
@@ -19380,6 +19394,10 @@
1938019394
}
1938119395
}
1938219396
},
19397+
"Due to iOS limitations, this not enabled by default. Our other apps tunnel all traffic via the VPN by default." : {
19398+
"comment" : "Explanation of why the \"Use Include All Networks\" feature is not enabled by default in the IAN settings view.",
19399+
"isCommentAutoGenerated" : true
19400+
},
1938319401
"Dusseldorf" : {
1938419402
"localizations" : {
1938519403
"da" : {
@@ -20678,6 +20696,10 @@
2067820696
}
2067920697
}
2068020698
},
20699+
"Enable notifications" : {
20700+
"comment" : "Title of an action button in an alert presented by the IANSettingsCoordinator when the user is prompted to enable notifications.",
20701+
"isCommentAutoGenerated" : true
20702+
},
2068120703
"Enable recents" : {
2068220704
"localizations" : {
2068320705
"da" : {
@@ -20797,6 +20819,7 @@
2079720819
}
2079820820
},
2079920821
"Enabling" : {
20822+
"extractionState" : "stale",
2080020823
"localizations" : {
2080120824
"da" : {
2080220825
"stringUnit" : {
@@ -23628,6 +23651,14 @@
2362823651
}
2362923652
}
2363023653
},
23654+
"Force all apps" : {
23655+
"comment" : "Title of the settings screen for forcing all apps to use the IAN tunnel.",
23656+
"isCommentAutoGenerated" : true
23657+
},
23658+
"Forces all app traffic on the device into the VPN tunnel, ensuring that other apps can’t accidentally or maliciously leak data. Apple system apps and services necessary for device functionality are not affected." : {
23659+
"comment" : "Description of the IAN feature, including a note that Apple system apps and services are not affected.",
23660+
"isCommentAutoGenerated" : true
23661+
},
2363123662
"Fortaleza" : {
2363223663
"localizations" : {
2363323664
"da" : {
@@ -24218,6 +24249,10 @@
2421824249
}
2421924250
}
2422024251
},
24252+
"Given the pros and cons of using this feature, I wish to proceed" : {
24253+
"comment" : "Label for an action box in the IAN settings view, allowing the user to agree to use the feature.",
24254+
"isCommentAutoGenerated" : true
24255+
},
2422124256
"Glasgow" : {
2422224257
"localizations" : {
2422324258
"da" : {
@@ -25988,6 +26023,10 @@
2598826023
}
2598926024
}
2599026025
},
26026+
"If this is not enabled, malicious apps on your device can leak traffic outside the tunnel." : {
26027+
"comment" : "Text describing the potential risk of data leakage when the IAN feature is disabled.",
26028+
"isCommentAutoGenerated" : true
26029+
},
2599126030
"If you are having issues connecting to VPN servers, please contact support." : {
2599226031
"localizations" : {
2599326032
"da" : {
@@ -28113,6 +28152,7 @@
2811328152
}
2811428153
},
2811528154
"Include all networks" : {
28155+
"extractionState" : "stale",
2811628156
"localizations" : {
2811728157
"da" : {
2811828158
"stringUnit" : {
@@ -39328,6 +39368,10 @@
3932839368
}
3932939369
}
3933039370
},
39371+
"Open system settings" : {
39372+
"comment" : "Title of an action button in an alert that allows the user to open system settings.",
39373+
"isCommentAutoGenerated" : true
39374+
},
3933139375
"Optional" : {
3933239376
"localizations" : {
3933339377
"da" : {
@@ -41924,6 +41968,10 @@
4192441968
}
4192541969
}
4192641970
},
41971+
"Please read through all information above in order to activate this feature" : {
41972+
"comment" : "Footer text in the IANSettingsView that instructs the user to read through all information before activating a feature.",
41973+
"isCommentAutoGenerated" : true
41974+
},
4192741975
"Please retry by using the \"Restore purchases\" button." : {
4192841976
"localizations" : {
4192941977
"da" : {
@@ -64835,5 +64883,5 @@
6483564883
}
6483664884
}
6483764885
},
64838-
"version" : "1.0"
64886+
"version" : "1.1"
6483964887
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//
2+
// InclueAllNetworksSettings.swift
3+
// MullvadVPN
4+
//
5+
// Created by Jon Petersson on 2026-01-16.
6+
// Copyright © 2026 Mullvad VPN AB. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
/// Whether IAN is enabled.
12+
public enum InclueAllNetworksState: Codable, Sendable {
13+
case on
14+
case off
15+
16+
public var isEnabled: Bool {
17+
get { self == .on }
18+
set { self = newValue ? .on : .off }
19+
}
20+
}
21+
22+
/// Whether "Local network sharing" is enabled.
23+
public enum LocalNetworkSharingState: Codable, Sendable {
24+
case on
25+
case off
26+
27+
public var isEnabled: Bool {
28+
get { self == .on }
29+
set { self = newValue ? .on : .off }
30+
}
31+
}
32+
33+
public struct InclueAllNetworksSettings: Codable, Equatable, Sendable {
34+
public var includeAllNetworksState: InclueAllNetworksState
35+
public var localNetworkSharingState: LocalNetworkSharingState
36+
public var consent: Bool
37+
38+
public var includeAllNetworksIsEnabled: Bool {
39+
includeAllNetworksState.isEnabled && consent
40+
}
41+
42+
public var localNetworkSharingIsEnabled: Bool {
43+
includeAllNetworksState.isEnabled && localNetworkSharingState.isEnabled && consent
44+
}
45+
46+
public init(
47+
includeAllNetworksState: InclueAllNetworksState = .off,
48+
localNetworkSharingState: LocalNetworkSharingState = .off,
49+
consent: Bool = false
50+
) {
51+
self.includeAllNetworksState = includeAllNetworksState
52+
self.localNetworkSharingState = localNetworkSharingState
53+
self.consent = consent
54+
}
55+
}

ios/MullvadSettings/TunnelSettingsStrategy.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,17 @@ public struct TunnelSettingsStrategy: TunnelSettingsStrategyProtocol, Sendable {
3434
oldSettings: LatestTunnelSettings,
3535
newSettings: LatestTunnelSettings
3636
) -> TunnelSettingsReconnectionStrategy {
37+
// Don't reconnect the tunnel If IAN consent was the setting that triggered the settings update.
38+
if oldSettings.includeAllNetworksConsent != newSettings.includeAllNetworksConsent {
39+
return .noReconnect
40+
}
41+
3742
if oldSettings.localNetworkSharing != newSettings.localNetworkSharing
3843
|| oldSettings.includeAllNetworks != newSettings.includeAllNetworks
3944
{
4045
return .hardReconnect
4146
}
47+
4248
switch (oldSettings, newSettings) {
4349
case let (old, new) where old != new:
4450
return .newRelayReconnect
@@ -55,4 +61,5 @@ public enum TunnelSettingsReconnectionStrategy {
5561
case currentRelayReconnect
5662
case newRelayReconnect
5763
case hardReconnect
64+
case noReconnect
5865
}

ios/MullvadSettings/TunnelSettingsUpdate.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,18 @@ import Foundation
1010
import MullvadTypes
1111

1212
public enum TunnelSettingsUpdate: Sendable {
13-
case localNetworkSharing(Bool)
14-
case includeAllNetworks(Bool)
1513
case dnsSettings(DNSSettings)
1614
case obfuscation(WireGuardObfuscationSettings)
1715
case relayConstraints(RelayConstraints)
1816
case quantumResistance(TunnelQuantumResistance)
1917
case multihop(MultihopState)
2018
case daita(DAITASettings)
19+
case includeAllNetworks(InclueAllNetworksSettings)
2120
}
2221

2322
extension TunnelSettingsUpdate {
2423
public func apply(to settings: inout LatestTunnelSettings) {
2524
switch self {
26-
case let .localNetworkSharing(enabled):
27-
settings.localNetworkSharing = enabled
28-
case let .includeAllNetworks(enabled):
29-
settings.includeAllNetworks = enabled
3025
case let .dnsSettings(newDNSSettings):
3126
settings.dnsSettings = newDNSSettings
3227
case let .obfuscation(newObfuscationSettings):
@@ -39,19 +34,22 @@ extension TunnelSettingsUpdate {
3934
settings.tunnelMultihopState = newState
4035
case let .daita(newDAITASettings):
4136
settings.daita = newDAITASettings
37+
case let .includeAllNetworks(newIncludeAllNetworksSettings):
38+
settings.includeAllNetworks = newIncludeAllNetworksSettings.includeAllNetworksState.isEnabled
39+
settings.localNetworkSharing = newIncludeAllNetworksSettings.localNetworkSharingState.isEnabled
40+
settings.includeAllNetworksConsent = newIncludeAllNetworksSettings.consent
4241
}
4342
}
4443

4544
public var subjectName: String {
4645
switch self {
47-
case .localNetworkSharing: "Local network sharing"
48-
case .includeAllNetworks: "Include all networks"
4946
case .dnsSettings: "DNS settings"
5047
case .obfuscation: "obfuscation settings"
5148
case .relayConstraints: "relay constraints"
5249
case .quantumResistance: "quantum resistance"
5350
case .multihop: "multihop"
5451
case .daita: "daita"
52+
case .includeAllNetworks: "include all networks"
5553
}
5654
}
5755
}

ios/MullvadSettings/TunnelSettingsV6.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ public struct TunnelSettingsV6: Codable, Equatable, TunnelSettings, Sendable {
5252
tunnelQuantumResistance: tunnelQuantumResistance,
5353
tunnelMultihopState: tunnelMultihopState,
5454
daita: daita,
55-
localNetworkSharing: false,
56-
includeAllNetworks: false
55+
includeAllNetworks: InclueAllNetworksSettings()
5756
)
5857
}
5958
}

ios/MullvadSettings/TunnelSettingsV7.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,30 +28,33 @@ public struct TunnelSettingsV7: Codable, Equatable, TunnelSettings, Sendable {
2828
/// DAITA settings.
2929
public var daita: DAITASettings
3030

31-
/// Local networks sharing.
32-
public var localNetworkSharing: Bool
33-
34-
/// Forces the system to route most traffic through the tunnel
31+
/// Forces the system to route most traffic through the tunnel.
3532
public var includeAllNetworks: Bool
3633

34+
/// Consent to enable `includeAllNetworks`, understanding the pros and cons.
35+
public var includeAllNetworksConsent: Bool
36+
37+
/// Local network sharing.
38+
public var localNetworkSharing: Bool
39+
3740
public init(
3841
relayConstraints: RelayConstraints = RelayConstraints(),
3942
dnsSettings: DNSSettings = DNSSettings(),
4043
wireGuardObfuscation: WireGuardObfuscationSettings = WireGuardObfuscationSettings(),
4144
tunnelQuantumResistance: TunnelQuantumResistance = .automatic,
4245
tunnelMultihopState: MultihopState = .off,
4346
daita: DAITASettings = DAITASettings(),
44-
localNetworkSharing: Bool = false,
45-
includeAllNetworks: Bool = false
47+
includeAllNetworks: InclueAllNetworksSettings = InclueAllNetworksSettings()
4648
) {
4749
self.relayConstraints = relayConstraints
4850
self.dnsSettings = dnsSettings
4951
self.wireGuardObfuscation = wireGuardObfuscation
5052
self.tunnelQuantumResistance = tunnelQuantumResistance
5153
self.tunnelMultihopState = tunnelMultihopState
5254
self.daita = daita
53-
self.localNetworkSharing = localNetworkSharing
54-
self.includeAllNetworks = includeAllNetworks
55+
self.includeAllNetworks = includeAllNetworks.localNetworkSharingState.isEnabled
56+
self.localNetworkSharing = includeAllNetworks.includeAllNetworksState.isEnabled
57+
self.includeAllNetworksConsent = includeAllNetworks.consent
5558
}
5659

5760
public func upgradeToNextVersion() -> any TunnelSettings {

0 commit comments

Comments
 (0)