summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadVPN/View controllers/DeviceList/DeviceManaging.swift
blob: c625f7dc6cc8e69eeedd9309faa4ba1d27b996af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//
//  DeviceManagementInteractor.swift
//  MullvadVPN
//
//  Created by pronebird on 26/07/2022.
//  Copyright © 2025 Mullvad VPN AB. All rights reserved.
//

import Foundation
import MullvadREST
import MullvadTypes
import WireGuardKitTypes

protocol DeviceManaging {
    var currentDeviceId: String? { get }
    func getDevices(_ completionHandler: @escaping @Sendable (Result<[Device], Error>) -> Void) -> Cancellable
    func deleteDevice(
        _ identifier: String,
        completionHandler: @escaping @Sendable (Result<Bool, Error>) -> Void
    ) -> Cancellable
}

class DeviceManagementInteractor: DeviceManaging, @unchecked Sendable {
    private let devicesProxy: DeviceHandling
    private let accountNumber: String
    let currentDeviceId: String?

    init(accountNumber: String, currentDeviceId: String? = nil, devicesProxy: DeviceHandling) {
        self.accountNumber = accountNumber
        self.devicesProxy = devicesProxy
        self.currentDeviceId = currentDeviceId
    }

    @discardableResult
    func getDevices(_ completionHandler: @escaping @Sendable (Result<[Device], Error>) -> Void) -> Cancellable {
        devicesProxy.getDevices(
            accountNumber: accountNumber,
            retryStrategy: .default,
            completion: completionHandler
        )
    }

    @discardableResult
    func deleteDevice(
        _ identifier: String,
        completionHandler: @escaping @Sendable (Result<Bool, Error>) -> Void
    ) -> Cancellable {
        devicesProxy.deleteDevice(
            accountNumber: accountNumber,
            identifier: identifier,
            retryStrategy: .default,
            completion: completionHandler
        )
    }
}

class MockDeviceManaging: DeviceManaging {
    let currentDeviceId: String? = "123"
    let getDevicesCompletionHandler: (() -> Result<[Device], Error>)?
    static private let mockDevices = [
        Device(
            id: "123",
            name: "Blind Mole",
            pubkey: PrivateKey().publicKey,
            hijackDNS: false,
            created: Date(),
            ipv4Address: IPAddressRange(from: "127.0.0.1/32")!,
            ipv6Address: IPAddressRange(from: "::ff/64")!
        ),
        Device(
            id: "456",
            name: "Tall Mole",
            pubkey: PrivateKey().publicKey,
            hijackDNS: false,
            created: Date(),
            ipv4Address: IPAddressRange(from: "127.0.0.1/32")!,
            ipv6Address: IPAddressRange(from: "::ff/64")!
        ),
        Device(
            id: "543",
            name: "Old Mole",
            pubkey: PrivateKey().publicKey,
            hijackDNS: false,
            created: Date(),
            ipv4Address: IPAddressRange(from: "127.0.0.1/32")!,
            ipv6Address: IPAddressRange(from: "::ff/64")!
        ),
        Device(
            id: "867",
            name: "Young Mole",
            pubkey: PrivateKey().publicKey,
            hijackDNS: false,
            created: Date(),
            ipv4Address: IPAddressRange(from: "127.0.0.1/32")!,
            ipv6Address: IPAddressRange(from: "::ff/64")!
        ),
        Device(
            id: "234",
            name: "Rich Mole",
            pubkey: PrivateKey().publicKey,
            hijackDNS: false,
            created: Date(),
            ipv4Address: IPAddressRange(from: "127.0.0.1/32")!,
            ipv6Address: IPAddressRange(from: "::ff/64")!
        ),
    ]
    let devicesToReturn: Int
    init(
        devicesToReturn: Int = 5,
        getDevicesCompletionHandler: (() -> Result<[Device], Error>)? = {
            .success(mockDevices)
        }
    ) {
        self.devicesToReturn = devicesToReturn
        self.getDevicesCompletionHandler = getDevicesCompletionHandler
    }

    func deleteDevice(
        _ identifier: String,
        completionHandler: @escaping @Sendable (Result<Bool, any Error>) -> Void
    ) -> any MullvadTypes.Cancellable {
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            completionHandler(.success(true))
        }
        return AnyCancellable()
    }

    func getDevices(_ completionHandler: @escaping @Sendable (Result<[Device], Error>) -> Void) -> Cancellable {
        if let getDevicesCompletionHandler {
            completionHandler(getDevicesCompletionHandler().map { Array($0.prefix(devicesToReturn)) })
        }
        return AnyCancellable()
    }
}