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

使用WCSession.transferFile从WatchOS传文件至iOS时didFinish回调丢失

临时解决WCSession.transferFile(_:metadata:)在watchOS 10.5/iOS 17.5.1下代理不触发的问题

问题场景

使用WCSession.transferFile(_:metadata:)从watchOS 10.5向iOS 17.5.1传输文件时,WCSessionDelegate的didFinish代理方法完全不触发。调试控制台输出错误:

[WCFileStorage persistOutgoingFileTransfer:] error serializing file transfer <WCSessionFileTransfer: 0x300155d60, session file: <WCSessionFile: 0x3001575c0, identifier: 0C8857EC-7D74-4E78-BA28-6C5526DE8949, file: FILE.EXT, hasMetadata: YES>, transferring: YES> due to Error Domain=NSCocoaErrorDomain Code=4866 "Caught exception during archival: This object may only be encoded by an NSXPCCoder. (...)"

该错误未转发至代理,且WCSession.outstandingFileTransfers显示所有任务一直处于排队状态。这是watchOS/iOS新版本的已知Bug,且模拟器不支持transferFile(_:metadata:)无法充分测试。

临时解决方案

1. 拆分metadata与文件传输,用transferUserInfo传递额外信息

放弃在transferFile中携带metadata,转而通过transferUserInfo(_:)单独传递文件的alternateId和type,在iOS端将用户信息与后续接收的文件关联:

// watchOS端:先传递用户信息
let fileUUID = UUID().uuidString
session.transferUserInfo(["alternateId": file.alternateId, "type": file.type, "fileUUID": fileUUID])
// 再传输文件(metadata传nil)
fileTransfer = session.transferFile(file.url, metadata: nil)

iOS端暂存用户信息,在接收文件时通过UUID关联:

var pendingFileInfos = [String: [String: Any]]()

func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
    guard let fileUUID = userInfo["fileUUID"] as? String else { return }
    pendingFileInfos[fileUUID] = userInfo
}

func session(_ session: WCSession, didReceive file: WCSessionFile) {
    if let info = pendingFileInfos.removeValue(forKey: file.fileIdentifier) {
        let alternateId = info["alternateId"]
        let type = info["type"]
        // 处理文件与关联信息
    }
    // 文件存储等逻辑
}

2. 将metadata序列化为JSON字符串传入

把原本的metadata字典序列化为JSON字符串,作为单一值传入transferFile的metadata参数,iOS端接收后反序列化:

// watchOS端:序列化metadata
do {
    let metaDict = ["alternateId": file.alternateId, "type": file.type]
    let jsonData = try JSONSerialization.data(withJSONObject: metaDict)
    let jsonStr = String(data: jsonData, encoding: .utf8)
    fileTransfer = session.transferFile(file.url, metadata: ["meta": jsonStr])
} catch {
    // 处理序列化错误
}

iOS端反序列化解析:

func session(_ session: WCSession, didReceive file: WCSessionFile) {
    guard let jsonStr = file.metadata?["meta"] as? String,
          let jsonData = jsonStr.data(using: .utf8),
          let metaDict = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else {
        return
    }
    let alternateId = metaDict["alternateId"]
    let type = metaDict["type"]
    // 处理文件与元数据
}

3. 定期轮询任务状态,重试超时任务

由于代理不返回错误,可定期检查outstandingFileTransfers,对超时排队的任务取消并重试:

// watchOS端设置定时器,每30秒检查一次
Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { [weak self] timer in
    guard let self = self else { timer.invalidate(); return }
    let staleTransfers = self.session.outstandingFileTransfers.filter {
        // 判断任务是否超时(示例:超过5分钟)
        Date().timeIntervalSince($0.timestamp) > 300
    }
    for transfer in staleTransfers {
        transfer.cancel()
        // 结合方案1或2重新发起传输
        self.retryTransfer(for: transfer.file)
    }
}

注意事项

  • 方案1和2需保证信息与文件的关联准确性,优先用文件的唯一标识符匹配
  • 方案3的轮询间隔需根据实际场景调整,避免过度消耗设备电量

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 19:31:13