Update Tunnel on the main thread
Also updated it to use the new Swift Observable macro
This commit is contained in:
parent
b008762a5b
commit
436a67b352
10 changed files with 167 additions and 69 deletions
|
|
@ -15,7 +15,7 @@ struct BurrowApp: App {
|
|||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
TunnelView()
|
||||
TunnelView(tunnel: Self.tunnel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
import SwiftUI
|
||||
|
||||
struct MenuItemToggleView: View {
|
||||
@ObservedObject var tunnel: Tunnel
|
||||
var tunnel: Tunnel
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
|
|
@ -23,7 +23,6 @@ struct MenuItemToggleView: View {
|
|||
.padding(.horizontal, 4)
|
||||
.padding(10)
|
||||
.frame(minWidth: 300, minHeight: 32, maxHeight: 32)
|
||||
.task { await tunnel.update() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ import NetworkExtension
|
|||
|
||||
extension NEVPNManager {
|
||||
func remove() async throws {
|
||||
let _: Void = try await withUnsafeThrowingContinuation { continuation in
|
||||
_ = try await withUnsafeThrowingContinuation { continuation in
|
||||
removeFromPreferences(completionHandler: completion(continuation))
|
||||
}
|
||||
}
|
||||
|
||||
func save() async throws {
|
||||
let _: Void = try await withUnsafeThrowingContinuation { continuation in
|
||||
_ = try await withUnsafeThrowingContinuation { continuation in
|
||||
saveToPreferences(completionHandler: completion(continuation))
|
||||
}
|
||||
}
|
||||
|
|
@ -18,13 +18,7 @@ extension NETunnelProviderManager {
|
|||
class var managers: [NETunnelProviderManager] {
|
||||
get async throws {
|
||||
try await withUnsafeThrowingContinuation { continuation in
|
||||
loadAllFromPreferences { managers, error in
|
||||
if let error = error {
|
||||
continuation.resume(throwing: error)
|
||||
} else {
|
||||
continuation.resume(returning: managers ?? [])
|
||||
}
|
||||
}
|
||||
loadAllFromPreferences(completionHandler: completion(continuation))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,10 +26,20 @@ extension NETunnelProviderManager {
|
|||
|
||||
private func completion(_ continuation: UnsafeContinuation<Void, Error>) -> (Error?) -> Void {
|
||||
return { error in
|
||||
if let error = error {
|
||||
if let error {
|
||||
continuation.resume(throwing: error)
|
||||
} else {
|
||||
continuation.resume(returning: ())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func completion<T>(_ continuation: UnsafeContinuation<T, Error>) -> (T?, Error?) -> Void {
|
||||
return { value, error in
|
||||
if let error {
|
||||
continuation.resume(throwing: error)
|
||||
} else if let value {
|
||||
continuation.resume(returning: value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,16 @@ import Combine
|
|||
import NetworkExtension
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
class Tunnel: ObservableObject {
|
||||
@Published private(set) var status: Status = .unknown
|
||||
@Published private var error: NEVPNError?
|
||||
@Observable class Tunnel {
|
||||
private(set) var status: Status = .unknown
|
||||
private var error: NEVPNError?
|
||||
|
||||
private let bundleIdentifier: String
|
||||
private let configure: (NETunnelProviderManager, NETunnelProviderProtocol) -> Void
|
||||
private var tasks: [Task<Void, Error>] = []
|
||||
|
||||
// Each manager corresponds to one entry in the Settings app.
|
||||
// Our goal is to maintain a single manager, so we create one if none exist and delete extra if there are any.
|
||||
private var managers: [NEVPNManager]? {
|
||||
didSet { status = currentStatus }
|
||||
}
|
||||
|
|
@ -48,24 +49,31 @@ class Tunnel: ObservableObject {
|
|||
self.bundleIdentifier = bundleIdentifier
|
||||
self.configure = configure
|
||||
|
||||
listenForUpdates()
|
||||
Task { await update() }
|
||||
}
|
||||
|
||||
private func listenForUpdates() {
|
||||
let center = NotificationCenter.default
|
||||
let statusTask = Task {
|
||||
for try await _ in NotificationCenter.default.notifications(named: .NEVPNStatusDidChange) {
|
||||
for try await _ in center.notifications(named: .NEVPNStatusDidChange).map({ _ in () }) {
|
||||
status = currentStatus
|
||||
}
|
||||
}
|
||||
let configurationTask = Task {
|
||||
for try await _ in NotificationCenter.default.notifications(named: .NEVPNConfigurationChange) {
|
||||
for try await _ in center.notifications(named: .NEVPNConfigurationChange).map({ _ in () }) {
|
||||
await update()
|
||||
}
|
||||
}
|
||||
tasks = [statusTask, configurationTask]
|
||||
}
|
||||
|
||||
func update() async {
|
||||
private func update() async {
|
||||
do {
|
||||
managers = try await NETunnelProviderManager.managers
|
||||
} catch let error as NEVPNError {
|
||||
self.error = error
|
||||
let updated = try await NETunnelProviderManager.managers
|
||||
await MainActor.run { managers = updated }
|
||||
} catch let vpnError as NEVPNError {
|
||||
error = vpnError
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
|
|
@ -109,7 +117,9 @@ class Tunnel: ObservableObject {
|
|||
}
|
||||
|
||||
deinit {
|
||||
tasks.forEach { $0.cancel() }
|
||||
for task in tasks {
|
||||
task.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,36 +1,34 @@
|
|||
import SwiftUI
|
||||
|
||||
struct TunnelView: View {
|
||||
// @ObservedObject var tunnel: Tunnel
|
||||
var tunnel: Tunnel
|
||||
|
||||
var body: some View {
|
||||
EmptyView()
|
||||
// VStack {
|
||||
// Text(verbatim: tunnel.status.description)
|
||||
// switch tunnel.status {
|
||||
// case .connected:
|
||||
// Button("Disconnect", action: stop)
|
||||
// case .permissionRequired:
|
||||
// Button("Allow", action: configure)
|
||||
// case .disconnected:
|
||||
// Button("Start", action: start)
|
||||
// default:
|
||||
// EmptyView()
|
||||
// }
|
||||
// }
|
||||
// .task { await tunnel.update() }
|
||||
// .padding()
|
||||
VStack {
|
||||
Text(verbatim: tunnel.status.description)
|
||||
switch tunnel.status {
|
||||
case .connected:
|
||||
Button("Disconnect", action: stop)
|
||||
case .permissionRequired:
|
||||
Button("Allow", action: configure)
|
||||
case .disconnected:
|
||||
Button("Start", action: start)
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
// private func start() {
|
||||
// try? tunnel.start()
|
||||
// }
|
||||
//
|
||||
// private func stop() {
|
||||
// tunnel.stop()
|
||||
// }
|
||||
//
|
||||
// private func configure() {
|
||||
// Task { try await tunnel.configure() }
|
||||
// }
|
||||
private func start() {
|
||||
try? tunnel.start()
|
||||
}
|
||||
|
||||
private func stop() {
|
||||
tunnel.stop()
|
||||
}
|
||||
|
||||
private func configure() {
|
||||
Task { try await tunnel.configure() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue