fix(client/macos): PacketTunnelProvider 改用 completion-handler 绕开 Swift 6.2 编译器崩溃
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled

Swift 6.2.3 在为 async 重写的 startTunnel 生成 ObjC 桥接 thunk 时崩溃
(emitInjectLoadableEnum)。改用 completion-handler 形式(直接是 @objc 原型)
绕开。startTunnel/stopTunnel/handleAppMessage 三个 override 同步改。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-19 14:30:11 +08:00
parent 7e92a9a711
commit 8367984e23
@@ -30,7 +30,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
// App Group : sing-box (/) app
private static let appGroup = "group.com.pangolin.pangolin"
override func startTunnel(options: [String: NSObject]?) async throws {
// completion-handler ( async):Swift 6.2 async
// ObjC thunk (emitInjectLoadableEnum),completion-handler
// @objc , bug completionHandler(nil), error
override func startTunnel(options: [String: NSObject]?,
completionHandler: @escaping (Error?) -> Void) {
log.info("startTunnel")
// 1) sing-box JSON: startVPNTunnel options["configContent"],
@@ -42,15 +46,17 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
configContent = (try? Self.readSharedConfig()) ?? ""
}
guard !configContent.isEmpty else {
throw NSError(domain: "pangolin.tunnel", code: 1,
userInfo: [NSLocalizedDescriptionKey: "empty sing-box config"])
completionHandler(NSError(domain: "pangolin.tunnel", code: 1,
userInfo: [NSLocalizedDescriptionKey: "empty sing-box config"]))
return
}
// 2) libbox ( App Group ,sysess )
// 2) libbox ( App Group ,)
guard let base = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: Self.appGroup) else {
throw NSError(domain: "pangolin.tunnel", code: 2,
userInfo: [NSLocalizedDescriptionKey: "no app group container"])
completionHandler(NSError(domain: "pangolin.tunnel", code: 2,
userInfo: [NSLocalizedDescriptionKey: "no app group container"]))
return
}
let work = base.appendingPathComponent("work", isDirectory: true)
try? FileManager.default.createDirectory(at: work, withIntermediateDirectories: true)
@@ -62,36 +68,41 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
setup.workingPath = work.path
setup.tempPath = NSTemporaryDirectory()
LibboxSetup(setup, &setupError)
if let setupError { throw setupError }
if let setupError { completionHandler(setupError); return }
let platform = PangolinPlatformInterface(provider: self)
self.platformInterface = platform
var newError: NSError?
guard let service = LibboxNewService(configContent, platform, &newError) else {
throw newError ?? NSError(domain: "pangolin.tunnel", code: 3)
completionHandler(newError ?? NSError(domain: "pangolin.tunnel", code: 3)); return
}
self.boxService = service
try service.start()
do { try service.start() } catch { completionHandler(error); return }
completionHandler(nil) //
return
*/
// :xcframework ,startTunnel ,
throw NSError(domain: "pangolin.tunnel", code: 99, userInfo: [
// :xcframework ,,
completionHandler(NSError(domain: "pangolin.tunnel", code: 99, userInfo: [
NSLocalizedDescriptionKey:
"Libbox 尚未接入(见 docs/p1-macos-system-extension.md 步骤 3)",
])
]))
}
override func stopTunnel(with reason: NEProviderStopReason) async {
override func stopTunnel(with reason: NEProviderStopReason,
completionHandler: @escaping () -> Void) {
log.info("stopTunnel reason=\(reason.rawValue)")
/* if let service = boxService as? LibboxBoxService { try? service.close() } */
boxService = nil
platformInterface = nil
completionHandler()
}
// app sendProviderMessage (); libbox CommandServer
override func handleAppMessage(_ messageData: Data) async -> Data? {
nil
override func handleAppMessage(_ messageData: Data,
completionHandler: ((Data?) -> Void)?) {
completionHandler?(nil)
}
private static func readSharedConfig() throws -> String {