Decouple WidgetExtension from Library

This commit is contained in:
世界
2026-02-05 15:19:14 +08:00
parent 20f2482376
commit f765bf1e3b
3 changed files with 74 additions and 30 deletions
+4 -15
View File
@@ -1,12 +1,11 @@
import AppIntents
import Library
import SwiftUI
import WidgetKit
struct ServiceToggleControl: ControlWidget {
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(
kind: ExtensionProfile.controlKind,
kind: WidgetAppConfiguration.widgetControlKind,
provider: Provider()
) { value in
ControlWidgetToggle(
@@ -15,7 +14,7 @@ struct ServiceToggleControl: ControlWidget {
action: ToggleServiceControlIntent()
) { isOn in
Label(isOn ? "Running" : "Stopped", systemImage: "shippingbox.fill")
// .controlWidgetActionHint(isOn ? "Stop" : "Start")
.controlWidgetActionHint(isOn ? "Stop" : "Start")
}
.tint(.init(red: CGFloat(Double(69) / 255), green: CGFloat(Double(90) / 255), blue: CGFloat(Double(100) / 255)))
}
@@ -31,10 +30,7 @@ extension ServiceToggleControl {
}
func currentValue() async throws -> Bool {
guard let extensionProfile = try await (ExtensionProfile.load()) else {
return false
}
return await extensionProfile.status.isStarted
try await WidgetTunnelControl.currentIsStarted()
}
}
}
@@ -46,14 +42,7 @@ struct ToggleServiceControlIntent: SetValueIntent {
var value: Bool
func perform() async throws -> some IntentResult {
guard let extensionProfile = try await (ExtensionProfile.load()) else {
return .result()
}
if value {
try await extensionProfile.start()
} else {
try await extensionProfile.stop()
}
try await WidgetTunnelControl.setStarted(value)
return .result()
}
}
+70
View File
@@ -0,0 +1,70 @@
import Foundation
import NetworkExtension
enum WidgetAppConfiguration {
static let packageName: String = {
guard let value = Bundle.main.object(forInfoDictionaryKey: "BasePackageIdentifier") as? String else {
fatalError("Missing BasePackageIdentifier in Info.plist")
}
return value
}()
static let appGroupID: String = {
guard let value = Bundle.main.object(forInfoDictionaryKey: "AppGroupIdentifier") as? String else {
fatalError("Missing AppGroupIdentifier in Info.plist")
}
return value
}()
static var widgetControlKind: String { "\(packageName).widget.ServiceToggle" }
}
extension NEVPNStatus {
var isStarted: Bool {
switch self {
case .connecting, .connected, .reasserting:
return true
default:
return false
}
}
}
enum WidgetTunnelControl {
static func currentIsStarted() async throws -> Bool {
guard let manager = try await loadManager() else {
return false
}
return manager.connection.status.isStarted
}
static func setStarted(_ started: Bool) async throws {
guard let manager = try await loadManager() else {
NSLog("[WidgetTunnelControl] No tunnel configuration found")
throw NSError(domain: "WidgetTunnelControl", code: 1, userInfo: [
NSLocalizedDescriptionKey: "Tunnel configuration not found",
])
}
if started {
if manager.isEnabled == false {
manager.isEnabled = true
try await manager.saveToPreferences()
}
do {
try manager.connection.startVPNTunnel()
} catch {
NSLog("[WidgetTunnelControl] startVPNTunnel failed: \(error.localizedDescription)")
throw error
}
} else {
manager.connection.stopVPNTunnel()
}
}
private static func loadManager() async throws -> NETunnelProviderManager? {
let managers = try await NETunnelProviderManager.loadAllFromPreferences()
return managers.first
}
}