Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable StrictConcurrency=complete swift feature flag #260

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion ios/NebulaNetworkExtension/PacketTunnelProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ extension AppMessageError: LocalizedError {
}
}

class PacketTunnelProvider: NEPacketTunnelProvider {
class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
private var networkMonitor: NWPathMonitor?

private var site: Site?
Expand Down
8 changes: 4 additions & 4 deletions ios/NebulaNetworkExtension/Site.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import os.log
let log = Logger(subsystem: "net.defined.mobileNebula", category: "Site")

enum SiteError: Error {
case nonConforming(site: [String: Any]?)
case nonConforming(site: String)
case noCertificate
case keyLoad
case keySave
Expand All @@ -22,7 +22,7 @@ extension SiteError: CustomStringConvertible {
public var description: String {
switch self {
case .nonConforming(let site):
return String("Non-conforming site \(String(describing: site))")
return String("Non-conforming site \(site)")
case .noCertificate:
return "No certificate found"
case .keyLoad:
Expand Down Expand Up @@ -150,7 +150,7 @@ let statusString: [NEVPNStatus: String] = [
]

// Represents a site that was pulled out of the system configuration
class Site: Codable {
class Site: Codable, @unchecked Sendable {
// Stored in manager
var name: String
var id: String
Expand Down Expand Up @@ -208,7 +208,7 @@ class Site: Codable {

let id = dict?["id"] as? String ?? nil
if id == nil {
throw SiteError.nonConforming(site: dict)
throw SiteError.nonConforming(site: String(describing: dict))
}

try self.init(path: SiteList.getSiteConfigFile(id: id!, createDir: false))
Expand Down
9 changes: 6 additions & 3 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = YES;
SWIFT_UPCOMING_FEATURE_DEPRECATE_APPLICATION_MAIN = YES;
SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES;
Expand All @@ -574,7 +575,7 @@
SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES;
SWIFT_UPCOMING_FEATURE_ISOLATED_DEFAULT_VALUES = YES;
SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES;
SWIFT_VERSION = 5.0;
SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
Expand Down Expand Up @@ -784,6 +785,7 @@
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = YES;
SWIFT_UPCOMING_FEATURE_DEPRECATE_APPLICATION_MAIN = YES;
SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES;
Expand All @@ -795,7 +797,7 @@
SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES;
SWIFT_UPCOMING_FEATURE_ISOLATED_DEFAULT_VALUES = YES;
SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES;
SWIFT_VERSION = 5.0;
SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
Expand Down Expand Up @@ -849,6 +851,7 @@
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = YES;
SWIFT_UPCOMING_FEATURE_DEPRECATE_APPLICATION_MAIN = YES;
SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES;
Expand All @@ -860,7 +863,7 @@
SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES;
SWIFT_UPCOMING_FEATURE_ISOLATED_DEFAULT_VALUES = YES;
SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES;
SWIFT_VERSION = 5.0;
SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
Expand Down
4 changes: 2 additions & 2 deletions ios/Runner/APIClient.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import MobileNebula
@preconcurrency import MobileNebula

enum APIClientError: Error {
case invalidCredentials
}

class APIClient {
struct APIClient: Sendable {
let apiClient: MobileNebulaAPIClient
let json = JSONDecoder()

Expand Down
24 changes: 13 additions & 11 deletions ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Flutter
@preconcurrency import Flutter
import MobileNebula
import NetworkExtension
import SwiftyJSON
Expand All @@ -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 {
for await site in dnUpdater.siteUpdates {
// 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 {
Expand Down
22 changes: 17 additions & 5 deletions ios/Runner/DNUpdate.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Foundation
import os.log

class DNUpdater {
class DNUpdater: @unchecked Sendable {
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, _) -> Void 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.
Expand All @@ -23,21 +23,33 @@ class DNUpdater {
}
}

func updateAllLoop(onUpdate: @escaping (Site) -> Void) {
func updateAllLoop(onUpdate: @Sendable @escaping (Site) -> Void) {
timer.eventHandler = {
self.updateAll(onUpdate: onUpdate)
}
timer.resume()
}

func updateSingleLoop(site: Site, onUpdate: @escaping (Site) -> Void) {
// Site updates provides an async/await alternative to `.updateAllLoop` that doesn't require a sendable closure.
// https://developer.apple.com/documentation/swift/asyncstream
var siteUpdates: AsyncStream<Site> {
AsyncStream { continuation in
self.updateAllLoop(onUpdate: { site in
continuation.yield(site)

})

}
}

func updateSingleLoop(site: Site, onUpdate: @Sendable @escaping (Site) -> Void) {
timer.eventHandler = {
self.updateSite(site: site, onUpdate: onUpdate)
}
timer.resume()
}

func updateSite(site: Site, onUpdate: @escaping (Site) -> Void) {
func updateSite(site: Site, onUpdate: @Sendable @escaping (Site) -> Void) {
do {
if !site.managed {
return
Expand Down
6 changes: 3 additions & 3 deletions ios/Runner/Sites.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ class Sites {
}
}

class SiteUpdater: NSObject, FlutterStreamHandler {
class SiteUpdater: NSObject, FlutterStreamHandler, @unchecked Sendable {
private var eventSink: FlutterEventSink?
private var eventChannel: FlutterEventChannel
private var site: Site
private var notification: Any?
public var startFunc: (() -> Void)?
private var configFd: Int32? = nil
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are also formatting changes from SwiftFormat, I can pull them out, but they're small correct changes, so 🤷

private var configObserver: (any DispatchSourceFileSystemObject)? = nil
private var configFd: Int32?
private var configObserver: (any DispatchSourceFileSystemObject)?

init(messenger: any FlutterBinaryMessenger, site: Site) {
do {
Expand Down