Skip to content

Commit

Permalink
Hide State and expose Observed State instead
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrej Mihajlov authored and buggmagnet committed Oct 18, 2023
1 parent b1271e7 commit 5c13b7e
Show file tree
Hide file tree
Showing 13 changed files with 201 additions and 89 deletions.
8 changes: 8 additions & 0 deletions ios/MullvadVPN.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@
5878F50029CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5878F4FF29CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift */; };
587988C728A2A01F00E3DF54 /* AccountDataThrottling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587988C628A2A01F00E3DF54 /* AccountDataThrottling.swift */; };
587A01FC23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587A01FB23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift */; };
587A5E522ADD7569003A70F1 /* ObservedState+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587A5E512ADD7569003A70F1 /* ObservedState+Extensions.swift */; };
587B7536266528A200DEF7E9 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B7535266528A200DEF7E9 /* NotificationManager.swift */; };
587B753B2666467500DEF7E9 /* NotificationBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B753A2666467500DEF7E9 /* NotificationBannerView.swift */; };
587B753D2666468F00DEF7E9 /* NotificationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B753C2666468F00DEF7E9 /* NotificationController.swift */; };
Expand Down Expand Up @@ -292,6 +293,7 @@
58CE5E66224146200008646E /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CE5E65224146200008646E /* LoginViewController.swift */; };
58CE5E6B224146210008646E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 58CE5E6A224146210008646E /* Assets.xcassets */; };
58CE5E81224146470008646E /* PacketTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 58CE5E79224146470008646E /* PacketTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
58CF95A22AD6F35800B59F5D /* ObservedState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CF95A12AD6F35800B59F5D /* ObservedState.swift */; };
58D0C79E23F1CEBA00FE9BA7 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D0C79D23F1CEBA00FE9BA7 /* SnapshotHelper.swift */; };
58D0C7A223F1CECF00FE9BA7 /* MullvadVPNScreenshots.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D0C7A023F1CECF00FE9BA7 /* MullvadVPNScreenshots.swift */; };
58D223A8294C8A490029F5F8 /* Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = 58D223A7294C8A490029F5F8 /* Operations.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -1315,6 +1317,7 @@
587988C628A2A01F00E3DF54 /* AccountDataThrottling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDataThrottling.swift; sourceTree = "<group>"; };
58799A352A84FC9F007BE51F /* PingerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PingerProtocol.swift; sourceTree = "<group>"; };
587A01FB23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatorTunnelProviderHost.swift; sourceTree = "<group>"; };
587A5E512ADD7569003A70F1 /* ObservedState+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ObservedState+Extensions.swift"; sourceTree = "<group>"; };
587AD7C523421D7000E93A53 /* TunnelSettingsV1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelSettingsV1.swift; sourceTree = "<group>"; };
587B7535266528A200DEF7E9 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
587B753A2666467500DEF7E9 /* NotificationBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationBannerView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1432,6 +1435,7 @@
58CE5E79224146470008646E /* PacketTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PacketTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; };
58CE5E7D224146470008646E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
58CE5E7E224146470008646E /* PacketTunnel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PacketTunnel.entitlements; sourceTree = "<group>"; };
58CF95A12AD6F35800B59F5D /* ObservedState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservedState.swift; sourceTree = "<group>"; };
58D0C79323F1CE7000FE9BA7 /* MullvadVPNScreenshots.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MullvadVPNScreenshots.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
58D0C79D23F1CEBA00FE9BA7 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapshotHelper.swift; sourceTree = "<group>"; };
58D0C79F23F1CECF00FE9BA7 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2336,6 +2340,8 @@
5838322A2AC3EF9600EA2071 /* CommandChannel.swift */,
583E60952A9F6D0800DC61EF /* ConfigurationBuilder.swift */,
580D6B892AB31AB400B2D6E0 /* NetworkPath+NetworkReachability.swift */,
58CF95A12AD6F35800B59F5D /* ObservedState.swift */,
587A5E512ADD7569003A70F1 /* ObservedState+Extensions.swift */,
58E9C3852A4EF1CB00CFDEAC /* PacketTunnelActor.swift */,
583832242AC318A100EA2071 /* PacketTunnelActor+ConnectionMonitoring.swift */,
583832222AC3181400EA2071 /* PacketTunnelActor+ErrorState.swift */,
Expand Down Expand Up @@ -4188,12 +4194,14 @@
58C7A4572A863FB90060C66F /* TunnelDeviceInfoProtocol.swift in Sources */,
58C7A4562A863FB90060C66F /* DefaultPathObserverProtocol.swift in Sources */,
58FE25DA2AA72A8F003D1918 /* PacketTunnelActor.swift in Sources */,
587A5E522ADD7569003A70F1 /* ObservedState+Extensions.swift in Sources */,
58FE25E62AA738E8003D1918 /* TunnelAdapterProtocol.swift in Sources */,
583832252AC318A100EA2071 /* PacketTunnelActor+ConnectionMonitoring.swift in Sources */,
58C7A4552A863FB90060C66F /* TunnelMonitor.swift in Sources */,
58C7AF182ABD84AB007EDD7A /* ProxyURLResponse.swift in Sources */,
58C7A4512A863FB50060C66F /* PingerProtocol.swift in Sources */,
583832292AC3DF1300EA2071 /* Command.swift in Sources */,
58CF95A22AD6F35800B59F5D /* ObservedState.swift in Sources */,
583832232AC3181400EA2071 /* PacketTunnelActor+ErrorState.swift in Sources */,
58C7AF112ABD8480007EDD7A /* TunnelProviderMessage.swift in Sources */,
58C7AF162ABD84A8007EDD7A /* URLRequestProxy.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ extension PacketTunnelProvider {
stopObservingActorState()

stateObserverTask = Task {
let stateStream = await self.actor.states
let stateStream = await self.actor.observedStates
var lastConnectionAttempt: UInt = 0

for await newState in stateStream {
Expand Down
75 changes: 75 additions & 0 deletions ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// ObservedState+Extensions.swift
// PacketTunnelCore
//
// Created by pronebird on 16/10/2023.
// Copyright © 2023 Mullvad VPN AB. All rights reserved.
//

import Foundation
import MullvadTypes

extension ObservedState {
public var packetTunnelStatus: PacketTunnelStatus {
var status = PacketTunnelStatus()

switch self {
case let .connecting(connState),
let .connected(connState),
let .reconnecting(connState),
let .disconnecting(connState):
switch connState.networkReachability {
case .reachable:
status.isNetworkReachable = true
case .unreachable:
status.isNetworkReachable = false
case .undetermined:
// TODO: fix me
status.isNetworkReachable = true
}

status.numberOfFailedAttempts = connState.connectionAttemptCount
status.tunnelRelay = connState.selectedRelay.packetTunnelRelay

case .disconnected, .initial:
break

case let .error(blockedState):
status.blockedStateReason = blockedState.reason
}

return status
}

public var relayConstraints: RelayConstraints? {
switch self {
case let .connecting(connState), let .connected(connState), let .reconnecting(connState):
return connState.relayConstraints

case let .error(blockedState):
return blockedState.relayConstraints

case .initial, .disconnecting, .disconnected:
return nil
}
}

public var name: String {
switch self {
case .connected:
return "Connected"
case .connecting:
return "Connecting"
case .reconnecting:
return "Reconnecting"
case .disconnecting:
return "Disconnecting"
case .disconnected:
return "Disconnected"
case .initial:
return "Initial"
case .error:
return "Error"
}
}
}
78 changes: 78 additions & 0 deletions ios/PacketTunnelCore/Actor/ObservedState.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// ObservedState.swift
// PacketTunnelCore
//
// Created by pronebird on 11/10/2023.
// Copyright © 2023 Mullvad VPN AB. All rights reserved.
//

import Combine
import Foundation
import MullvadTypes
import Network

/// A serializable representation of internal state.
public enum ObservedState: Equatable, Codable {
case initial
case connecting(ObservedConnectionState)
case reconnecting(ObservedConnectionState)
case connected(ObservedConnectionState)
case disconnecting(ObservedConnectionState)
case disconnected
case error(ObservedBlockedState)
}

/// A serializable representation of internal connection state.
public struct ObservedConnectionState: Equatable, Codable {
public var selectedRelay: SelectedRelay
public var relayConstraints: RelayConstraints
public var networkReachability: NetworkReachability
public var connectionAttemptCount: UInt
}

/// A serializable representation of internal blocked state.
public struct ObservedBlockedState: Equatable, Codable {
public var reason: BlockedStateReason
public var relayConstraints: RelayConstraints?
}

extension State {
/// Map `State` to `ObservedState`.
var observedState: ObservedState {
switch self {
case .initial:
return .initial
case let .connecting(connState):
return .connecting(connState.observedConnectionState)
case let .connected(connState):
return .connected(connState.observedConnectionState)
case let .reconnecting(connState):
return .reconnecting(connState.observedConnectionState)
case let .disconnecting(connState):
return .disconnecting(connState.observedConnectionState)
case .disconnected:
return .disconnected
case let .error(blockedState):
return .error(blockedState.observedBlockedState)
}
}
}

extension ConnectionState {
/// Map `ConnectionState` to `ObservedConnectionState`.
var observedConnectionState: ObservedConnectionState {
ObservedConnectionState(
selectedRelay: selectedRelay,
relayConstraints: relayConstraints,
networkReachability: networkReachability,
connectionAttemptCount: connectionAttemptCount
)
}
}

extension BlockedState {
/// Map `BlockedState` to `ObservedBlockedState`
var observedBlockedState: ObservedBlockedState {
return ObservedBlockedState(reason: reason, relayConstraints: relayConstraints)
}
}
16 changes: 8 additions & 8 deletions ios/PacketTunnelCore/Actor/PacketTunnelActor+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
import Foundation

extension PacketTunnelActor {
/// Returns a stream yielding new value when `state` changes.
/// The stream starts with current `state` and ends upon moving to `.disconnected` state.
public var states: AsyncStream<State> {
/// Returns a stream yielding `ObservedState`.
/// Note that the stream yields current value when created.
public var observedStates: AsyncStream<ObservedState> {
AsyncStream { continuation in
let cancellable = self.$state.sink { newState in
let cancellable = $observedState.sink { newState in
continuation.yield(newState)

// Finish stream once entered `.disconnected` state.
Expand All @@ -28,10 +28,10 @@ extension PacketTunnelActor {
}
}

/// Wait until the `state` moved to `.connected`.
/// Wait until the `observedState` moved to `.connected`.
/// Should return if the state is `.disconnected` as this is the final state of actor.
public func waitUntilConnected() async {
for await newState in states {
for await newState in observedStates {
switch newState {
case .connected, .disconnected:
// Return once either desired or final state is reached.
Expand All @@ -43,9 +43,9 @@ extension PacketTunnelActor {
}
}

/// Wait until the `state` moved to `.disiconnected`.
/// Wait until the `observedState` moved to `.disiconnected`.
public func waitUntilDisconnected() async {
for await newState in states {
for await newState in observedStates {
if case .disconnected = newState {
return
}
Expand Down
5 changes: 4 additions & 1 deletion ios/PacketTunnelCore/Actor/PacketTunnelActor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@ import class WireGuardKitTypes.PrivateKey
same anyway.
*/
public actor PacketTunnelActor {
@Published internal(set) public var state: State = .initial {
var state: State = .initial {
didSet {
logger.debug("\(state.logFormat())")
observedState = state.observedState
}
}

@Published internal(set) public var observedState: ObservedState = .initial

let logger = Logger(label: "PacketTunnelActor")

let timings: PacketTunnelActorTimings
Expand Down
2 changes: 1 addition & 1 deletion ios/PacketTunnelCore/Actor/PacketTunnelActorProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Foundation

public protocol PacketTunnelActorProtocol {
var state: State { get async }
var observedState: ObservedState { get async }

func reconnect(to nextRelay: NextRelay)
func notifyKeyRotation(date: Date?)
Expand Down
44 changes: 0 additions & 44 deletions ios/PacketTunnelCore/Actor/State+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,50 +37,6 @@ extension State {
}
}

var packetTunnelStatus: PacketTunnelStatus {
var status = PacketTunnelStatus()

switch self {
case let .connecting(connState),
let .connected(connState),
let .reconnecting(connState),
let .disconnecting(connState):
switch connState.networkReachability {
case .reachable:
status.isNetworkReachable = true
case .unreachable:
status.isNetworkReachable = false
case .undetermined:
// TODO: fix me
status.isNetworkReachable = true
}

status.numberOfFailedAttempts = connState.connectionAttemptCount
status.tunnelRelay = connState.selectedRelay.packetTunnelRelay

case .disconnected, .initial:
break

case let .error(blockedState):
status.blockedStateReason = blockedState.reason
}

return status
}

public var relayConstraints: RelayConstraints? {
switch self {
case let .connecting(connState), let .connected(connState), let .reconnecting(connState):
return connState.relayConstraints

case let .error(blockedState):
return blockedState.relayConstraints

case .initial, .disconnecting, .disconnected:
return nil
}
}

// MARK: - Logging

func logFormat() -> String {
Expand Down
Loading

0 comments on commit 5c13b7e

Please sign in to comment.