Fix several concurrency warnings.

This commit is contained in:
Brent Simmons 2024-07-01 22:20:00 -07:00
parent cc71152b16
commit 79c2f4c7f5
2 changed files with 122 additions and 83 deletions

View File

@ -8,11 +8,11 @@
import Foundation import Foundation
import WebKit import WebKit
@preconcurrency import Images import Images
protocol ArticleIconSchemeHandlerDelegate: AnyObject { protocol ArticleIconSchemeHandlerDelegate: AnyObject {
func iconImage(for articleID: String) -> IconImage? @MainActor func iconImage(for articleID: String) -> IconImage?
} }
final class ArticleIconSchemeHandler: NSObject, WKURLSchemeHandler { final class ArticleIconSchemeHandler: NSObject, WKURLSchemeHandler {
@ -34,6 +34,8 @@ final class ArticleIconSchemeHandler: NSObject, WKURLSchemeHandler {
return return
} }
let articleID = components.path let articleID = components.path
MainActor.assumeIsolated {
guard let iconImage = delegate.iconImage(for: articleID) else { guard let iconImage = delegate.iconImage(for: articleID) else {
urlSchemeTask.didFailWithError(URLError(.fileDoesNotExist)) urlSchemeTask.didFailWithError(URLError(.fileDoesNotExist))
return return
@ -58,6 +60,7 @@ final class ArticleIconSchemeHandler: NSObject, WKURLSchemeHandler {
} }
} }
} }
}
func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) { func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
urlSchemeTask.didFailWithError(URLError(.unknown)) urlSchemeTask.didFailWithError(URLError(.unknown))

View File

@ -15,6 +15,7 @@ import MessageUI
import Core import Core
import ArticleExtractor import ArticleExtractor
import Images import Images
import Web
protocol WebViewControllerDelegate: AnyObject { protocol WebViewControllerDelegate: AnyObject {
@ -388,17 +389,53 @@ extension WebViewController: UIContextMenuInteractionDelegate {
extension WebViewController: WKNavigationDelegate { extension WebViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { nonisolated func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.navigationType == .linkActivated { guard navigationAction.navigationType == .linkActivated else {
decisionHandler(.allow)
return
}
guard let url = navigationAction.request.url else { guard let url = navigationAction.request.url else {
decisionHandler(.allow) decisionHandler(.allow)
return return
} }
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let scheme = components.scheme else {
decisionHandler(.allow)
return
}
let components = URLComponents(url: url, resolvingAgainstBaseURL: false) switch scheme {
if components?.scheme == "http" || components?.scheme == "https" {
case URLScheme.http, URLScheme.https:
decisionHandler(.cancel) decisionHandler(.cancel)
openURLInBrowser(url)
case URLScheme.mailto:
decisionHandler(.cancel)
guard let emailAddressURL = url.percentEncodedEmailAddress else {
return
}
openEmailAddressURL(emailAddressURL)
case URLScheme.tel:
decisionHandler(.cancel)
openTelURL(url)
default:
decisionHandler(.allow)
}
}
nonisolated func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
Task { @MainActor in
fullReload()
}
}
nonisolated private func openURLInBrowser(_ url: URL) {
Task { @MainActor in
if AppDefaults.shared.useSystemBrowser { if AppDefaults.shared.useSystemBrowser {
UIApplication.shared.open(url, options: [:]) UIApplication.shared.open(url, options: [:])
} else { } else {
@ -410,59 +447,52 @@ extension WebViewController: WKNavigationDelegate {
self.present(vc, animated: true) self.present(vc, animated: true)
} }
} }
}
} else if components?.scheme == "mailto" {
decisionHandler(.cancel)
guard let emailAddress = url.percentEncodedEmailAddress else {
return
} }
if UIApplication.shared.canOpenURL(emailAddress) { nonisolated private func openEmailAddressURL(_ url: URL) {
UIApplication.shared.open(emailAddress, options: [.universalLinksOnly : false], completionHandler: nil)
Task { @MainActor in
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [.universalLinksOnly : false], completionHandler: nil)
} else { } else {
let alert = UIAlertController(title: NSLocalizedString("Error", comment: "Error"), message: NSLocalizedString("This device cannot send emails.", comment: "This device cannot send emails."), preferredStyle: .alert) let alert = UIAlertController(title: NSLocalizedString("Error", comment: "Error"), message: NSLocalizedString("This device cannot send emails.", comment: "This device cannot send emails."), preferredStyle: .alert)
alert.addAction(.init(title: NSLocalizedString("Dismiss", comment: "Dismiss"), style: .cancel, handler: nil)) alert.addAction(.init(title: NSLocalizedString("Dismiss", comment: "Dismiss"), style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} }
} else if components?.scheme == "tel" { }
decisionHandler(.cancel) }
nonisolated private func openTelURL(_ url: URL) {
Task { @MainActor in
if UIApplication.shared.canOpenURL(url) { if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [.universalLinksOnly : false], completionHandler: nil) UIApplication.shared.open(url, options: [.universalLinksOnly : false], completionHandler: nil)
} }
} else {
decisionHandler(.allow)
}
} else {
decisionHandler(.allow)
} }
} }
func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
fullReload()
}
} }
// MARK: WKUIDelegate // MARK: WKUIDelegate
extension WebViewController: WKUIDelegate { extension WebViewController: WKUIDelegate {
func webView(_ webView: WKWebView, contextMenuForElement elementInfo: WKContextMenuElementInfo, willCommitWithAnimator animator: UIContextMenuInteractionCommitAnimating) { nonisolated func webView(_ webView: WKWebView, contextMenuForElement elementInfo: WKContextMenuElementInfo, willCommitWithAnimator animator: UIContextMenuInteractionCommitAnimating) {
// We need to have at least an unimplemented WKUIDelegate assigned to the WKWebView. This makes the // We need to have at least an unimplemented WKUIDelegate assigned to the WKWebView. This makes the
// link preview launch Safari when the link preview is tapped. In theory, you should be able to get // link preview launch Safari when the link preview is tapped. In theory, you should be able to get
// the link from the elementInfo above and transition to SFSafariViewController instead of launching // the link from the elementInfo above and transition to SFSafariViewController instead of launching
// Safari. As the time of this writing, the link in elementInfo is always nil. ¯\_()_/¯ // Safari. As the time of this writing, the link in elementInfo is always nil. ¯\_()_/¯
} }
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? { nonisolated func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
guard let url = navigationAction.request.url else { guard let url = navigationAction.request.url else {
return nil return nil
} }
Task { @MainActor in
openURL(url) openURL(url)
}
return nil return nil
} }
@ -472,12 +502,17 @@ extension WebViewController: WKUIDelegate {
extension WebViewController: WKScriptMessageHandler { extension WebViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { nonisolated func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
switch message.name {
let name = message.name
let body = message.body as? String
Task { @MainActor in
switch name {
case MessageName.imageWasShown: case MessageName.imageWasShown:
clickedImageCompletion?() clickedImageCompletion?()
case MessageName.imageWasClicked: case MessageName.imageWasClicked:
imageWasClicked(body: message.body as? String) imageWasClicked(body: body)
case MessageName.showFeedInspector: case MessageName.showFeedInspector:
if let feed = article?.feed { if let feed = article?.feed {
coordinator.showFeedInspector(for: feed) coordinator.showFeedInspector(for: feed)
@ -486,6 +521,7 @@ extension WebViewController: WKScriptMessageHandler {
return return
} }
} }
}
} }