diff --git a/ios/NebulaNetworkExtension/PacketTunnelProvider.swift b/ios/NebulaNetworkExtension/PacketTunnelProvider.swift index df48d93..e587e43 100644 --- a/ios/NebulaNetworkExtension/PacketTunnelProvider.swift +++ b/ios/NebulaNetworkExtension/PacketTunnelProvider.swift @@ -17,7 +17,7 @@ enum AppMessageError: Error { extension AppMessageError: LocalizedError { public var description: String? { switch self { - case .unknownIPCType(let command): + case let .unknownIPCType(command): return NSLocalizedString("Unknown IPC message type \(String(command))", comment: "") } } @@ -57,27 +57,27 @@ class PacketTunnelProvider: NEPacketTunnelProvider { if ProcessInfo().isOperatingSystemAtLeast( OperatingSystemVersion(majorVersion: 17, minorVersion: 0, patchVersion: 0)) { - manager = try await self.findManager() + manager = try await findManager() guard let foundManager = manager else { throw VPNStartError.couldNotFindManager } - self.site = try Site(manager: foundManager) + site = try Site(manager: foundManager) } else { // This does not save the manager with the site, which means we cannot update the // vpn profile name when updates happen (rare). - self.site = try Site(proto: self.protocolConfiguration as! NETunnelProviderProtocol) + site = try Site(proto: protocolConfiguration as! NETunnelProviderProtocol) } - config = try self.site!.getConfig() + config = try site!.getConfig() } catch { - //TODO: need a way to notify the app - self.log.error("Failed to render config from vpn object") + // TODO: need a way to notify the app + log.error("Failed to render config from vpn object") throw error } - let _site = self.site! + let _site = site! key = try _site.getKey() - guard let fileDescriptor = self.tunnelFileDescriptor else { + guard let fileDescriptor = tunnelFileDescriptor else { throw VPNStartError.noTunFileDescriptor } let tunFD = Int(fileDescriptor) @@ -92,7 +92,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider { throw err! } tunnelNetworkSettings.ipv4Settings = NEIPv4Settings( - addresses: [ipNet!.ip], subnetMasks: [ipNet!.maskCIDR]) + addresses: [ipNet!.ip], subnetMasks: [ipNet!.maskCIDR] + ) var routes: [NEIPv4Route] = [ NEIPv4Route(destinationAddress: ipNet!.network, subnetMask: ipNet!.maskCIDR) ] @@ -109,26 +110,28 @@ class PacketTunnelProvider: NEPacketTunnelProvider { tunnelNetworkSettings.ipv4Settings!.includedRoutes = routes tunnelNetworkSettings.mtu = _site.mtu as NSNumber - try await self.setTunnelNetworkSettings(tunnelNetworkSettings) + try await setTunnelNetworkSettings(tunnelNetworkSettings) var nebulaErr: NSError? - self.nebula = MobileNebulaNewNebula( - String(data: config, encoding: .utf8), key, self.site!.logFile, tunFD, &nebulaErr) - self.startNetworkMonitor() + nebula = MobileNebulaNewNebula( + String(data: config, encoding: .utf8), key, site!.logFile, tunFD, &nebulaErr + ) + startNetworkMonitor() if nebulaErr != nil { - self.log.error("We had an error starting up: \(nebulaErr, privacy: .public)") + log.error("We had an error starting up: \(nebulaErr, privacy: .public)") throw nebulaErr! } - self.nebula!.start() - self.dnUpdater.updateSingleLoop(site: self.site!, onUpdate: self.handleDNUpdate) + nebula!.start() + dnUpdater.updateSingleLoop(site: site!, onUpdate: handleDNUpdate) } private func handleDNUpdate(newSite: Site) { do { - self.site = newSite - try self.nebula?.reload( - String(data: newSite.getConfig(), encoding: .utf8), key: newSite.getKey()) + site = newSite + try nebula?.reload( + String(data: newSite.getConfig(), encoding: .utf8), key: newSite.getKey() + ) } catch { log.error( @@ -136,14 +139,14 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } } - //TODO: Sleep/wake get called aggressively and do nothing to help us here, we should locate why that is and make these work appropriately + // TODO: Sleep/wake get called aggressively and do nothing to help us here, we should locate why that is and make these work appropriately // override func sleep(completionHandler: @escaping () -> Void) { // nebula!.sleep() // completionHandler() // } private func findManager() async throws -> NETunnelProviderManager { - let targetProtoConfig = self.protocolConfiguration as? NETunnelProviderProtocol + let targetProtoConfig = protocolConfiguration as? NETunnelProviderProtocol guard let targetProviderConfig = targetProtoConfig?.providerConfiguration else { throw VPNStartError.noProviderConfig } @@ -168,17 +171,17 @@ class PacketTunnelProvider: NEPacketTunnelProvider { private func startNetworkMonitor() { networkMonitor = NWPathMonitor() - networkMonitor!.pathUpdateHandler = self.pathUpdate + networkMonitor!.pathUpdateHandler = pathUpdate networkMonitor!.start(queue: DispatchQueue(label: "NetworkMonitor")) } private func stopNetworkMonitor() { - self.networkMonitor?.cancel() + networkMonitor?.cancel() networkMonitor = nil } override func stopTunnel( - with reason: NEProviderStopReason, completionHandler: @escaping () -> Void + with _: NEProviderStopReason, completionHandler: @escaping () -> Void ) { nebula?.stop() stopNetworkMonitor() @@ -199,14 +202,14 @@ class PacketTunnelProvider: NEPacketTunnelProvider { private func collectAddresses(endpoints: [Network.NWEndpoint]) -> String { var str: [String] = [] - endpoints.forEach { endpoint in + for endpoint in endpoints { switch endpoint { case let .hostPort(.ipv6(host), port): str.append("[\(host)]:\(port)") case let .hostPort(.ipv4(host), port): str.append("\(host):\(port)") default: - return + continue } } @@ -219,72 +222,72 @@ class PacketTunnelProvider: NEPacketTunnelProvider { return nil } - var error: Error? + var error: (any Error)? var data: JSON? // start command has special treatment due to needing to call two completers if call.command == "start" { do { - try await self.start() + try await start() // No response data, this is expected on a clean start - return try? JSONEncoder().encode(IPCResponse.init(type: .success, message: nil)) + return try? JSONEncoder().encode(IPCResponse(type: .success, message: nil)) } catch { defer { self.cancelTunnelWithError(error) } return try? JSONEncoder().encode( - IPCResponse.init(type: .error, message: JSON(error.localizedDescription))) + IPCResponse(type: .error, message: JSON(error.localizedDescription))) } } if nebula == nil { // Respond with an empty success message in the event a command comes in before we've truly started log.warning("Received command but do not have a nebula instance") - return try? JSONEncoder().encode(IPCResponse.init(type: .success, message: nil)) + return try? JSONEncoder().encode(IPCResponse(type: .success, message: nil)) } - //TODO: try catch over all this + // TODO: try catch over all this switch call.command { case "listHostmap": (data, error) = listHostmap(pending: false) case "listPendingHostmap": (data, error) = listHostmap(pending: true) case "getHostInfo": (data, error) = getHostInfo(args: call.arguments!) case "setRemoteForTunnel": (data, error) = setRemoteForTunnel(args: call.arguments!) case "closeTunnel": (data, error) = closeTunnel(args: call.arguments!) - default: error = AppMessageError.unknownIPCType(command: call.command) } if error != nil { return try? JSONEncoder().encode( - IPCResponse.init( - type: .error, message: JSON(error?.localizedDescription ?? "Unknown error"))) + IPCResponse( + type: .error, message: JSON(error?.localizedDescription ?? "Unknown error") + )) } else { - return try? JSONEncoder().encode(IPCResponse.init(type: .success, message: data)) + return try? JSONEncoder().encode(IPCResponse(type: .success, message: data)) } } - private func listHostmap(pending: Bool) -> (JSON?, Error?) { + private func listHostmap(pending: Bool) -> (JSON?, (any Error)?) { var err: NSError? let res = nebula!.listHostmap(pending, error: &err) return (JSON(res), err) } - private func getHostInfo(args: JSON) -> (JSON?, Error?) { + private func getHostInfo(args: JSON) -> (JSON?, (any Error)?) { var err: NSError? let res = nebula!.getHostInfo( byVpnIp: args["vpnIp"].string, pending: args["pending"].boolValue, error: &err) return (JSON(res), err) } - private func setRemoteForTunnel(args: JSON) -> (JSON?, Error?) { + private func setRemoteForTunnel(args: JSON) -> (JSON?, (any Error)?) { var err: NSError? let res = nebula!.setRemoteForTunnel( args["vpnIp"].string, addr: args["addr"].string, error: &err) return (JSON(res), err) } - private func closeTunnel(args: JSON) -> (JSON?, Error?) { + private func closeTunnel(args: JSON) -> (JSON?, (any Error)?) { let res = nebula!.closeTunnel(args["vpnIp"].string) return (JSON(res), nil) } diff --git a/ios/NebulaNetworkExtension/Site.swift b/ios/NebulaNetworkExtension/Site.swift index 3efbe88..1592bd8 100644 --- a/ios/NebulaNetworkExtension/Site.swift +++ b/ios/NebulaNetworkExtension/Site.swift @@ -21,7 +21,7 @@ enum SiteError: Error { extension SiteError: CustomStringConvertible { public var description: String { switch self { - case .nonConforming(let site): + case let .nonConforming(site): return String("Non-conforming site \(String(describing: site))") case .noCertificate: return "No certificate found" @@ -35,20 +35,20 @@ extension SiteError: CustomStringConvertible { return "failed to find dn credentials in keychain" case .dnCredentialSave: return "failed to store dn credentials in keychain" - case .unexpected(_): + case .unexpected: return "An unexpected error occurred." } } } enum IPCResponseType: String, Codable { - case error = "error" - case success = "success" + case error + case success } class IPCResponse: Codable { var type: IPCResponseType - //TODO: change message to data? + // TODO: change message to data? var message: JSON? init(type: IPCResponseType, message: JSON?) { @@ -166,7 +166,7 @@ class Site: Codable { var cipher: String var sortKey: Int var logVerbosity: String - var connected: Bool? //TODO: active is a better name + var connected: Bool? // TODO: active is a better name var status: String? var logFile: String? var managed: Bool @@ -186,12 +186,12 @@ class Site: Codable { /// Creates a new site from a vpn manager instance. Mainly used by the UI. A manager is required to be able to edit the system profile convenience init(manager: NETunnelProviderManager) throws { - //TODO: Throw an error and have Sites delete the site, notify the user instead of using ! + // TODO: Throw an error and have Sites delete the site, notify the user instead of using ! let proto = manager.protocolConfiguration as! NETunnelProviderProtocol try self.init(proto: proto) self.manager = manager - self.connected = statusMap[manager.connection.status] - self.status = statusString[manager.connection.status] + connected = statusMap[manager.connection.status] + status = statusString[manager.connection.status] } convenience init(proto: NETunnelProviderProtocol) throws { @@ -202,7 +202,7 @@ class Site: Codable { let decoder = JSONDecoder() let incoming = try decoder.decode(IncomingSite.self, from: config) self.init(incoming: incoming) - self.needsToMigrateToFS = true + needsToMigrateToFS = true return } @@ -272,7 +272,7 @@ class Site: Codable { ca = try JSONDecoder().decode([CertificateInfo].self, from: rawCaDetails.data(using: .utf8)!) var hasErrors = false - ca.forEach { cert in + for cert in ca { if !cert.validity.valid { hasErrors = true } @@ -288,7 +288,7 @@ class Site: Codable { } do { - logFile = try SiteList.getSiteLogFile(id: self.id, createDir: true).path + logFile = try SiteList.getSiteLogFile(id: id, createDir: true).path } catch { logFile = nil errors.append("Unable to create the site directory: \(error.localizedDescription)") @@ -322,7 +322,7 @@ class Site: Codable { throw SiteError.keyLoad } - //TODO: make sure this is valid on return! + // TODO: make sure this is valid on return! return String(decoding: keyData, as: UTF8.self) } @@ -344,7 +344,7 @@ class Site: Codable { let creds = try getDNCredentials() creds.invalid = true - if !(try creds.save(siteID: self.id)) { + if try !(creds.save(siteID: id)) { throw SiteError.dnCredentialLoad } } @@ -353,13 +353,13 @@ class Site: Codable { let creds = try getDNCredentials() creds.invalid = false - if !(try creds.save(siteID: self.id)) { + if try !(creds.save(siteID: id)) { throw SiteError.dnCredentialSave } } func getConfig() throws -> Data { - return try self.incomingSite!.getConfig() + return try incomingSite!.getConfig() } // Limits what we export to the UI @@ -458,12 +458,12 @@ struct IncomingSite: Codable { func save( manager: NETunnelProviderManager?, saveToManager: Bool = true, - callback: @escaping (Error?) -> Void + callback: @escaping ((any Error)?) -> Void ) { let configPath: URL do { - configPath = try SiteList.getSiteConfigFile(id: self.id, createDir: true) + configPath = try SiteList.getSiteConfigFile(id: id, createDir: true) } catch { callback(error) @@ -472,22 +472,22 @@ struct IncomingSite: Codable { log.notice("Saving to \(configPath, privacy: .public)") do { - if self.key != nil { - let data = self.key!.data(using: .utf8) - if !KeyChain.save(key: "\(self.id).key", data: data!, managed: self.managed ?? false) { + if key != nil { + let data = key!.data(using: .utf8) + if !KeyChain.save(key: "\(id).key", data: data!, managed: managed ?? false) { return callback(SiteError.keySave) } } do { - if (try self.dnCredentials?.save(siteID: self.id)) == false { + if try (dnCredentials?.save(siteID: id)) == false { return callback(SiteError.dnCredentialSave) } } catch { return callback(error) } - try self.getConfig().write(to: configPath) + try getConfig().write(to: configPath) } catch { return callback(error) @@ -506,7 +506,7 @@ struct IncomingSite: Codable { } private func saveToManager( - manager: NETunnelProviderManager?, callback: @escaping (Error?) -> Void + manager: NETunnelProviderManager?, callback: @escaping ((any Error)?) -> Void ) { if manager != nil { // We need to refresh our settings to properly update config @@ -524,7 +524,7 @@ struct IncomingSite: Codable { } private func finishSaveToManager( - manager: NETunnelProviderManager, callback: @escaping (Error?) -> Void + manager: NETunnelProviderManager, callback: @escaping ((any Error)?) -> Void ) { // Stuff our details in the protocol let proto = @@ -532,19 +532,19 @@ struct IncomingSite: Codable { proto.providerBundleIdentifier = "net.defined.mobileNebula.NebulaNetworkExtension" // WARN: If we stop setting providerConfiguration["id"] here, we'll need to use something else to match // managers in PacketTunnelProvider.findManager - proto.providerConfiguration = ["id": self.id] + proto.providerConfiguration = ["id": id] proto.serverAddress = "Nebula" // Finish up the manager, this is what stores everything at the system level manager.protocolConfiguration = proto - //TODO: cert name? manager.protocolConfiguration?.username + // TODO: cert name? manager.protocolConfiguration?.username - //TODO: This is what is shown on the vpn page. We should add more identifying details in - manager.localizedDescription = self.name + // TODO: This is what is shown on the vpn page. We should add more identifying details in + manager.localizedDescription = name manager.isEnabled = true manager.saveToPreferences { error in - return callback(error) + callback(error) } } } diff --git a/ios/NebulaNetworkExtension/SiteList.swift b/ios/NebulaNetworkExtension/SiteList.swift index 471b338..aaddc61 100644 --- a/ios/NebulaNetworkExtension/SiteList.swift +++ b/ios/NebulaNetworkExtension/SiteList.swift @@ -46,10 +46,11 @@ class SiteList { /// Gets the file that represents the site log output, $rootDir/sites/$siteID/log static func getSiteLogFile(id: String, createDir: Bool) throws -> URL { return try getSiteDir(id: id, create: createDir).appendingPathComponent( - "logs", isDirectory: false) + "logs", isDirectory: false + ) } - init(completion: @escaping ([String: Site]?, Error?) -> Void) { + init(completion: @escaping ([String: Site]?, (any Error)?) -> Void) { #if targetEnvironment(simulator) SiteList.loadAllFromFS { sites, err in if sites != nil { @@ -67,21 +68,22 @@ class SiteList { #endif } - private static func loadAllFromFS(completion: @escaping ([String: Site]?, Error?) -> Void) { + private static func loadAllFromFS(completion: @escaping ([String: Site]?, (any Error)?) -> Void) { let fileManager = FileManager.default var siteDirs: [URL] var sites = [String: Site]() do { siteDirs = try fileManager.contentsOfDirectory( - at: getSitesDir(), includingPropertiesForKeys: nil) + at: getSitesDir(), includingPropertiesForKeys: nil + ) } catch { completion(nil, error) return } - siteDirs.forEach { path in + for path in siteDirs { do { let site = try Site( path: path.appendingPathComponent("config").appendingPathExtension("json")) @@ -97,7 +99,9 @@ class SiteList { completion(sites, nil) } - private static func loadAllFromNETPM(completion: @escaping ([String: Site]?, Error?) -> Void) { + private static func loadAllFromNETPM( + completion: @escaping ([String: Site]?, (any Error)?) -> Void + ) { var sites = [String: Site]() // dispatchGroup is used to ensure we have migrated all sites before returning them @@ -127,10 +131,10 @@ class SiteList { sites[site.id] = site } catch { - //TODO: notify the user about this + // TODO: notify the user about this print("Deleted non conforming site \(manager) \(error)") manager.removeFromPreferences() - //TODO: delete from disk, we need to try and discover the site id though + // TODO: delete from disk, we need to try and discover the site id though } } diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 9528bbd..a3edd49 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -45,21 +45,18 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { sites = Sites(messenger: controller.binaryMessenger) ui = FlutterMethodChannel(name: ChannelName.vpn, binaryMessenger: controller.binaryMessenger) - ui!.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in + ui!.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in switch call.method { case "nebula.parseCerts": return self.nebulaParseCerts(call: call, result: result) case "nebula.generateKeyPair": return self.nebulaGenerateKeyPair(result: result) case "nebula.renderConfig": return self.nebulaRenderConfig(call: call, result: result) case "nebula.verifyCertAndKey": return self.nebulaVerifyCertAndKey(call: call, result: result) - case "dn.enroll": return self.dnEnroll(call: call, result: result) - case "listSites": return self.listSites(result: result) case "deleteSite": return self.deleteSite(call: call, result: result) case "saveSite": return self.saveSite(call: call, result: result) case "startSite": return self.startSite(call: call, result: result) case "stopSite": return self.stopSite(call: call, result: result) - case "active.listHostmap": self.vpnRequest(command: "listHostmap", arguments: call.arguments, result: result) case "active.listPendingHostmap": @@ -70,11 +67,10 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { self.vpnRequest(command: "setRemoteForTunnel", arguments: call.arguments, result: result) case "active.closeTunnel": self.vpnRequest(command: "closeTunnel", arguments: call.arguments, result: result) - default: result(FlutterMethodNotImplemented) } - }) + } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } @@ -90,7 +86,8 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { if err != nil { return result( CallFailedError( - message: "Error while parsing certificate(s)", details: err!.localizedDescription)) + message: "Error while parsing certificate(s)", details: err!.localizedDescription + )) } return result(json) @@ -112,7 +109,8 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { return result( CallFailedError( message: "Error while verifying certificate and private key", - details: err!.localizedDescription)) + details: err!.localizedDescription + )) } return result(valid) @@ -124,7 +122,8 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { if err != nil { return result( CallFailedError( - message: "Error while generating key pairs", details: err!.localizedDescription)) + message: "Error while generating key pairs", details: err!.localizedDescription + )) } return result(kp) @@ -150,7 +149,7 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { do { let site = try apiClient.enroll(code: code) - let oldSite = self.sites?.getSite(id: site.id) + let oldSite = sites?.getSite(id: site.id) site.save(manager: oldSite?.manager) { error in if error != nil { return result( @@ -166,7 +165,7 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { } func listSites(result: @escaping FlutterResult) { - self.sites?.loadSites { (sites, err) -> Void in + sites?.loadSites { sites, err in if err != nil { return result( CallFailedError(message: "Failed to load site list", details: err!.localizedDescription)) @@ -181,8 +180,8 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { func deleteSite(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let id = call.arguments as? String else { return result(NoArgumentsError()) } - //TODO: stop the site if its running currently - self.sites?.deleteSite(id: id) { error in + // TODO: stop the site if its running currently + sites?.deleteSite(id: id) { error in if error != nil { result( CallFailedError(message: "Failed to delete site", details: error!.localizedDescription)) @@ -200,7 +199,7 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { return result(NoArgumentsError()) } - let oldSite = self.sites?.getSite(id: site.id) + let oldSite = sites?.getSite(id: site.id) site.save(manager: oldSite?.manager) { error in if error != nil { return result( @@ -220,29 +219,30 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { } #if targetEnvironment(simulator) - let updater = self.sites?.getUpdater(id: id) + let updater = sites?.getUpdater(id: id) updater?.update(connected: true) #else - let container = self.sites?.getContainer(id: id) + let container = sites?.getContainer(id: id) let manager = container?.site.manager manager?.loadFromPreferences { error in - //TODO: Handle load error + // TODO: Handle load error // This is silly but we need to enable the site each time to avoid situations where folks have multiple sites manager?.isEnabled = true manager?.saveToPreferences { error in - //TODO: Handle load error + // TODO: Handle load error manager?.loadFromPreferences { error in - //TODO: Handle load error + // TODO: Handle load error do { - container?.updater.startFunc = { () -> Void in - return self.vpnRequest(command: "start", arguments: args, result: result) + container?.updater.startFunc = { () in + self.vpnRequest(command: "start", arguments: args, result: result) } try manager?.connection.startVPNTunnel(options: ["expectStart": NSNumber(1)]) } catch { return result( CallFailedError( - message: "Could not start site", details: error.localizedDescription)) + message: "Could not start site", details: error.localizedDescription + )) } } } @@ -256,13 +256,13 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { return result(MissingArgumentError(message: "id is a required argument")) } #if targetEnvironment(simulator) - let updater = self.sites?.getUpdater(id: id) + let updater = sites?.getUpdater(id: id) updater?.update(connected: false) #else - let manager = self.sites?.getSite(id: id)?.manager - manager?.loadFromPreferences { error in - //TODO: Handle load error + let manager = sites?.getSite(id: id)?.manager + manager?.loadFromPreferences { _ in + // TODO: Handle load error manager?.connection.stopVPNTunnel() return result(nil) @@ -290,13 +290,13 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { if let session = container!.site.manager?.connection as? NETunnelProviderSession { do { try session.sendProviderMessage( - try JSONEncoder().encode(IPCRequest(command: command, arguments: JSON(args))) + JSONEncoder().encode(IPCRequest(command: command, arguments: JSON(args))) ) { data in if data == nil { return result(nil) } - //print(String(decoding: data!, as: UTF8.self)) + // print(String(decoding: data!, as: UTF8.self)) guard let res = try? JSONDecoder().decode(IPCResponse.self, from: data!) else { return result(CallFailedError(message: "Failed to decode response")) } @@ -312,19 +312,19 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { return result(CallFailedError(message: error.localizedDescription)) } } else { - //TODO: we have a site without a manager, things have gone weird. How to handle since this shouldn't happen? + // TODO: we have a site without a manager, things have gone weird. How to handle since this shouldn't happen? result(nil) } } } -func MissingArgumentError(message: String, details: Error? = nil) -> FlutterError { +func MissingArgumentError(message: String, details: (any Error)? = nil) -> FlutterError { return FlutterError(code: "missingArgument", message: message, details: details) } func NoArgumentsError( message: String? = "no arguments were provided or could not be deserialized", - details: Error? = nil + details: (any Error)? = nil ) -> FlutterError { return FlutterError(code: "noArguments", message: message, details: details) } diff --git a/ios/Runner/DNUpdate.swift b/ios/Runner/DNUpdate.swift index 9f7f2c1..53d7d2c 100644 --- a/ios/Runner/DNUpdate.swift +++ b/ios/Runner/DNUpdate.swift @@ -7,7 +7,7 @@ class DNUpdater { private let log = Logger(subsystem: "net.defined.mobileNebula", category: "DNUpdater") func updateAll(onUpdate: @escaping (Site) -> Void) { - _ = SiteList { (sites, _) -> Void in + _ = SiteList { sites, _ in // NEVPN seems to force us onto the main thread and we are about to make network calls that // could block for a while. Push ourselves onto another thread to avoid blocking the UI. Task.detached(priority: .userInitiated) { @@ -93,14 +93,13 @@ class DNUpdater { // From https://medium.com/over-engineering/a-background-repeating-timer-in-swift-412cecfd2ef9 class RepeatingTimer { - let timeInterval: TimeInterval init(timeInterval: TimeInterval) { self.timeInterval = timeInterval } - private lazy var timer: DispatchSourceTimer = { + private lazy var timer: any DispatchSourceTimer = { let t = DispatchSource.makeTimerSource() t.schedule(deadline: .now(), repeating: self.timeInterval) t.setEventHandler(handler: { [weak self] in @@ -122,9 +121,9 @@ class RepeatingTimer { timer.setEventHandler {} timer.cancel() /* - If the timer is suspended, calling cancel without resuming - triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902 - */ + If the timer is suspended, calling cancel without resuming + triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902 + */ resume() eventHandler = nil } diff --git a/ios/Runner/Sites.swift b/ios/Runner/Sites.swift index a477513..bf92cf8 100644 --- a/ios/Runner/Sites.swift +++ b/ios/Runner/Sites.swift @@ -13,14 +13,14 @@ class SiteContainer { class Sites { private var containers = [String: SiteContainer]() - private var messenger: FlutterBinaryMessenger? + private var messenger: (any FlutterBinaryMessenger)? - init(messenger: FlutterBinaryMessenger?) { + init(messenger: (any FlutterBinaryMessenger)?) { self.messenger = messenger } - func loadSites(completion: @escaping ([String: Site]?, Error?) -> Void) { - _ = SiteList { (sites, err) in + func loadSites(completion: @escaping ([String: Site]?, (any Error)?) -> Void) { + _ = SiteList { sites, err in if err != nil { return completion(nil, err) } @@ -36,14 +36,14 @@ class Sites { } let justSites = self.containers.mapValues { - return $0.site + $0.site } completion(justSites, nil) } } - func deleteSite(id: String, callback: @escaping (Error?) -> Void) { - if let site = self.containers.removeValue(forKey: id) { + func deleteSite(id: String, callback: @escaping ((any Error)?) -> Void) { + if let site = containers.removeValue(forKey: id) { _ = KeyChain.delete(key: "\(site.site.id).dnCredentials") _ = KeyChain.delete(key: "\(site.site.id).key") @@ -66,15 +66,15 @@ class Sites { } func getSite(id: String) -> Site? { - return self.containers[id]?.site + return containers[id]?.site } func getUpdater(id: String) -> SiteUpdater? { - return self.containers[id]?.updater + return containers[id]?.updater } func getContainer(id: String) -> SiteContainer? { - return self.containers[id] + return containers[id] } } @@ -85,38 +85,39 @@ class SiteUpdater: NSObject, FlutterStreamHandler { private var notification: Any? public var startFunc: (() -> Void)? private var configFd: Int32? = nil - private var configObserver: DispatchSourceFileSystemObject? = nil + private var configObserver: (any DispatchSourceFileSystemObject)? = nil - init(messenger: FlutterBinaryMessenger, site: Site) { + init(messenger: any FlutterBinaryMessenger, site: Site) { do { let configPath = try SiteList.getSiteConfigFile(id: site.id, createDir: false) - self.configFd = open(configPath.path, O_EVTONLY) - self.configObserver = DispatchSource.makeFileSystemObjectSource( - fileDescriptor: self.configFd!, + configFd = open(configPath.path, O_EVTONLY) + configObserver = DispatchSource.makeFileSystemObjectSource( + fileDescriptor: configFd!, eventMask: .write ) } catch { // SiteList.getSiteConfigFile should never throw because we are not creating it here - self.configObserver = nil + configObserver = nil } eventChannel = FlutterEventChannel( - name: "net.defined.nebula/\(site.id)", binaryMessenger: messenger) + name: "net.defined.nebula/\(site.id)", binaryMessenger: messenger + ) self.site = site super.init() eventChannel.setStreamHandler(self) - self.configObserver?.setEventHandler(handler: self.configUpdated) - self.configObserver?.setCancelHandler { + configObserver?.setEventHandler(handler: configUpdated) + configObserver?.setCancelHandler { if self.configFd != nil { close(self.configFd!) } self.configObserver = nil } - self.configObserver?.resume() + configObserver?.resume() } func setSite(site: Site) { @@ -124,29 +125,30 @@ class SiteUpdater: NSObject, FlutterStreamHandler { } /// onListen is called when flutter code attaches an event listener - func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + func onListen(withArguments _: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { eventSink = events #if !targetEnvironment(simulator) if site.manager == nil { - //TODO: The dn updater path seems to race to build a site that lacks a manager. The UI does not display this error + // TODO: The dn updater path seems to race to build a site that lacks a manager. The UI does not display this error // and a another listen should occur and succeed. return FlutterError( - code: "Internal Error", message: "Flutter manager was not present", details: nil) + code: "Internal Error", message: "Flutter manager was not present", details: nil + ) } - self.notification = NotificationCenter.default.addObserver( + notification = NotificationCenter.default.addObserver( forName: NSNotification.Name.NEVPNStatusDidChange, object: site.manager!.connection, queue: nil - ) { n in + ) { _ in let oldConnected = self.site.connected self.site.status = statusString[self.site.manager!.connection.status] self.site.connected = statusMap[self.site.manager!.connection.status] // Check to see if we just moved to connected and if we have a start function to call when that happens - if self.site.connected! && oldConnected != self.site.connected && self.startFunc != nil { + if self.site.connected!, oldConnected != self.site.connected, self.startFunc != nil { self.startFunc!() self.startFunc = nil } @@ -158,9 +160,9 @@ class SiteUpdater: NSObject, FlutterStreamHandler { } /// onCancel is called when the flutter listener stops listening - func onCancel(withArguments arguments: Any?) -> FlutterError? { - if self.notification != nil { - NotificationCenter.default.removeObserver(self.notification!) + func onCancel(withArguments _: Any?) -> FlutterError? { + if notification != nil { + NotificationCenter.default.removeObserver(notification!) } return nil } @@ -175,18 +177,18 @@ class SiteUpdater: NSObject, FlutterStreamHandler { let encoder = JSONEncoder() let data = try! encoder.encode(site) - self.eventSink?(String(data: data, encoding: .utf8)) + eventSink?(String(data: data, encoding: .utf8)) } private func configUpdated() { - if self.site.connected != true { + if site.connected != true { return } - guard let newSite = try? Site(manager: self.site.manager!) else { + guard let newSite = try? Site(manager: site.manager!) else { return } - self.update(connected: newSite.connected ?? false, replaceSite: newSite) + update(connected: newSite.connected ?? false, replaceSite: newSite) } }