如何无需显式触发主线程初始化自定义WKWebView?
问题
我在代码中使用一个自定义WKWebView:
public class AuthenticatedWebView: WKWebView, AdInAuthenticatedWebView { private var shouldStopFurtherRedirects = false public weak var authDelegate: AuthenticatedWebViewDelegate? private let authenticationService = AuthenticationServiceProvider.service public init() { super.init(frame: .zero, configuration: WKWebViewConfiguration.config) setup() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setup() { navigationDelegate = self shouldStopFurtherRedirects = false } public func authenticate() { authenticationService.webSessionURL(redirectURL: nil, completion: { url, error in if let url = url { let request = URLRequest(url: url) self.load(request) } else { self.authDelegate?.webView(self, authenticationDidFailWithError: error) } }) } }
当我尝试初始化该视图时出现以下错误:
init(delegate: AdInAuthenticatedWebViewServiceDelegate?) { authDelegate = AdInAuthenticatedWebViewServiceDelegateWrapper() authDelegate?.adInDelegate = delegate authWebView = AuthenticatedWebView() authWebView.authDelegate = authDelegate }
错误信息:Call to main actor-isolated initializer 'init()' in a synchronous nonisolated contextMain actor-isolated property 'authDelegate' can not be mutated from a non-isolated context
我在项目其他位置使用完全相同的自定义WebView实现却未出现该错误,查阅WKWebView文档也未发现其初始化方法有@MainActor要求。已确认问题源于继承WKWebView,但尚未找到解决办法,且在另一个项目中类似实现可正常运行。
解决方法
1. 给自定义WebView类显式标记@MainActor
iOS 16+的SDK中,WKWebView被隐式标记为@MainActor(文档未明确说明但实际执行规则如此),继承它的类会自动继承主线程隔离。显式给类加上标记,确保所有相关代码在主线程上下文执行:
@MainActor public class AuthenticatedWebView: WKWebView, AdInAuthenticatedWebView { // 原有代码保持不变 }
2. 在主线程上下文执行初始化和属性修改
如果不想给整个类加@MainActor,可以把初始化WebView、修改属性的代码放到主线程异步任务中:
init(delegate: AdInAuthenticatedWebViewServiceDelegate?) { authDelegate = AdInAuthenticatedWebViewServiceDelegateWrapper() authDelegate?.adInDelegate = delegate // 切换到主线程上下文执行 Task { @MainActor in authWebView = AuthenticatedWebView() authWebView.authDelegate = authDelegate } }
注意:这种情况下authWebView需要定义为可选类型,或者通过await等待任务完成,避免空值访问问题。
3. 检查项目版本差异
你提到另一个项目能正常运行,大概率是两个项目的Swift版本或iOS SDK版本不同。iOS 16+对UIKit类的Actor隔离规则更严格,旧版本没有此限制。可以检查当前项目的部署目标、Swift版本,调整到和正常项目一致,或者适配新的隔离规则。
4. 单独给初始化方法标记@MainActor
如果不想给整个类加标记,也可以只给自定义初始化方法添加@MainActor:
public class AuthenticatedWebView: WKWebView, AdInAuthenticatedWebView { // ...其他代码 @MainActor public init() { super.init(frame: .zero, configuration: WKWebViewConfiguration.config) setup() } // ...其他代码 }
然后在初始化时,同样要确保在主线程上下文调用:
init(delegate: AdInAuthenticatedWebViewServiceDelegate?) { authDelegate = AdInAuthenticatedWebViewServiceDelegateWrapper() authDelegate?.adInDelegate = delegate Task { @MainActor in authWebView = AuthenticatedWebView() authWebView.authDelegate = authDelegate } }
内容的提问来源于stack exchange,提问作者Saad Qureshi

