在 Xcode 16 中,我收到此错误,与 Swift 6 相关。
在
中捕获不可发送类型“Timer”的“timer” 关闭;这是 Swift 6 语言模式下的错误@Sendable
如何使此代码兼容 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()
}
}
}
}
}
您可以将闭包包装在与全局 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
}
}
}