Refactor DNUpdater to an actor and try to fix warnings

This commit is contained in:
Caleb Jasik 2025-02-19 15:51:47 -06:00
parent 8634f4cf63
commit a4c7b4290c
No known key found for this signature in database
4 changed files with 28 additions and 22 deletions

View file

@ -23,7 +23,7 @@ extension AppMessageError: LocalizedError {
} }
} }
class PacketTunnelProvider: NEPacketTunnelProvider { final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
private var networkMonitor: NWPathMonitor? private var networkMonitor: NWPathMonitor?
private var site: Site? private var site: Site?
@ -115,7 +115,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
} }
nebula!.start() nebula!.start()
dnUpdater.updateSingleLoop(site: site!, onUpdate: handleDNUpdate) await dnUpdater.updateSingleLoop(site: site!, onUpdate: handleDNUpdate)
} }
private func handleDNUpdate(newSite: Site) { private func handleDNUpdate(newSite: Site) {

View file

@ -150,7 +150,7 @@ let statusString: [NEVPNStatus: String] = [
] ]
// Represents a site that was pulled out of the system configuration // Represents a site that was pulled out of the system configuration
class Site: Codable { final class Site: Codable, @unchecked Sendable {
// Stored in manager // Stored in manager
var name: String var name: String
var id: 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") 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)") 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") 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 // 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 name: String
var id: String var id: String
var staticHostmap: [String: StaticHosts] var staticHostmap: [String: StaticHosts]

View file

@ -25,7 +25,8 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError {
) -> Bool { ) -> Bool {
GeneratedPluginRegistrant.register(with: self) GeneratedPluginRegistrant.register(with: self)
dnUpdater.updateAllLoop { site in Task {
await dnUpdater.updateAllLoop { @MainActor site in
// Signal the site has changed in case the current site details screen is active // Signal the site has changed in case the current site details screen is active
let container = self.sites?.getContainer(id: site.id) let container = self.sites?.getContainer(id: site.id)
if container != nil { if container != nil {
@ -37,6 +38,7 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError {
// Signal to the main screen to reload // Signal to the main screen to reload
self.ui?.invokeMethod("refreshSites", arguments: nil) self.ui?.invokeMethod("refreshSites", arguments: nil)
} }
}
guard let controller = window?.rootViewController as? FlutterViewController else { guard let controller = window?.rootViewController as? FlutterViewController else {
fatalError("rootViewController is not type FlutterViewController") fatalError("rootViewController is not type FlutterViewController")

View file

@ -1,29 +1,33 @@
import Foundation import Foundation
import os.log import os.log
class DNUpdater { actor DNUpdater {
private let apiClient = APIClient() private let apiClient = APIClient()
private let timer = RepeatingTimer(timeInterval: 15 * 60) // 15 * 60 is 15 minutes private let timer = RepeatingTimer(timeInterval: 15 * 60) // 15 * 60 is 15 minutes
private let log = Logger(subsystem: "net.defined.mobileNebula", category: "DNUpdater") 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 _ = 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 // 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. // could block for a while. Push ourselves onto another thread to avoid blocking the UI.
Task.detached(priority: .userInitiated) { Task.detached(priority: .userInitiated) {
sites?.values.forEach { site in for site in unwrappedSites.values {
if site.connected == true { if site.connected == true {
// The vpn service is in charge of updating the currently connected site // The vpn service is in charge of updating the currently connected site
return 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 = { timer.eventHandler = {
self.updateAll(onUpdate: onUpdate) self.updateAll(onUpdate: onUpdate)
} }
@ -37,7 +41,7 @@ class DNUpdater {
timer.resume() timer.resume()
} }
func updateSite(site: Site, onUpdate: @escaping (Site) -> Void) { func updateSite(site: Site, onUpdate: sending @escaping (Site) -> Void) {
do { do {
if !site.managed { if !site.managed {
return return