Swift 6 错误:`@Sendable` 闭包中的不可发送类型“Timer”

问题描述 投票:0回答:1

在 Xcode 16 中,我收到此错误,与 Swift 6 相关。

@Sendable
中捕获不可发送类型“Timer”的“timer” 关闭;这是 Swift 6 语言模式下的错误

enter image description here

如何使此代码兼容 Swift 6?

func updateBuyAmountWithJsValue(url: String, delaySeconds: Double, maxTries: Int, js: String) {
    var tries = 0
    Timer.scheduledTimer(withTimeInterval: delaySeconds, repeats: true) { timer in
        tries += 1
        if (self.webView.url?.absoluteString != url || tries > maxTries) {
            timer.invalidate()
            return
        }
        self.webView.evaluateJavaScript(js) { (result, error) in
            if let error = error {
                print("Error executing JavaScript: \(error)")
            } else if let value = result as? String {
                if value.contains(".") || (tries==maxTries && value.count < 10) {
                    self.updateBuyProgress("AMOUNT*" + value)
                    timer.invalidate()
                }
            }
        }
    }
}
ios swift closures swift6 sendable
1个回答
0
投票

您可以将闭包包装在与全局 MainActor 隔离的任务中,以避免此警告:

Timer.scheduledTimer(withTimeInterval: delaySeconds, repeats: true) { timer in
    Task { @MainActor in //<- here
        ...
        timer.invalidate() //⚠️
    }
}

但是,这里仍然存在警告/错误,并且在我看来,这是不可能解决的。因为闭包标有

@Sendable
,但是Timer本身并不符合
Sendable
,除非你显式地覆盖
Foudation
之外的扩展。

开放类 func ScheduledTimer(withTimeInterval 间隔:TimeInterval,重复:Bool,块:@escaping @Sendable (Timer) -> Void) -> Timer

@available(*, unavailable)
extension Timer : @unchecked Sendable {
}

因为

evaluateJavaScript
也支持
async await
。我更愿意重构代码:

final class ViewController: UIViewController {
    private let webView = WKWebView()
    private let maxTries = 3
    
    private var task: Task<Void, Never>?
    ...
    
    //✅ entry point here
    private func startSchedule(with url: String, and js: String) {
        executeTask(tries: 0, with: url, and: js)
    }

    private func executeTask(tries: Int, with url: String, and js: String) {
        task = Task {
            if self.webView.url?.absoluteString != url || tries > self.maxTries {
                self.task?.cancel()
            }
            
            do {
                let result = try await evaluateWebJS(js)
                
                if let result {
                    if result.contains(".") || (tries==maxTries && result.count < 10) {
                        self.updateBuyProgress("AMOUNT*" + result)
                    }
                    self.task?.cancel()
                } else {
                    guard !Task.isCancelled else {
                        return
                    }

                    //recursive call until the max retries have been reached
                    executeTask(tries: tries + 1, with: url, and: js)
                }
            } catch {
                print("cannot evaluate JS: \(error)")
                self.task?.cancel()
            }
        }
    }

    private func evaluateJS(_ js: String) async throws -> String? {
        let result = try await webView.evaluateJavaScript(js)
        guard let output = result as? String else { return nil }
            return output
        }
    }
}

© www.soinside.com 2019 - 2024. All rights reserved.