From a4c7b4290cbdd9085fbf0c8971bfc9bbe64c151e Mon Sep 17 00:00:00 2001 From: Caleb Jasik Date: Wed, 19 Feb 2025 15:51:47 -0600 Subject: [PATCH] Refactor DNUpdater to an actor and try to fix warnings --- .../PacketTunnelProvider.swift | 4 ++-- ios/NebulaNetworkExtension/Site.swift | 8 +++---- ios/Runner/AppDelegate.swift | 22 ++++++++++--------- ios/Runner/DNUpdate.swift | 16 +++++++++----- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/ios/NebulaNetworkExtension/PacketTunnelProvider.swift b/ios/NebulaNetworkExtension/PacketTunnelProvider.swift index aa01351..52c564e 100644 --- a/ios/NebulaNetworkExtension/PacketTunnelProvider.swift +++ b/ios/NebulaNetworkExtension/PacketTunnelProvider.swift @@ -23,7 +23,7 @@ extension AppMessageError: LocalizedError { } } -class PacketTunnelProvider: NEPacketTunnelProvider { +final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable { private var networkMonitor: NWPathMonitor? private var site: Site? @@ -115,7 +115,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } nebula!.start() - dnUpdater.updateSingleLoop(site: site!, onUpdate: handleDNUpdate) + await dnUpdater.updateSingleLoop(site: site!, onUpdate: handleDNUpdate) } private func handleDNUpdate(newSite: Site) { diff --git a/ios/NebulaNetworkExtension/Site.swift b/ios/NebulaNetworkExtension/Site.swift index 44fc846..1567a63 100644 --- a/ios/NebulaNetworkExtension/Site.swift +++ b/ios/NebulaNetworkExtension/Site.swift @@ -150,7 +150,7 @@ let statusString: [NEVPNStatus: String] = [ ] // Represents a site that was pulled out of the system configuration -class Site: Codable { +final class Site: Codable, @unchecked Sendable { // Stored in manager var name: String var id: String @@ -278,7 +278,7 @@ class Site: Codable { } } - if hasErrors && !managed { + if hasErrors, !managed { errors.append("There are issues with 1 or more ca certificates") } @@ -294,7 +294,7 @@ class Site: Codable { errors.append("Unable to create the site directory: \(error.localizedDescription)") } - if managed && (try? getDNCredentials())?.invalid != false { + if managed, (try? getDNCredentials())?.invalid != false { errors.append("Unable to fetch managed updates - please re-enroll the device") } @@ -426,7 +426,7 @@ class DNCredentials: Codable { } // This class represents a site coming in from flutter, meant only to be saved and re-loaded as a proper Site -struct IncomingSite: Codable { +struct IncomingSite: Codable, @unchecked Sendable { var name: String var id: String var staticHostmap: [String: StaticHosts] diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 0bbec64..3519c9b 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -25,17 +25,19 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError { ) -> Bool { GeneratedPluginRegistrant.register(with: self) - dnUpdater.updateAllLoop { site in - // Signal the site has changed in case the current site details screen is active - let container = self.sites?.getContainer(id: site.id) - if container != nil { - // Update references to the site with the new site config - container!.site = site - container!.updater.update(connected: site.connected ?? false, replaceSite: site) - } + Task { + await dnUpdater.updateAllLoop { @MainActor site in + // Signal the site has changed in case the current site details screen is active + let container = self.sites?.getContainer(id: site.id) + if container != nil { + // Update references to the site with the new site config + container!.site = site + container!.updater.update(connected: site.connected ?? false, replaceSite: site) + } - // Signal to the main screen to reload - self.ui?.invokeMethod("refreshSites", arguments: nil) + // Signal to the main screen to reload + self.ui?.invokeMethod("refreshSites", arguments: nil) + } } guard let controller = window?.rootViewController as? FlutterViewController else { diff --git a/ios/Runner/DNUpdate.swift b/ios/Runner/DNUpdate.swift index 4ea7357..f6b5d56 100644 --- a/ios/Runner/DNUpdate.swift +++ b/ios/Runner/DNUpdate.swift @@ -1,29 +1,33 @@ import Foundation import os.log -class DNUpdater { +actor DNUpdater { private let apiClient = APIClient() private let timer = RepeatingTimer(timeInterval: 15 * 60) // 15 * 60 is 15 minutes private let log = Logger(subsystem: "net.defined.mobileNebula", category: "DNUpdater") - func updateAll(onUpdate: @escaping (Site) -> Void) { + func updateAll(onUpdate: @Sendable @escaping (Site) -> Void) { _ = SiteList { sites, _ in + guard let unwrappedSites = sites else { + // There was an error, let's bail. + return + } // 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) { - sites?.values.forEach { site in + for site in unwrappedSites.values { if site.connected == true { // The vpn service is in charge of updating the currently connected site return } - self.updateSite(site: site, onUpdate: onUpdate) + await self.updateSite(site: site, onUpdate: onUpdate) } } } } - func updateAllLoop(onUpdate: @escaping (Site) -> Void) { + func updateAllLoop(onUpdate: @Sendable @escaping (Site) -> Void) { timer.eventHandler = { self.updateAll(onUpdate: onUpdate) } @@ -37,7 +41,7 @@ class DNUpdater { timer.resume() } - func updateSite(site: Site, onUpdate: @escaping (Site) -> Void) { + func updateSite(site: Site, onUpdate: sending @escaping (Site) -> Void) { do { if !site.managed { return