fix(client/macos): PacketTunnelProvider 改用 completion-handler 绕开 Swift 6.2 编译器崩溃
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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user