diff --git a/ios/MullvadMockData/MullvadMockData.h b/ios/MullvadMockData/MullvadMockData.h
new file mode 100644
index 000000000000..16454534518d
--- /dev/null
+++ b/ios/MullvadMockData/MullvadMockData.h
@@ -0,0 +1,19 @@
+//
+//  MullvadMockData.h
+//  MullvadMockData
+//
+//  Created by Mojgan on 2024-05-03.
+//  Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+//! Project version number for MullvadMockData.
+FOUNDATION_EXPORT double MullvadMockDataVersionNumber;
+
+//! Project version string for MullvadMockData.
+FOUNDATION_EXPORT const unsigned char MullvadMockDataVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <MullvadMockData/PublicHeader.h>
+
+
diff --git a/ios/MullvadVPNTests/MullvadREST/ApiHandlers/APIProxy+Stubs.swift b/ios/MullvadMockData/MullvadREST/APIProxy+Stubs.swift
similarity index 93%
rename from ios/MullvadVPNTests/MullvadREST/ApiHandlers/APIProxy+Stubs.swift
rename to ios/MullvadMockData/MullvadREST/APIProxy+Stubs.swift
index c802a8fa7154..654403944436 100644
--- a/ios/MullvadVPNTests/MullvadREST/ApiHandlers/APIProxy+Stubs.swift
+++ b/ios/MullvadMockData/MullvadREST/APIProxy+Stubs.swift
@@ -7,9 +7,9 @@
 //
 
 import Foundation
-@testable import MullvadREST
-@testable import MullvadTypes
-@testable import WireGuardKitTypes
+import MullvadREST
+import MullvadTypes
+import WireGuardKitTypes
 
 struct APIProxyStub: APIQuerying {
     func getAddressList(
diff --git a/ios/MullvadVPNTests/MullvadREST/ApiHandlers/AccessTokenManager+Stubs.swift b/ios/MullvadMockData/MullvadREST/AccessTokenManager+Stubs.swift
similarity index 88%
rename from ios/MullvadVPNTests/MullvadREST/ApiHandlers/AccessTokenManager+Stubs.swift
rename to ios/MullvadMockData/MullvadREST/AccessTokenManager+Stubs.swift
index a36719314efa..3275549148d4 100644
--- a/ios/MullvadVPNTests/MullvadREST/ApiHandlers/AccessTokenManager+Stubs.swift
+++ b/ios/MullvadMockData/MullvadREST/AccessTokenManager+Stubs.swift
@@ -7,8 +7,8 @@
 //
 
 import Foundation
-@testable import MullvadREST
-@testable import MullvadTypes
+import MullvadREST
+import MullvadTypes
 
 struct AccessTokenManagerStub: RESTAccessTokenManagement {
     func getAccessToken(
diff --git a/ios/MullvadVPNTests/MullvadREST/ApiHandlers/AccountsProxy+Stubs.swift b/ios/MullvadMockData/MullvadREST/AccountsProxy+Stubs.swift
similarity index 72%
rename from ios/MullvadVPNTests/MullvadREST/ApiHandlers/AccountsProxy+Stubs.swift
rename to ios/MullvadMockData/MullvadREST/AccountsProxy+Stubs.swift
index 76e5d17ccbde..a429cdeccf5c 100644
--- a/ios/MullvadVPNTests/MullvadREST/ApiHandlers/AccountsProxy+Stubs.swift
+++ b/ios/MullvadMockData/MullvadREST/AccountsProxy+Stubs.swift
@@ -7,14 +7,14 @@
 //
 
 import Foundation
-@testable import MullvadREST
-@testable import MullvadTypes
+import MullvadREST
+import MullvadTypes
 
 struct AccountsProxyStub: RESTAccountHandling {
     var createAccountResult: Result<REST.NewAccountData, Error>?
     func createAccount(
         retryStrategy: REST.RetryStrategy,
-        completion: @escaping MullvadREST.ProxyCompletionHandler<REST.NewAccountData>
+        completion: @escaping ProxyCompletionHandler<REST.NewAccountData>
     ) -> Cancellable {
         if let createAccountResult = createAccountResult {
             completion(createAccountResult)
@@ -24,7 +24,12 @@ struct AccountsProxyStub: RESTAccountHandling {
 
     func getAccountData(accountNumber: String) -> any RESTRequestExecutor<Account> {
         RESTRequestExecutorStub<Account>(success: {
-            Account(id: accountNumber, expiry: .distantFuture, maxDevices: 1, canAddDevices: true)
+            Account(
+                id: accountNumber,
+                expiry: Calendar.current.date(byAdding: .day, value: 38, to: Date())!,
+                maxDevices: 1,
+                canAddDevices: true
+            )
         })
     }
 
diff --git a/ios/MullvadVPNTests/MullvadREST/ApiHandlers/DevicesProxy+Stubs.swift b/ios/MullvadMockData/MullvadREST/DevicesProxy+Stubs.swift
similarity index 66%
rename from ios/MullvadVPNTests/MullvadREST/ApiHandlers/DevicesProxy+Stubs.swift
rename to ios/MullvadMockData/MullvadREST/DevicesProxy+Stubs.swift
index 69d0251f81d7..8511805460a2 100644
--- a/ios/MullvadVPNTests/MullvadREST/ApiHandlers/DevicesProxy+Stubs.swift
+++ b/ios/MullvadMockData/MullvadREST/DevicesProxy+Stubs.swift
@@ -7,19 +7,22 @@
 //
 
 import Foundation
-@testable import MullvadREST
-@testable import MullvadTypes
-@testable import WireGuardKitTypes
+import MullvadREST
+import MullvadTypes
+import WireGuardKitTypes
 
 struct DevicesProxyStub: DeviceHandling {
-    let mockDevice = Device.mock(publicKey: PrivateKey().publicKey)
+    var deviceResult: Result<Device, Error>?
     func getDevice(
         accountNumber: String,
         identifier: String,
         retryStrategy: REST.RetryStrategy,
         completion: @escaping ProxyCompletionHandler<Device>
     ) -> Cancellable {
-        AnyCancellable()
+        if let result = deviceResult {
+            completion(result)
+        }
+        return AnyCancellable()
     }
 
     func getDevices(
@@ -27,7 +30,15 @@ struct DevicesProxyStub: DeviceHandling {
         retryStrategy: REST.RetryStrategy,
         completion: @escaping ProxyCompletionHandler<[Device]>
     ) -> Cancellable {
-        AnyCancellable()
+        if let result = deviceResult {
+            switch result {
+            case let .success(success):
+                completion(.success([success]))
+            case let .failure(failure):
+                completion(.failure(failure))
+            }
+        }
+        return AnyCancellable()
     }
 
     func createDevice(
@@ -36,7 +47,9 @@ struct DevicesProxyStub: DeviceHandling {
         retryStrategy: REST.RetryStrategy,
         completion: @escaping ProxyCompletionHandler<Device>
     ) -> Cancellable {
-        completion(.success(mockDevice))
+        if let result = deviceResult {
+            completion(result)
+        }
         return AnyCancellable()
     }
 
@@ -57,6 +70,9 @@ struct DevicesProxyStub: DeviceHandling {
         retryStrategy: REST.RetryStrategy,
         completion: @escaping ProxyCompletionHandler<Device>
     ) -> Cancellable {
-        AnyCancellable()
+        if let result = deviceResult {
+            completion(result)
+        }
+        return AnyCancellable()
     }
 }
diff --git a/ios/MullvadMockData/MullvadREST/MockProxyFactory.swift b/ios/MullvadMockData/MullvadREST/MockProxyFactory.swift
new file mode 100644
index 000000000000..31ac5f637a54
--- /dev/null
+++ b/ios/MullvadMockData/MullvadREST/MockProxyFactory.swift
@@ -0,0 +1,52 @@
+//
+//  MockProxyFactory.swift
+//  MullvadMockData
+//
+//  Created by Mojgan on 2024-05-03.
+//  Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import MullvadREST
+import MullvadTypes
+import WireGuardKitTypes
+
+public struct MockProxyFactory: ProxyFactoryProtocol {
+    public var configuration: REST.AuthProxyConfiguration
+
+    public func createAPIProxy() -> any APIQuerying {
+        REST.APIProxy(configuration: configuration)
+    }
+
+    public func createAccountsProxy() -> any RESTAccountHandling {
+        AccountsProxyStub(createAccountResult: .success(.mockValue()))
+    }
+
+    public func createDevicesProxy() -> any DeviceHandling {
+        DevicesProxyStub(deviceResult: .success(Device.mock(publicKey: PrivateKey().publicKey)))
+    }
+
+    public static func makeProxyFactory(
+        transportProvider: any RESTTransportProvider,
+        addressCache: REST.AddressCache
+    ) -> any ProxyFactoryProtocol {
+        let basicConfiguration = REST.ProxyConfiguration(
+            transportProvider: transportProvider,
+            addressCacheStore: addressCache
+        )
+
+        let authenticationProxy = REST.AuthenticationProxy(
+            configuration: basicConfiguration
+        )
+        let accessTokenManager = REST.AccessTokenManager(
+            authenticationProxy: authenticationProxy
+        )
+
+        let authConfiguration = REST.AuthProxyConfiguration(
+            proxyConfiguration: basicConfiguration,
+            accessTokenManager: accessTokenManager
+        )
+
+        return MockProxyFactory(configuration: authConfiguration)
+    }
+}
diff --git a/ios/MullvadVPNTests/MullvadREST/ApiHandlers/RESTRequestExecutor+Stubs.swift b/ios/MullvadMockData/MullvadREST/RESTRequestExecutor+Stubs.swift
similarity index 74%
rename from ios/MullvadVPNTests/MullvadREST/ApiHandlers/RESTRequestExecutor+Stubs.swift
rename to ios/MullvadMockData/MullvadREST/RESTRequestExecutor+Stubs.swift
index cd7fad6c76aa..25aef84e5bd0 100644
--- a/ios/MullvadVPNTests/MullvadREST/ApiHandlers/RESTRequestExecutor+Stubs.swift
+++ b/ios/MullvadMockData/MullvadREST/RESTRequestExecutor+Stubs.swift
@@ -7,23 +7,27 @@
 //
 
 import Foundation
-@testable import MullvadREST
-@testable import MullvadTypes
+import MullvadREST
+import MullvadTypes
 
 struct RESTRequestExecutorStub<Success>: RESTRequestExecutor {
-    typealias Success = Success
-
     var success: (() -> Success)?
 
     func execute(completionHandler: @escaping (Result<Success, Error>) -> Void) -> Cancellable {
-        AnyCancellable()
+        if let result = success?() {
+            completionHandler(.success(result))
+        }
+        return AnyCancellable()
     }
 
     func execute(
         retryStrategy: REST.RetryStrategy,
         completionHandler: @escaping (Result<Success, Error>) -> Void
     ) -> Cancellable {
-        AnyCancellable()
+        if let result = success?() {
+            completionHandler(.success(result))
+        }
+        return AnyCancellable()
     }
 
     func execute() async throws -> Success {
diff --git a/ios/MullvadVPNTests/MullvadTypes/AccountMock.swift b/ios/MullvadMockData/MullvadTypes/AccountMock.swift
similarity index 82%
rename from ios/MullvadVPNTests/MullvadTypes/AccountMock.swift
rename to ios/MullvadMockData/MullvadTypes/AccountMock.swift
index 70ab1d10b358..85c10524385b 100644
--- a/ios/MullvadVPNTests/MullvadTypes/AccountMock.swift
+++ b/ios/MullvadMockData/MullvadTypes/AccountMock.swift
@@ -9,7 +9,7 @@
 import MullvadTypes
 
 extension Account {
-    static func mock(expiry: Date = .distantFuture) -> Account {
+    public static func mock(expiry: Date = .distantFuture) -> Account {
         Account(
             id: "account-id",
             expiry: expiry,
diff --git a/ios/MullvadVPNTests/MullvadTypes/DeviceMock.swift b/ios/MullvadMockData/MullvadTypes/DeviceMock.swift
similarity index 84%
rename from ios/MullvadVPNTests/MullvadTypes/DeviceMock.swift
rename to ios/MullvadMockData/MullvadTypes/DeviceMock.swift
index 9f17410dadfe..c6bff6bfb521 100644
--- a/ios/MullvadVPNTests/MullvadTypes/DeviceMock.swift
+++ b/ios/MullvadMockData/MullvadTypes/DeviceMock.swift
@@ -11,10 +11,10 @@ import MullvadTypes
 import WireGuardKitTypes
 
 extension Device {
-    static func mock(publicKey: PublicKey) -> Device {
+    public static func mock(publicKey: PublicKey) -> Device {
         Device(
             id: "device-id",
-            name: "Devicey McDeviceface",
+            name: "Secure Mole",
             pubkey: publicKey,
             hijackDNS: false,
             created: Date(),
diff --git a/ios/MullvadREST/ApiHandlers/RESTProxyFactory.swift b/ios/MullvadREST/ApiHandlers/RESTProxyFactory.swift
index 43201890cc02..c57c9d98d7b8 100644
--- a/ios/MullvadREST/ApiHandlers/RESTProxyFactory.swift
+++ b/ios/MullvadREST/ApiHandlers/RESTProxyFactory.swift
@@ -7,15 +7,27 @@
 //
 
 import Foundation
+public protocol ProxyFactoryProtocol {
+    var configuration: REST.AuthProxyConfiguration { get }
+
+    func createAPIProxy() -> APIQuerying
+    func createAccountsProxy() -> RESTAccountHandling
+    func createDevicesProxy() -> DeviceHandling
+
+    static func makeProxyFactory(
+        transportProvider: RESTTransportProvider,
+        addressCache: REST.AddressCache
+    ) -> ProxyFactoryProtocol
+}
 
 extension REST {
-    public final class ProxyFactory {
-        public let configuration: AuthProxyConfiguration
+    public final class ProxyFactory: ProxyFactoryProtocol {
+        public var configuration: AuthProxyConfiguration
 
-        public class func makeProxyFactory(
-            transportProvider: RESTTransportProvider,
-            addressCache: AddressCache
-        ) -> ProxyFactory {
+        public static func makeProxyFactory(
+            transportProvider: any RESTTransportProvider,
+            addressCache: REST.AddressCache
+        ) -> any ProxyFactoryProtocol {
             let basicConfiguration = REST.ProxyConfiguration(
                 transportProvider: transportProvider,
                 addressCacheStore: addressCache
diff --git a/ios/MullvadRESTTests/Mocks/TimeServerProxy.swift b/ios/MullvadRESTTests/Mocks/TimeServerProxy.swift
index 8013b86e33a4..76f8da18a8a5 100644
--- a/ios/MullvadRESTTests/Mocks/TimeServerProxy.swift
+++ b/ios/MullvadRESTTests/Mocks/TimeServerProxy.swift
@@ -40,9 +40,3 @@ final class TimeServerProxy: REST.Proxy<REST.ProxyConfiguration> {
 struct TimeResponse: Codable {
     var dateTime: Date
 }
-
-extension REST.ProxyFactory {
-    func createTimeServerProxy() -> TimeServerProxy {
-        return TimeServerProxy(configuration: configuration)
-    }
-}
diff --git a/ios/MullvadRESTTests/RequestExecutorTests.swift b/ios/MullvadRESTTests/RequestExecutorTests.swift
index d257007f8328..e2688f80eb74 100644
--- a/ios/MullvadRESTTests/RequestExecutorTests.swift
+++ b/ios/MullvadRESTTests/RequestExecutorTests.swift
@@ -27,7 +27,7 @@ final class RequestExecutorTests: XCTestCase {
             transportProvider: transportProvider,
             addressCache: addressCache
         )
-        timerServerProxy = proxyFactory.createTimeServerProxy()
+        timerServerProxy = TimeServerProxy(configuration: proxyFactory.configuration)
     }
 
     func testExecuteAsync() async throws {
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 27fc3db0b0da..4e2af6143719 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -39,11 +39,8 @@
 		06799AFC28F98EE300ACD94E /* AddressCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AC114128F8413A0037AF9A /* AddressCache.swift */; };
 		0697D6E728F01513007A9E99 /* TransportMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0697D6E628F01513007A9E99 /* TransportMonitor.swift */; };
 		06AC116228F94C450037AF9A /* ApplicationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BFA5CB22A7CE1F00A6173D /* ApplicationConfiguration.swift */; };
-		440E9F022BDA9CEC00B1FD11 /* ServerRelaysResponse+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C342C42ACC42130045F00E /* ServerRelaysResponse+Stubs.swift */; };
 		449872E12B7BBC5400094DDC /* TunnelSettingsUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449872E02B7BBC5400094DDC /* TunnelSettingsUpdate.swift */; };
 		449872E42B7CB96300094DDC /* TunnelSettingsUpdateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449872E32B7CB96300094DDC /* TunnelSettingsUpdateTests.swift */; };
-		449EB9FD2B95F8AD00DFA4EB /* DeviceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449EB9FC2B95F8AD00DFA4EB /* DeviceMock.swift */; };
-		449EB9FF2B95FF2500DFA4EB /* AccountMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449EB9FE2B95FF2500DFA4EB /* AccountMock.swift */; };
 		449EBA262B975B9700DFA4EB /* PostQuantumKeyReceiving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449EBA252B975B9700DFA4EB /* PostQuantumKeyReceiving.swift */; };
 		44B02E3B2BC5732D008EDF34 /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44B02E3A2BC5732D008EDF34 /* LoggingTests.swift */; };
 		44B02E3C2BC5B8A5008EDF34 /* Bundle+ProductVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5891BF1B25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift */; };
@@ -655,11 +652,6 @@
 		85EC620C2B838D10005AFFB5 /* MullvadAPIWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85557B132B5983CF00795FE1 /* MullvadAPIWrapper.swift */; };
 		85FB5A0C2B6903990015DCED /* WelcomePage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85FB5A0B2B6903990015DCED /* WelcomePage.swift */; };
 		85FB5A102B6960A30015DCED /* AccountDeletionPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85FB5A0F2B6960A30015DCED /* AccountDeletionPage.swift */; };
-		A900E9B82ACC5C2B00C95F67 /* AccountsProxy+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9B72ACC5C2B00C95F67 /* AccountsProxy+Stubs.swift */; };
-		A900E9BA2ACC5D0600C95F67 /* RESTRequestExecutor+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9B92ACC5D0600C95F67 /* RESTRequestExecutor+Stubs.swift */; };
-		A900E9BC2ACC609200C95F67 /* DevicesProxy+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9BB2ACC609200C95F67 /* DevicesProxy+Stubs.swift */; };
-		A900E9BE2ACC654100C95F67 /* APIProxy+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9BD2ACC654100C95F67 /* APIProxy+Stubs.swift */; };
-		A900E9C02ACC661900C95F67 /* AccessTokenManager+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9BF2ACC661900C95F67 /* AccessTokenManager+Stubs.swift */; };
 		A90763B02B2857D50045ADF0 /* Socks5ConnectCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90763A02B2857D50045ADF0 /* Socks5ConnectCommand.swift */; };
 		A90763B12B2857D50045ADF0 /* Socks5Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90763A12B2857D50045ADF0 /* Socks5Endpoint.swift */; };
 		A90763B22B2857D50045ADF0 /* Socks5EndpointReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90763A22B2857D50045ADF0 /* Socks5EndpointReader.swift */; };
@@ -817,7 +809,6 @@
 		A9BFB0012BD00B7F00F2BCA1 /* CustomListPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BFB0002BD00B7F00F2BCA1 /* CustomListPage.swift */; };
 		A9C342C12ACC37E30045F00E /* TunnelStatusBlockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E0317D2ACC32920095D843 /* TunnelStatusBlockObserver.swift */; };
 		A9C342C32ACC3EE90045F00E /* RelayCacheTracker+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C342C22ACC3EE90045F00E /* RelayCacheTracker+Stubs.swift */; };
-		A9C342C52ACC42130045F00E /* ServerRelaysResponse+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C342C42ACC42130045F00E /* ServerRelaysResponse+Stubs.swift */; };
 		A9D99B9A2A1F7C3200DE27D3 /* RESTTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FAE67D28F83CA50033DD93 /* RESTTransport.swift */; };
 		A9DF789B2B7D1DF10094E4AD /* mullvad-api.h in Headers */ = {isa = PBXBuildFile; fileRef = 01EF6F2D2B6A51B100125696 /* mullvad-api.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		A9DF789D2B7D1E8B0094E4AD /* LoggedInWithTimeUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 859089692B61763B003AF5F5 /* LoggedInWithTimeUITestCase.swift */; };
@@ -880,6 +871,23 @@
 		F09D04BD2AEBB7C5003D4F89 /* OutgoingConnectionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04BC2AEBB7C5003D4F89 /* OutgoingConnectionService.swift */; };
 		F09D04C02AF39D63003D4F89 /* OutgoingConnectionServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04BF2AF39D63003D4F89 /* OutgoingConnectionServiceTests.swift */; };
 		F09D04C12AF39EA2003D4F89 /* OutgoingConnectionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04BC2AEBB7C5003D4F89 /* OutgoingConnectionService.swift */; };
+		F0ACE30D2BE4E478006D5333 /* MullvadMockData.h in Headers */ = {isa = PBXBuildFile; fileRef = F0ACE30A2BE4E478006D5333 /* MullvadMockData.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		F0ACE3102BE4E478006D5333 /* MullvadMockData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0ACE3082BE4E478006D5333 /* MullvadMockData.framework */; };
+		F0ACE3112BE4E478006D5333 /* MullvadMockData.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F0ACE3082BE4E478006D5333 /* MullvadMockData.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		F0ACE31D2BE4E4F2006D5333 /* DevicesProxy+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9BB2ACC609200C95F67 /* DevicesProxy+Stubs.swift */; };
+		F0ACE31E2BE4E4F2006D5333 /* AccountsProxy+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9B72ACC5C2B00C95F67 /* AccountsProxy+Stubs.swift */; };
+		F0ACE3202BE4E4F2006D5333 /* AccessTokenManager+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9BF2ACC661900C95F67 /* AccessTokenManager+Stubs.swift */; };
+		F0ACE3222BE4E4F2006D5333 /* APIProxy+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9BD2ACC654100C95F67 /* APIProxy+Stubs.swift */; };
+		F0ACE3232BE4E53B006D5333 /* MullvadREST.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06799ABC28F98E1D00ACD94E /* MullvadREST.framework */; platformFilter = ios; };
+		F0ACE3262BE4E6C7006D5333 /* MullvadTypes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223D5294C8E5E0029F5F8 /* MullvadTypes.framework */; platformFilter = ios; };
+		F0ACE3282BE4E712006D5333 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = F0ACE3272BE4E712006D5333 /* WireGuardKitTypes */; };
+		F0ACE32A2BE4E712006D5333 /* WireGuardKitTypes in Embed Frameworks */ = {isa = PBXBuildFile; productRef = F0ACE3272BE4E712006D5333 /* WireGuardKitTypes */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
+		F0ACE32C2BE4E77E006D5333 /* DeviceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449EB9FC2B95F8AD00DFA4EB /* DeviceMock.swift */; };
+		F0ACE32D2BE4E784006D5333 /* AccountMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449EB9FE2B95FF2500DFA4EB /* AccountMock.swift */; };
+		F0ACE32F2BE4EA8B006D5333 /* MockProxyFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0ACE32E2BE4EA8B006D5333 /* MockProxyFactory.swift */; };
+		F0ACE3332BE516F1006D5333 /* RESTRequestExecutor+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9B92ACC5D0600C95F67 /* RESTRequestExecutor+Stubs.swift */; };
+		F0ACE3362BE517D6006D5333 /* ServerRelaysResponse+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0ACE3342BE51745006D5333 /* ServerRelaysResponse+Stubs.swift */; };
+		F0ACE3372BE517F1006D5333 /* ServerRelaysResponse+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0ACE3342BE51745006D5333 /* ServerRelaysResponse+Stubs.swift */; };
 		F0B0E6972AFE6E7E001DC66B /* XCTest+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0B0E6962AFE6E7E001DC66B /* XCTest+Async.swift */; };
 		F0BE65372B9F136A005CC385 /* LocationSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0BE65362B9F136A005CC385 /* LocationSectionHeaderView.swift */; };
 		F0C2AEFD2A0BB5CC00986207 /* NotificationProviderIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0C2AEFC2A0BB5CC00986207 /* NotificationProviderIdentifier.swift */; };
@@ -911,6 +919,9 @@
 		F0E8E4C92A604E7400ED26A3 /* AccountDeletionInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E8E4C82A604E7400ED26A3 /* AccountDeletionInteractor.swift */; };
 		F0EF50D32A8FA47E0031E8DF /* ChangeLogInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0EF50D22A8FA47E0031E8DF /* ChangeLogInteractor.swift */; };
 		F0EF50D52A949F8E0031E8DF /* ChangeLogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0EF50D42A949F8E0031E8DF /* ChangeLogViewModel.swift */; };
+		F0FADDEA2BE90AAA000D0B02 /* LaunchArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0F1EF8C2BE8FF0A00CED01D /* LaunchArguments.swift */; };
+		F0FADDEB2BE90AAE000D0B02 /* LaunchArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0F1EF8C2BE8FF0A00CED01D /* LaunchArguments.swift */; };
+		F0FADDEC2BE90AB0000D0B02 /* LaunchArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0F1EF8C2BE8FF0A00CED01D /* LaunchArguments.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -1194,6 +1205,13 @@
 			remoteGlobalIDString = 06799ABB28F98E1D00ACD94E;
 			remoteInfo = MullvadREST;
 		};
+		F0ACE30E2BE4E478006D5333 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 58CE5E58224146200008646E /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = F0ACE3072BE4E478006D5333;
+			remoteInfo = MullvadMockData;
+		};
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -1203,6 +1221,7 @@
 			dstPath = "";
 			dstSubfolderSpec = 10;
 			files = (
+				F0ACE3112BE4E478006D5333 /* MullvadMockData.framework in Embed Frameworks */,
 				58D223E7294C8F120029F5F8 /* MullvadTypes.framework in Embed Frameworks */,
 				58D223FA294C8FF10029F5F8 /* MullvadLogging.framework in Embed Frameworks */,
 				58B2FDDA2AA71D2A003EB5C6 /* MullvadSettings.framework in Embed Frameworks */,
@@ -1286,6 +1305,17 @@
 			name = "Embed Frameworks";
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		F0ACE3292BE4E712006D5333 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				F0ACE32A2BE4E712006D5333 /* WireGuardKitTypes in Embed Frameworks */,
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
@@ -1986,7 +2016,6 @@
 		A9BFAFFE2BD004ED00F2BCA1 /* CustomListsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListsTests.swift; sourceTree = "<group>"; };
 		A9BFB0002BD00B7F00F2BCA1 /* CustomListPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListPage.swift; sourceTree = "<group>"; };
 		A9C342C22ACC3EE90045F00E /* RelayCacheTracker+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RelayCacheTracker+Stubs.swift"; sourceTree = "<group>"; };
-		A9C342C42ACC42130045F00E /* ServerRelaysResponse+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ServerRelaysResponse+Stubs.swift"; sourceTree = "<group>"; };
 		A9CF11FC2A0518E7001D9565 /* AddressCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressCacheTests.swift; sourceTree = "<group>"; };
 		A9D96B192A8247C100A5C673 /* MigrationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationManager.swift; sourceTree = "<group>"; };
 		A9E031762ACB08950095D843 /* UIApplication+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Extensions.swift"; sourceTree = "<group>"; };
@@ -2040,6 +2069,10 @@
 		F09D04BA2AE95396003D4F89 /* URLSessionStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionStub.swift; sourceTree = "<group>"; };
 		F09D04BC2AEBB7C5003D4F89 /* OutgoingConnectionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutgoingConnectionService.swift; sourceTree = "<group>"; };
 		F09D04BF2AF39D63003D4F89 /* OutgoingConnectionServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutgoingConnectionServiceTests.swift; sourceTree = "<group>"; };
+		F0ACE3082BE4E478006D5333 /* MullvadMockData.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MullvadMockData.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		F0ACE30A2BE4E478006D5333 /* MullvadMockData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MullvadMockData.h; sourceTree = "<group>"; };
+		F0ACE32E2BE4EA8B006D5333 /* MockProxyFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockProxyFactory.swift; sourceTree = "<group>"; };
+		F0ACE3342BE51745006D5333 /* ServerRelaysResponse+Stubs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ServerRelaysResponse+Stubs.swift"; sourceTree = "<group>"; };
 		F0B0E6962AFE6E7E001DC66B /* XCTest+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCTest+Async.swift"; sourceTree = "<group>"; };
 		F0BE65362B9F136A005CC385 /* LocationSectionHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationSectionHeaderView.swift; sourceTree = "<group>"; };
 		F0C2AEFC2A0BB5CC00986207 /* NotificationProviderIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationProviderIdentifier.swift; sourceTree = "<group>"; };
@@ -2067,6 +2100,7 @@
 		F0E8E4C82A604E7400ED26A3 /* AccountDeletionInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeletionInteractor.swift; sourceTree = "<group>"; };
 		F0EF50D22A8FA47E0031E8DF /* ChangeLogInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangeLogInteractor.swift; sourceTree = "<group>"; };
 		F0EF50D42A949F8E0031E8DF /* ChangeLogViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeLogViewModel.swift; sourceTree = "<group>"; };
+		F0F1EF8C2BE8FF0A00CED01D /* LaunchArguments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchArguments.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -2158,6 +2192,7 @@
 				58D223E6294C8F120029F5F8 /* MullvadTypes.framework in Frameworks */,
 				7ABCA5B32A9349F20044A708 /* Routing.framework in Frameworks */,
 				58D223CC294C8BCB0029F5F8 /* Operations.framework in Frameworks */,
+				F0ACE3102BE4E478006D5333 /* MullvadMockData.framework in Frameworks */,
 				06799AD128F98E1D00ACD94E /* MullvadREST.framework in Frameworks */,
 				58B2FDD92AA71D2A003EB5C6 /* MullvadSettings.framework in Frameworks */,
 			);
@@ -2242,6 +2277,16 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		F0ACE3052BE4E478006D5333 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				F0ACE3262BE4E6C7006D5333 /* MullvadTypes.framework in Frameworks */,
+				F0ACE3282BE4E712006D5333 /* WireGuardKitTypes in Frameworks */,
+				F0ACE3232BE4E53B006D5333 /* MullvadREST.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
@@ -2313,13 +2358,8 @@
 		440E9EF42BDA943B00B1FD11 /* ApiHandlers */ = {
 			isa = PBXGroup;
 			children = (
-				A900E9BF2ACC661900C95F67 /* AccessTokenManager+Stubs.swift */,
-				A900E9B72ACC5C2B00C95F67 /* AccountsProxy+Stubs.swift */,
-				A900E9BD2ACC654100C95F67 /* APIProxy+Stubs.swift */,
+				F0ACE3342BE51745006D5333 /* ServerRelaysResponse+Stubs.swift */,
 				A9CF11FC2A0518E7001D9565 /* AddressCacheTests.swift */,
-				A900E9BB2ACC609200C95F67 /* DevicesProxy+Stubs.swift */,
-				A900E9B92ACC5D0600C95F67 /* RESTRequestExecutor+Stubs.swift */,
-				A9C342C42ACC42130045F00E /* ServerRelaysResponse+Stubs.swift */,
 			);
 			path = ApiHandlers;
 			sourceTree = "<group>";
@@ -2362,8 +2402,6 @@
 		440E9EF92BDA95FC00B1FD11 /* MullvadTypes */ = {
 			isa = PBXGroup;
 			children = (
-				449EB9FE2B95FF2500DFA4EB /* AccountMock.swift */,
-				449EB9FC2B95F8AD00DFA4EB /* DeviceMock.swift */,
 				58FBFBF0291630700020E046 /* DurationTests.swift */,
 				58C3FA672A385C89006A450A /* FileCacheTests.swift */,
 				582A8A3928BCE19B00D0F9FB /* FixedWidthIntegerArithmeticsTests.swift */,
@@ -2484,7 +2522,6 @@
 		581943F228F8014500B0CB5E /* MullvadTypes */ = {
 			isa = PBXGroup;
 			children = (
-				449EBA242B975B7C00DFA4EB /* Protocols */,
 				584D26BE270C550B004EA533 /* AnyIPAddress.swift */,
 				586A951329013235007BAF2B /* AnyIPEndpoint.swift */,
 				06AC113628F83FD70037AF9A /* Cancellable.swift */,
@@ -2504,6 +2541,7 @@
 				58D223D7294C8E5E0029F5F8 /* MullvadTypes.h */,
 				A97FF54F2A0D2FFC00900996 /* NSFileCoordinator+Extensions.swift */,
 				58CAFA01298530DC00BE19F7 /* Promise.swift */,
+				449EBA242B975B7C00DFA4EB /* Protocols */,
 				5898D2B12902A6DE00EB5EBA /* RelayConstraint.swift */,
 				58781CC822AE7CA8009B9D8E /* RelayConstraints.swift */,
 				7AF9BE8A2A321BEF00DBFEDB /* RelayFilter.swift */,
@@ -3098,6 +3136,7 @@
 			children = (
 				58BFA5CB22A7CE1F00A6173D /* ApplicationConfiguration.swift */,
 				58C76A072A33850E00100D75 /* ApplicationTarget.swift */,
+				F0F1EF8C2BE8FF0A00CED01D /* LaunchArguments.swift */,
 			);
 			path = Shared;
 			sourceTree = "<group>";
@@ -3317,32 +3356,33 @@
 		58CE5E57224146200008646E = {
 			isa = PBXGroup;
 			children = (
-				8556EB512B9A1C6900D26DD4 /* MullvadApi.swift */,
-				01EF6F2D2B6A51B100125696 /* mullvad-api.h */,
 				58F3C0A824A50C0E003E76BE /* Assets */,
 				58ECD29023F178FD004298B6 /* Configurations */,
-				589A454A28DDF59B00565204 /* Shared */,
-				58CE5E62224146200008646E /* MullvadVPN */,
-				58D0C79423F1CE7000FE9BA7 /* MullvadVPNScreenshots */,
-				58B0A2A1238EE67E00BC001D /* MullvadVPNTests */,
-				852969262B4D9C1F007EAD4C /* MullvadVPNUITests */,
+				584F991F2902CBDD001F858D /* Frameworks */,
+				01EF6F2D2B6A51B100125696 /* mullvad-api.h */,
+				8556EB512B9A1C6900D26DD4 /* MullvadApi.swift */,
 				58D223F4294C8FF00029F5F8 /* MullvadLogging */,
-				581943F228F8014500B0CB5E /* MullvadTypes */,
+				F0ACE3092BE4E478006D5333 /* MullvadMockData */,
 				06799ABD28F98E1D00ACD94E /* MullvadREST */,
 				58FBFBE7291622580020E046 /* MullvadRESTTests */,
 				58B2FDD42AA71D2A003EB5C6 /* MullvadSettings */,
+				581943F228F8014500B0CB5E /* MullvadTypes */,
+				58CE5E62224146200008646E /* MullvadVPN */,
+				58D0C79423F1CE7000FE9BA7 /* MullvadVPNScreenshots */,
+				58B0A2A1238EE67E00BC001D /* MullvadVPNTests */,
+				852969262B4D9C1F007EAD4C /* MullvadVPNUITests */,
 				58D223A6294C8A490029F5F8 /* Operations */,
 				589A455328E094B300565204 /* OperationsTests */,
 				58CE5E7A224146470008646E /* PacketTunnel */,
-				584023202A406BF5007B27AC /* TunnelObfuscation */,
-				58695A9E2A4ADA9200328DB3 /* TunnelObfuscationTests */,
-				7A83C3FC2A55B39500DFB83A /* TestPlans */,
 				58C7A4372A863F450060C66F /* PacketTunnelCore */,
 				58C7A4432A863F490060C66F /* PacketTunnelCoreTests */,
+				58CE5E61224146200008646E /* Products */,
 				7A88DCCF2A8FABBE00D2FF0E /* Routing */,
 				7A88DCDD2A8FABBE00D2FF0E /* RoutingTests */,
-				58CE5E61224146200008646E /* Products */,
-				584F991F2902CBDD001F858D /* Frameworks */,
+				589A454A28DDF59B00565204 /* Shared */,
+				7A83C3FC2A55B39500DFB83A /* TestPlans */,
+				584023202A406BF5007B27AC /* TunnelObfuscation */,
+				58695A9E2A4ADA9200328DB3 /* TunnelObfuscationTests */,
 			);
 			sourceTree = "<group>";
 		};
@@ -3367,6 +3407,7 @@
 				7A88DCD72A8FABBE00D2FF0E /* RoutingTests.xctest */,
 				58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */,
 				852969252B4D9C1F007EAD4C /* MullvadVPNUITests.xctest */,
+				F0ACE3082BE4E478006D5333 /* MullvadMockData.framework */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -3911,6 +3952,38 @@
 			path = GeneralAPIs;
 			sourceTree = "<group>";
 		};
+		F0ACE3092BE4E478006D5333 /* MullvadMockData */ = {
+			isa = PBXGroup;
+			children = (
+				F0ACE30A2BE4E478006D5333 /* MullvadMockData.h */,
+				F0ACE3172BE4E487006D5333 /* MullvadREST */,
+				F0ACE32B2BE4E748006D5333 /* MullvadTypes */,
+			);
+			path = MullvadMockData;
+			sourceTree = "<group>";
+		};
+		F0ACE3172BE4E487006D5333 /* MullvadREST */ = {
+			isa = PBXGroup;
+			children = (
+				A900E9BF2ACC661900C95F67 /* AccessTokenManager+Stubs.swift */,
+				A900E9B72ACC5C2B00C95F67 /* AccountsProxy+Stubs.swift */,
+				A900E9BD2ACC654100C95F67 /* APIProxy+Stubs.swift */,
+				A900E9BB2ACC609200C95F67 /* DevicesProxy+Stubs.swift */,
+				F0ACE32E2BE4EA8B006D5333 /* MockProxyFactory.swift */,
+				A900E9B92ACC5D0600C95F67 /* RESTRequestExecutor+Stubs.swift */,
+			);
+			path = MullvadREST;
+			sourceTree = "<group>";
+		};
+		F0ACE32B2BE4E748006D5333 /* MullvadTypes */ = {
+			isa = PBXGroup;
+			children = (
+				449EB9FE2B95FF2500DFA4EB /* AccountMock.swift */,
+				449EB9FC2B95F8AD00DFA4EB /* DeviceMock.swift */,
+			);
+			path = MullvadTypes;
+			sourceTree = "<group>";
+		};
 		F0DC779F2B2222D20087F09D /* Relay */ = {
 			isa = PBXGroup;
 			children = (
@@ -4102,6 +4175,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		F0ACE3032BE4E478006D5333 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				F0ACE30D2BE4E478006D5333 /* MullvadMockData.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXHeadersBuildPhase section */
 
 /* Begin PBXLegacyTarget section */
@@ -4320,6 +4401,7 @@
 				58C7A4482A863F490060C66F /* PBXTargetDependency */,
 				7ABCA5B62A9349F20044A708 /* PBXTargetDependency */,
 				58B2FDD82AA71D2A003EB5C6 /* PBXTargetDependency */,
+				F0ACE30F2BE4E478006D5333 /* PBXTargetDependency */,
 			);
 			name = MullvadVPN;
 			packageProductDependencies = (
@@ -4519,6 +4601,28 @@
 			productReference = 852969252B4D9C1F007EAD4C /* MullvadVPNUITests.xctest */;
 			productType = "com.apple.product-type.bundle.ui-testing";
 		};
+		F0ACE3072BE4E478006D5333 /* MullvadMockData */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = F0ACE3162BE4E479006D5333 /* Build configuration list for PBXNativeTarget "MullvadMockData" */;
+			buildPhases = (
+				F0ACE3032BE4E478006D5333 /* Headers */,
+				F0ACE3042BE4E478006D5333 /* Sources */,
+				F0ACE3052BE4E478006D5333 /* Frameworks */,
+				F0ACE3062BE4E478006D5333 /* Resources */,
+				F0ACE3292BE4E712006D5333 /* Embed Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = MullvadMockData;
+			packageProductDependencies = (
+				F0ACE3272BE4E712006D5333 /* WireGuardKitTypes */,
+			);
+			productName = MullvadMockData;
+			productReference = F0ACE3082BE4E478006D5333 /* MullvadMockData.framework */;
+			productType = "com.apple.product-type.framework";
+		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -4605,6 +4709,9 @@
 						CreatedOnToolsVersion = 15.1;
 						TestTargetID = 58CE5E5F224146200008646E;
 					};
+					F0ACE3072BE4E478006D5333 = {
+						CreatedOnToolsVersion = 15.3;
+					};
 				};
 			};
 			buildConfigurationList = 58CE5E5B224146200008646E /* Build configuration list for PBXProject "MullvadVPN" */;
@@ -4643,6 +4750,7 @@
 				58B2FDD22AA71D2A003EB5C6 /* MullvadSettings */,
 				7A88DCCD2A8FABBE00D2FF0E /* Routing */,
 				7A88DCD62A8FABBE00D2FF0E /* RoutingTests */,
+				F0ACE3072BE4E478006D5333 /* MullvadMockData */,
 			);
 		};
 /* End PBXProject section */
@@ -4774,6 +4882,13 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		F0ACE3062BE4E478006D5333 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
@@ -5003,7 +5118,6 @@
 			files = (
 				A9A5FA432ACB05F20083449F /* UIColor+Helpers.swift in Sources */,
 				A9A5FA3D2ACB05D90083449F /* DeviceCheck.swift in Sources */,
-				A900E9B82ACC5C2B00C95F67 /* AccountsProxy+Stubs.swift in Sources */,
 				A9A5FA3E2ACB05D90083449F /* DeviceCheckOperation.swift in Sources */,
 				44DD7D272B6D18FB0005F67F /* MockTunnelInteractor.swift in Sources */,
 				A9A5FA3F2ACB05D90083449F /* DeviceCheckRemoteService.swift in Sources */,
@@ -5013,13 +5127,11 @@
 				7A5869C32B5820CE00640D27 /* IPOverrideRepositoryTests.swift in Sources */,
 				A9A5FA392ACB05910083449F /* UIColor+Palette.swift in Sources */,
 				A9A5FA3A2ACB05910083449F /* UIEdgeInsets+Extensions.swift in Sources */,
-				A9C342C52ACC42130045F00E /* ServerRelaysResponse+Stubs.swift in Sources */,
 				A9A5FA3B2ACB05910083449F /* UIMetrics.swift in Sources */,
 				58B07C182AEFDD6C00A09625 /* StoreTransactionLog.swift in Sources */,
 				A9A5FA382ACB05600083449F /* InputTextFormatter.swift in Sources */,
 				A9A5FA372ACB052D0083449F /* ApplicationTarget.swift in Sources */,
 				A9A5F9E12ACB05160083449F /* AddressCacheTracker.swift in Sources */,
-				A900E9BC2ACC609200C95F67 /* DevicesProxy+Stubs.swift in Sources */,
 				A9A5F9E22ACB05160083449F /* BackgroundTask.swift in Sources */,
 				A9A5F9E32ACB05160083449F /* AccountDataThrottling.swift in Sources */,
 				A9A5F9E42ACB05160083449F /* AppPreferences.swift in Sources */,
@@ -5060,15 +5172,12 @@
 				A9A5F9FE2ACB05160083449F /* NotificationManager.swift in Sources */,
 				A9A5F9FF2ACB05160083449F /* NotificationManagerDelegate.swift in Sources */,
 				7A9BE5AD2B90DF2D00E2A7D0 /* AllLocationsDataSourceTests.swift in Sources */,
-				A900E9BE2ACC654100C95F67 /* APIProxy+Stubs.swift in Sources */,
-				A900E9BA2ACC5D0600C95F67 /* RESTRequestExecutor+Stubs.swift in Sources */,
 				A9A5FA002ACB05160083449F /* ProductsRequestOperation.swift in Sources */,
 				A9A5FA012ACB05160083449F /* RelayCacheTrackerObserver.swift in Sources */,
 				A9A5FA022ACB05160083449F /* RelayCacheTracker.swift in Sources */,
 				A9A5FA032ACB05160083449F /* SimulatorTunnelInfo.swift in Sources */,
 				A9A5FA042ACB05160083449F /* SimulatorTunnelProvider.swift in Sources */,
 				A9A5FA052ACB05160083449F /* SimulatorTunnelProviderHost.swift in Sources */,
-				A900E9C02ACC661900C95F67 /* AccessTokenManager+Stubs.swift in Sources */,
 				A9E0317A2ACB0AE70095D843 /* UIApplication+Stubs.swift in Sources */,
 				A9A5FA062ACB05160083449F /* SimulatorTunnelProviderManager.swift in Sources */,
 				A9A5FA072ACB05160083449F /* SimulatorVPNConnection.swift in Sources */,
@@ -5122,7 +5231,6 @@
 				A9A5FA272ACB05160083449F /* VPNConnectionProtocol.swift in Sources */,
 				A9A5FA282ACB05160083449F /* WgKeyRotation.swift in Sources */,
 				449872E42B7CB96300094DDC /* TunnelSettingsUpdateTests.swift in Sources */,
-				449EB9FD2B95F8AD00DFA4EB /* DeviceMock.swift in Sources */,
 				A9A5FA292ACB05160083449F /* AddressCacheTests.swift in Sources */,
 				A9B6AC182ADE8F4300F7802A /* MigrationManagerTests.swift in Sources */,
 				7A9BE5AB2B909A1700E2A7D0 /* LocationDataSourceProtocol.swift in Sources */,
@@ -5138,7 +5246,7 @@
 				A9A5FA302ACB05160083449F /* InputTextFormatterTests.swift in Sources */,
 				44B02E3B2BC5732D008EDF34 /* LoggingTests.swift in Sources */,
 				F0B0E6972AFE6E7E001DC66B /* XCTest+Async.swift in Sources */,
-				449EB9FF2B95FF2500DFA4EB /* AccountMock.swift in Sources */,
+				F0ACE3362BE517D6006D5333 /* ServerRelaysResponse+Stubs.swift in Sources */,
 				7ADCB2DA2B6A730400C88F89 /* IPOverrideRepositoryStub.swift in Sources */,
 				A9A5FA312ACB05160083449F /* MockFileCache.swift in Sources */,
 				A9A5FA322ACB05160083449F /* RelayCacheTests.swift in Sources */,
@@ -5264,7 +5372,7 @@
 				A97D25B02B0BB5C400946B2D /* ProtocolObfuscationStub.swift in Sources */,
 				7A3FD1B72AD54ABD0042BEA6 /* AnyTransport.swift in Sources */,
 				58FE25F22AA77674003D1918 /* SettingsReaderStub.swift in Sources */,
-				440E9F022BDA9CEC00B1FD11 /* ServerRelaysResponse+Stubs.swift in Sources */,
+				F0ACE3372BE517F1006D5333 /* ServerRelaysResponse+Stubs.swift in Sources */,
 				58F7753D2AB8473200425B47 /* BlockedStateErrorMapperStub.swift in Sources */,
 				58FE25D42AA729B5003D1918 /* PacketTunnelActorTests.swift in Sources */,
 				7A3FD1B52AD4465A0042BEA6 /* AppMessageHandlerTests.swift in Sources */,
@@ -5510,6 +5618,7 @@
 				580909D32876D09A0078138D /* RevokedDeviceViewController.swift in Sources */,
 				58FF9FF02B07C4D300E4C97D /* PersistentAccessMethod+ViewModel.swift in Sources */,
 				58CEB2FD2AFD19D300E6E088 /* UITableView+ReuseIdentifier.swift in Sources */,
+				F0FADDEA2BE90AAA000D0B02 /* LaunchArguments.swift in Sources */,
 				5835B7CC233B76CB0096D79F /* TunnelManager.swift in Sources */,
 				588D7EDE2AF3A585005DF40A /* ListAccessMethodItem.swift in Sources */,
 				5827B0B02B0F4CCD00CCBBA1 /* ListAccessMethodViewControllerDelegate.swift in Sources */,
@@ -5670,6 +5779,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				58D0C7A223F1CECF00FE9BA7 /* MullvadVPNScreenshots.swift in Sources */,
+				F0FADDEB2BE90AAE000D0B02 /* LaunchArguments.swift in Sources */,
 				7A0B311F2B303A11004B12E0 /* AccessbilityIdentifier.swift in Sources */,
 				58D0C79E23F1CEBA00FE9BA7 /* SnapshotHelper.swift in Sources */,
 			);
@@ -5828,6 +5938,7 @@
 				85FB5A0C2B6903990015DCED /* WelcomePage.swift in Sources */,
 				852A26462BA9C9CB006EB9C8 /* DNSSettingsPage.swift in Sources */,
 				A998DA812BD147AD001D61A2 /* ListCustomListsPage.swift in Sources */,
+				F0FADDEC2BE90AB0000D0B02 /* LaunchArguments.swift in Sources */,
 				850201DF2B5040A500EF8C96 /* TunnelControlPage.swift in Sources */,
 				8542CE242B95F7B9006FCA14 /* VPNSettingsPage.swift in Sources */,
 				85557B1E2B5FB8C700795FE1 /* HeaderBar.swift in Sources */,
@@ -5837,6 +5948,21 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		F0ACE3042BE4E478006D5333 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				F0ACE31D2BE4E4F2006D5333 /* DevicesProxy+Stubs.swift in Sources */,
+				F0ACE31E2BE4E4F2006D5333 /* AccountsProxy+Stubs.swift in Sources */,
+				F0ACE3202BE4E4F2006D5333 /* AccessTokenManager+Stubs.swift in Sources */,
+				F0ACE32C2BE4E77E006D5333 /* DeviceMock.swift in Sources */,
+				F0ACE3222BE4E4F2006D5333 /* APIProxy+Stubs.swift in Sources */,
+				F0ACE3332BE516F1006D5333 /* RESTRequestExecutor+Stubs.swift in Sources */,
+				F0ACE32D2BE4E784006D5333 /* AccountMock.swift in Sources */,
+				F0ACE32F2BE4EA8B006D5333 /* MockProxyFactory.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
@@ -6047,6 +6173,11 @@
 			target = 06799ABB28F98E1D00ACD94E /* MullvadREST */;
 			targetProxy = F04F959E2B21D02700431E08 /* PBXContainerItemProxy */;
 		};
+		F0ACE30F2BE4E478006D5333 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = F0ACE3072BE4E478006D5333 /* MullvadMockData */;
+			targetProxy = F0ACE30E2BE4E478006D5333 /* PBXContainerItemProxy */;
+		};
 /* End PBXTargetDependency section */
 
 /* Begin XCBuildConfiguration section */
@@ -8421,6 +8552,171 @@
 			};
 			name = MockRelease;
 		};
+		F0ACE3122BE4E478006D5333 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				BUILD_LIBRARY_FOR_DISTRIBUTION = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 1;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				ENABLE_MODULE_VERIFIER = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu17;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Mullvad VPN AB. All rights reserved.";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 14.2;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+				MARKETING_VERSION = 1.0;
+				MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+				MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
+				PRODUCT_BUNDLE_IDENTIFIER = Mojgan.MullvadMockData;
+				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+				SKIP_INSTALL = YES;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_INSTALL_OBJC_HEADER = NO;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Debug;
+		};
+		F0ACE3132BE4E478006D5333 /* Staging */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				BUILD_LIBRARY_FOR_DISTRIBUTION = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 1;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				ENABLE_MODULE_VERIFIER = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu17;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Mullvad VPN AB. All rights reserved.";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 14.2;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+				MARKETING_VERSION = 1.0;
+				MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+				MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
+				PRODUCT_BUNDLE_IDENTIFIER = Mojgan.MullvadMockData;
+				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+				SKIP_INSTALL = YES;
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_INSTALL_OBJC_HEADER = NO;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Staging;
+		};
+		F0ACE3142BE4E478006D5333 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				BUILD_LIBRARY_FOR_DISTRIBUTION = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 1;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				ENABLE_MODULE_VERIFIER = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu17;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Mullvad VPN AB. All rights reserved.";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 14.2;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+				MARKETING_VERSION = 1.0;
+				MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+				MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
+				PRODUCT_BUNDLE_IDENTIFIER = Mojgan.MullvadMockData;
+				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+				SKIP_INSTALL = YES;
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_INSTALL_OBJC_HEADER = NO;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Release;
+		};
+		F0ACE3152BE4E478006D5333 /* MockRelease */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				BUILD_LIBRARY_FOR_DISTRIBUTION = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 1;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				ENABLE_MODULE_VERIFIER = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu17;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Mullvad VPN AB. All rights reserved.";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 14.2;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+				MARKETING_VERSION = 1.0;
+				MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+				MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
+				PRODUCT_BUNDLE_IDENTIFIER = Mojgan.MullvadMockData;
+				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+				SKIP_INSTALL = YES;
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_INSTALL_OBJC_HEADER = NO;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = MockRelease;
+		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -8644,6 +8940,17 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		F0ACE3162BE4E479006D5333 /* Build configuration list for PBXNativeTarget "MullvadMockData" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				F0ACE3122BE4E478006D5333 /* Debug */,
+				F0ACE3132BE4E478006D5333 /* Staging */,
+				F0ACE3142BE4E478006D5333 /* Release */,
+				F0ACE3152BE4E478006D5333 /* MockRelease */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 
 /* Begin XCRemoteSwiftPackageReference section */
@@ -8716,6 +9023,11 @@
 			package = 58F097482A20C30000DA2DAD /* XCRemoteSwiftPackageReference "wireguard-apple" */;
 			productName = WireGuardKitTypes;
 		};
+		F0ACE3272BE4E712006D5333 /* WireGuardKitTypes */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = 58F097482A20C30000DA2DAD /* XCRemoteSwiftPackageReference "wireguard-apple" */;
+			productName = WireGuardKitTypes;
+		};
 /* End XCSwiftPackageProductDependency section */
 	};
 	rootObject = 58CE5E58224146200008646E /* Project object */;
diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift
index 156b4771428d..d858cf82c671 100644
--- a/ios/MullvadVPN/AppDelegate.swift
+++ b/ios/MullvadVPN/AppDelegate.swift
@@ -8,6 +8,7 @@
 
 import BackgroundTasks
 import MullvadLogging
+import MullvadMockData
 import MullvadREST
 import MullvadSettings
 import MullvadTypes
@@ -30,7 +31,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     private(set) var tunnelManager: TunnelManager!
     private(set) var addressCache: REST.AddressCache!
 
-    private var proxyFactory: REST.ProxyFactory!
+    private var proxyFactory: ProxyFactoryProtocol!
     private(set) var apiProxy: APIQuerying!
     private(set) var accountsProxy: RESTAccountHandling!
     private(set) var devicesProxy: DeviceHandling!
@@ -46,6 +47,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     private(set) var shadowsocksLoader: ShadowsocksLoaderProtocol!
     private(set) var configuredTransportProvider: ProxyConfigurationTransportProvider!
     private(set) var ipOverrideRepository = IPOverrideRepository()
+    private var launchArguments = LaunchArguments()
 
     // MARK: - Application lifecycle
 
@@ -54,7 +56,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         _ application: UIApplication,
         didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
     ) -> Bool {
-        if ProcessInfo().arguments.contains("DisableAnimations") {
+        if let overriddenLaunchArguments = try? ProcessInfo.processInfo.decode(LaunchArguments.self) {
+            launchArguments = overriddenLaunchArguments
+        }
+
+        if launchArguments.isDisabledAnimations {
             UIView.setAnimationsEnabled(false)
         }
 
@@ -147,13 +153,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     }
 
     private func setUpProxies(containerURL: URL) {
-        proxyFactory = REST.ProxyFactory.makeProxyFactory(
-            transportProvider: REST.AnyTransportProvider { [weak self] in
-                return self?.transportMonitor.makeTransport()
-            },
-            addressCache: addressCache
-        )
-
+        if launchArguments.target == .screenshots {
+            proxyFactory = MockProxyFactory.makeProxyFactory(
+                transportProvider: REST.AnyTransportProvider { [weak self] in
+                    return self?.transportMonitor.makeTransport()
+                },
+                addressCache: addressCache
+            )
+        } else {
+            proxyFactory = REST.ProxyFactory.makeProxyFactory(
+                transportProvider: REST.AnyTransportProvider { [weak self] in
+                    return self?.transportMonitor.makeTransport()
+                },
+                addressCache: addressCache
+            )
+        }
         apiProxy = proxyFactory.createAPIProxy()
         accountsProxy = proxyFactory.createAccountsProxy()
         devicesProxy = proxyFactory.createDevicesProxy()
diff --git a/ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift b/ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift
index 4aacb5f72898..26974f12810d 100644
--- a/ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift
+++ b/ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift
@@ -11,15 +11,19 @@ import XCTest
 class MullvadVPNScreenshots: XCTestCase {
     let app = XCUIApplication()
 
-    override func setUp() {
+    override func setUpWithError() throws {
         // Put setup code here. This method is called before the invocation of
         // each test method in the class.
 
         // In UI tests it is usually best to stop immediately when a failure occurs.
         continueAfterFailure = false
 
-        // Disable animations to speed up tests. This argument is picked up in AppDelegate.didFinishLaunchingWithOptions.
-        app.launchArguments = ["DisableAnimations"]
+        let argumentsJsonString = try LaunchArguments(
+            target: .screenshots,
+            isDisabledAnimations: true
+        ).toJSON()
+
+        app.launchEnvironment[LaunchArguments.tag] = argumentsJsonString
 
         // In UI tests it’s important to set the initial state - such as interface orientation -
         // required for your tests before they run. The setUp method is a good place to do this.
@@ -68,15 +72,17 @@ class MullvadVPNScreenshots: XCTestCase {
 
         let countryCell = app.cells["se"]
         let cityCell = app.cells["se-got"]
+        let relayCell = app.cells["se-got-wg-101"]
 
         _ = countryCell.waitForExistence(timeout: 2)
 
-        if cityCell.exists {
-            cityCell.tap()
+        if relayCell.exists {
+            relayCell.tap()
         } else {
             _ = countryCell.buttons[AccessibilityIdentifier.expandButton.rawValue].waitForExistence(timeout: 5)
             countryCell.buttons[AccessibilityIdentifier.expandButton.rawValue].tap()
-            cityCell.tap()
+            cityCell.buttons[AccessibilityIdentifier.expandButton.rawValue].tap()
+            relayCell.tap()
         }
 
         // Wait for Disconnect button to appear
@@ -87,7 +93,6 @@ class MullvadVPNScreenshots: XCTestCase {
         // Re-open Select location controller (iPhone only)
         if case .phone = UIDevice.current.userInterfaceIdiom {
             app.buttons[AccessibilityIdentifier.selectLocationButton.rawValue].tap()
-            cityCell.buttons[AccessibilityIdentifier.expandButton.rawValue].tap()
             snapshot("SelectLocation")
 
             // Tap the "Filter" button and expand each relay filter
diff --git a/ios/MullvadVPNTests/MullvadVPN/PacketTunnel/DeviceCheck/DeviceCheckOperationTests.swift b/ios/MullvadVPNTests/MullvadVPN/PacketTunnel/DeviceCheck/DeviceCheckOperationTests.swift
index 2dc3914db9d4..8f190b582ba5 100644
--- a/ios/MullvadVPNTests/MullvadVPN/PacketTunnel/DeviceCheck/DeviceCheckOperationTests.swift
+++ b/ios/MullvadVPNTests/MullvadVPN/PacketTunnel/DeviceCheck/DeviceCheckOperationTests.swift
@@ -6,6 +6,7 @@
 //  Copyright © 2023 Mullvad VPN AB. All rights reserved.
 //
 
+@testable import MullvadMockData
 import MullvadREST
 import MullvadSettings
 import MullvadTypes
diff --git a/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelManagerTests.swift b/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelManagerTests.swift
index af51b48468fe..e9843b5dab4f 100644
--- a/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelManagerTests.swift
+++ b/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelManagerTests.swift
@@ -5,9 +5,13 @@
 //  Created by Marco Nikic on 2023-10-02.
 //  Copyright © 2023 Mullvad VPN AB. All rights reserved.
 //
-
 import MullvadREST
+
+@testable import MullvadMockData
 @testable import MullvadSettings
+@testable import MullvadTypes
+@testable import WireGuardKitTypes
+
 import XCTest
 
 final class TunnelManagerTests: XCTestCase {
@@ -26,7 +30,7 @@ final class TunnelManagerTests: XCTestCase {
         let tunnelStore = TunnelStoreStub()
         let relayCacheTracker = RelayCacheTrackerStub()
         let accountProxy = AccountsProxyStub()
-        let devicesProxy = DevicesProxyStub()
+        let devicesProxy = DevicesProxyStub(deviceResult: .success(Device.mock(publicKey: PrivateKey().publicKey)))
         let apiProxy = APIProxyStub()
         let accessTokenManager = AccessTokenManagerStub()
         let tunnelManager = TunnelManager(
@@ -46,7 +50,7 @@ final class TunnelManagerTests: XCTestCase {
         let tunnelStore = TunnelStoreStub()
         let relayCacheTracker = RelayCacheTrackerStub()
         var accountProxy = AccountsProxyStub()
-        let devicesProxy = DevicesProxyStub()
+        let devicesProxy = DevicesProxyStub(deviceResult: .success(Device.mock(publicKey: PrivateKey().publicKey)))
         let apiProxy = APIProxyStub()
         let accessTokenManager = AccessTokenManagerStub()
         accountProxy.createAccountResult = .success(REST.NewAccountData.mockValue())
@@ -68,7 +72,7 @@ final class TunnelManagerTests: XCTestCase {
         let tunnelStore = TunnelStoreStub()
         let relayCacheTracker = RelayCacheTrackerStub()
         var accountProxy = AccountsProxyStub()
-        let devicesProxy = DevicesProxyStub()
+        let devicesProxy = DevicesProxyStub(deviceResult: .success(Device.mock(publicKey: PrivateKey().publicKey)))
         let apiProxy = APIProxyStub()
         let accessTokenManager = AccessTokenManagerStub()
         accountProxy.createAccountResult = .success(REST.NewAccountData.mockValue())
diff --git a/ios/PacketTunnelCoreTests/AppMessageHandlerTests.swift b/ios/PacketTunnelCoreTests/AppMessageHandlerTests.swift
index 97cf010b343e..cdd062b098f5 100644
--- a/ios/PacketTunnelCoreTests/AppMessageHandlerTests.swift
+++ b/ios/PacketTunnelCoreTests/AppMessageHandlerTests.swift
@@ -7,6 +7,7 @@
 //
 
 import Combine
+@testable import MullvadMockData
 @testable import MullvadREST
 import MullvadTypes
 import PacketTunnelCore
diff --git a/ios/Shared/LaunchArguments.swift b/ios/Shared/LaunchArguments.swift
new file mode 100644
index 000000000000..d348023c3120
--- /dev/null
+++ b/ios/Shared/LaunchArguments.swift
@@ -0,0 +1,68 @@
+//
+//  LaunchArguments.swift
+//  MullvadTypes
+//
+//  Created by Mojgan on 2024-05-06.
+//  Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+
+public protocol Taggable {
+    static var tag: String { get }
+}
+
+public extension Taggable {
+    static var tag: String {
+        String(describing: self)
+    }
+}
+
+public enum MullvadExecutableTarget: Codable {
+    case uiTests, screenshots, main
+}
+
+// This arguments are picked up in AppDelegate.
+public struct LaunchArguments: Codable, Taggable {
+    // Defines which target is running
+    public var target: MullvadExecutableTarget = .main
+
+    // Disable animations to speed up tests.
+    public var isDisabledAnimations = false
+}
+
+public extension ProcessInfo {
+    func decode<T: Taggable & Decodable>(_: T.Type) throws -> T {
+        guard let environment = environment[T.tag] else {
+            throw DecodingError.valueNotFound(
+                T.self,
+                DecodingError.Context(codingPath: [], debugDescription: "\(T.self) not found in environment")
+            )
+        }
+        return try T.decode(from: environment)
+    }
+}
+
+extension Encodable {
+    public func toJSON(_ encoder: JSONEncoder = JSONEncoder()) throws -> String {
+        let data = try encoder.encode(self)
+        let result = String(decoding: data, as: UTF8.self)
+        return result
+    }
+}
+
+private extension Decodable {
+    static func decode(from json: String) throws -> Self {
+        guard let data = json.data(using: .utf8) else {
+            throw DecodingError.valueNotFound(
+                Self.self,
+                DecodingError.Context(
+                    codingPath: [],
+                    debugDescription: "Could not convert \(json) into UTF-8 Data"
+                )
+            )
+        }
+
+        return try JSONDecoder().decode(self, from: data)
+    }
+}