You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS Sharing Extension异常:URL与HTML上传服务器功能失效求助

排查iOS Sharing Extension上传失效问题

我正在开发一款iOS Sharing Extension,用于获取URL与HTML内容并上传至服务器。该代码曾成功运行2次,但之后突然失效,目前无法复现成功场景。已确认可从主应用获取FirebaseAccessToken,但作为Swift新手,完全找不到问题原因,恳请帮忙排查。

相关代码如下:

override func didSelectPost() {
    let url = self.extensionContext!.inputItems[0] as! NSExtensionItem
    let itemProvider = url.attachments![0] as NSItemProvider
    
    if itemProvider.hasItemConformingToTypeIdentifier("public.url") {
        itemProvider.loadItem(forTypeIdentifier: "public.url", options: nil) { (url, error) in
            if let error = error {
                // Handle error
                return
            }
            
            guard let shareURL = url as? URL else {
                return
            }
            
            // Get the HTML
            guard let htmlString = try? String(contentsOf: shareURL) else {
                return
            }
            
            let shareData = ShareData(url: shareURL.absoluteString, html: htmlString)
            guard let jsonData = try? JSONEncoder().encode(shareData) else {
                return
            }
            
            // Do  sending it to a server
            let token = UserDefaults(suiteName: "group.com.readout.app")?.string(forKey: "FirebaseAccessToken")
                        let url = URL(string: "http://blabla.com/api/article")!
                        var request = URLRequest(url: url)
                        request.httpMethod = "POST"
                        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
                        request.setValue("Bearer \(token ?? "")", forHTTPHeaderField: "Authorization")
                        request.httpBody = jsonData
                        
                        let task = URLSession.shared.dataTask(with: request) { data, response, error in
                            if let error = error {
                                // Handle error
                                return
                            }
                            
                            guard let response = response as? HTTPURLResponse else {
                                return
                            }
                            
                            if response.statusCode == 200 {
                                // Successfully sent data
                            } else {
                                // Handle non-200 response
                            }
                        }
                        task.resume()
                    }
                }

            self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}

问题排查与修复建议

1. 核心问题:Extension生命周期被提前终止

你在loadItem的异步闭包执行前就调用了completeRequest,Extension会直接结束生命周期,导致后续的HTML获取、网络请求等异步任务被强制中断。之前偶尔成功是因为闭包执行速度刚好快于completeRequest,但这是不可靠的。

修复:把completeRequest移到网络请求完成的闭包内,确保所有异步任务结束后再终止Extension:

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    // ... 原有逻辑 ...
    
    // 所有任务完成后再终止Extension
    DispatchQueue.main.async {
        self.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
    }
}
task.resume()

2. 错误处理缺失,无法定位故障点

代码中大量使用try?和空的错误处理块,出现任何错误都会静默失败,完全不知道哪一步出问题。比如:

  • String(contentsOf: shareURL)可能因网络错误、网站反爬限制失败
  • JSONEncoder.encode可能因ShareData不符合Codable要求失败
  • loadItem可能因权限问题获取URL失败

修复:添加错误日志打印,替换try?为do-catch:

// 获取HTML时
do {
    let htmlString = try String(contentsOf: shareURL)
    // 后续逻辑
} catch {
    print("获取HTML失败:\(error.localizedDescription)")
    DispatchQueue.main.async {
        self.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
    }
    return
}

// 编码JSON时
do {
    let jsonData = try JSONEncoder().encode(shareData)
    // 后续逻辑
} catch {
    print("JSON编码失败:\(error.localizedDescription)")
    DispatchQueue.main.async {
        self.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
    }
    return
}

3. 强制解包存在崩溃风险

extensionContext!.inputItems[0]、url.attachments![0]都是强制解包,如果输入项为空或无附件,会直接崩溃,且Extension崩溃不会在主应用中显示日志。

修复:用可选绑定替换强制解包:

guard let extensionItem = extensionContext?.inputItems.first as? NSExtensionItem,
      let itemProvider = extensionItem.attachments?.first as? NSItemProvider else {
    print("无法获取输入项或附件")
    extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
    return
}

4. 网络请求的额外注意事项

  • 确保Extension的Info.plist中添加了对应服务器的网络权限(比如HTTP请求需要关闭App Transport Security或添加例外)
  • 自定义URLSession配置,避免共享session的生命周期问题:
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: request) { ... }

内容的提问来源于stack exchange,提问作者Vynny zePooh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 20:20:26