From cf8dbb26f7aaa3333c26bde227927ccd957eedea Mon Sep 17 00:00:00 2001 From: Maurice Parker <mo@vincode.io> Date: Tue, 1 Sep 2020 18:54:46 -0500 Subject: [PATCH] Resolve issue where we could have a web view deallocated before getting displayed. --- .../Shared/Article/PreloadedWebView.swift | 8 +-- .../Shared/Article/WebViewProvider.swift | 8 +-- .../iOS/Article/WebViewController.swift | 56 ++++++++++--------- .../macOS/Article/WebViewController.swift | 41 +++++++------- iOS/Article/PreloadedWebView.swift | 8 +-- iOS/Article/WebViewController.swift | 56 ++++++++++--------- iOS/Article/WebViewProvider.swift | 8 +-- 7 files changed, 94 insertions(+), 91 deletions(-) diff --git a/Multiplatform/Shared/Article/PreloadedWebView.swift b/Multiplatform/Shared/Article/PreloadedWebView.swift index baa99a48b..2baca9e73 100644 --- a/Multiplatform/Shared/Article/PreloadedWebView.swift +++ b/Multiplatform/Shared/Article/PreloadedWebView.swift @@ -13,7 +13,7 @@ import RSWeb class PreloadedWebView: WKWebView { private var isReady: Bool = false - private var readyCompletion: ((PreloadedWebView) -> Void)? + private var readyCompletion: (() -> Void)? init(articleIconSchemeHandler: ArticleIconSchemeHandler) { let preferences = WKPreferences() @@ -44,7 +44,7 @@ class PreloadedWebView: WKWebView { loadFileURL(ArticleRenderer.blank.url, allowingReadAccessTo: ArticleRenderer.blank.baseURL) } - func ready(completion: @escaping (PreloadedWebView) -> Void) { + func ready(completion: @escaping () -> Void) { if isReady { completeRequest(completion: completion) } else { @@ -89,10 +89,10 @@ extension PreloadedWebView: WKNavigationDelegate { private extension PreloadedWebView { - func completeRequest(completion: @escaping (PreloadedWebView) -> Void) { + func completeRequest(completion: @escaping () -> Void) { isReady = false navigationDelegate = nil - completion(self) + completion() } } diff --git a/Multiplatform/Shared/Article/WebViewProvider.swift b/Multiplatform/Shared/Article/WebViewProvider.swift index 0eff3cd4f..64d834c0c 100644 --- a/Multiplatform/Shared/Article/WebViewProvider.swift +++ b/Multiplatform/Shared/Article/WebViewProvider.swift @@ -86,9 +86,7 @@ class WebViewProviderDequeueOperation: MainThreadOperation { func run() { if let webView = queue.lastObject as? PreloadedWebView { - webView.ready { preloadedWebView in - self.completion(preloadedWebView) - } + self.completion(webView) self.queue.remove(webView) self.operationDelegate?.operationDidComplete(self) return @@ -98,9 +96,7 @@ class WebViewProviderDequeueOperation: MainThreadOperation { let webView = PreloadedWebView(articleIconSchemeHandler: articleIconSchemeHandler) webView.preload() - webView.ready { preloadedWebView in - self.completion(preloadedWebView) - } + self.completion(webView) self.operationDelegate?.operationDidComplete(self) } diff --git a/Multiplatform/iOS/Article/WebViewController.swift b/Multiplatform/iOS/Article/WebViewController.swift index 1630124bd..151084f75 100644 --- a/Multiplatform/iOS/Article/WebViewController.swift +++ b/Multiplatform/iOS/Article/WebViewController.swift @@ -452,37 +452,41 @@ private extension WebViewController { sceneModel?.webViewProvider?.dequeueWebView() { webView in - // Add the webview - webView.translatesAutoresizingMaskIntoConstraints = false - self.view.insertSubview(webView, at: 0) - NSLayoutConstraint.activate([ - self.view.leadingAnchor.constraint(equalTo: webView.leadingAnchor), - self.view.trailingAnchor.constraint(equalTo: webView.trailingAnchor), - self.view.topAnchor.constraint(equalTo: webView.topAnchor), - self.view.bottomAnchor.constraint(equalTo: webView.bottomAnchor) - ]) - - // UISplitViewController reports the wrong size to WKWebView which can cause horizontal - // rubberbanding on the iPad. This interferes with our UIPageViewController preventing - // us from easily swiping between WKWebViews. This hack fixes that. - webView.scrollView.contentInset = UIEdgeInsets(top: 0, left: -1, bottom: 0, right: 0) + webView.ready { + + // Add the webview + webView.translatesAutoresizingMaskIntoConstraints = false + self.view.insertSubview(webView, at: 0) + NSLayoutConstraint.activate([ + self.view.leadingAnchor.constraint(equalTo: webView.leadingAnchor), + self.view.trailingAnchor.constraint(equalTo: webView.trailingAnchor), + self.view.topAnchor.constraint(equalTo: webView.topAnchor), + self.view.bottomAnchor.constraint(equalTo: webView.bottomAnchor) + ]) + + // UISplitViewController reports the wrong size to WKWebView which can cause horizontal + // rubberbanding on the iPad. This interferes with our UIPageViewController preventing + // us from easily swiping between WKWebViews. This hack fixes that. + webView.scrollView.contentInset = UIEdgeInsets(top: 0, left: -1, bottom: 0, right: 0) - webView.scrollView.setZoomScale(1.0, animated: false) + webView.scrollView.setZoomScale(1.0, animated: false) - self.view.setNeedsLayout() - self.view.layoutIfNeeded() + self.view.setNeedsLayout() + self.view.layoutIfNeeded() - // Configure the webview - webView.navigationDelegate = self - webView.uiDelegate = self - webView.scrollView.delegate = self -// self.configureContextMenuInteraction() + // Configure the webview + webView.navigationDelegate = self + webView.uiDelegate = self + webView.scrollView.delegate = self + // self.configureContextMenuInteraction() - webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked) - webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown) - webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.showFeedInspector) + webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked) + webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown) + webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.showFeedInspector) - self.renderPage(webView) + self.renderPage(webView) + + } } diff --git a/Multiplatform/macOS/Article/WebViewController.swift b/Multiplatform/macOS/Article/WebViewController.swift index e37ab4b8b..c0ebfc927 100644 --- a/Multiplatform/macOS/Article/WebViewController.swift +++ b/Multiplatform/macOS/Article/WebViewController.swift @@ -246,28 +246,31 @@ private extension WebViewController { sceneModel?.webViewProvider?.dequeueWebView() { webView in - // Add the webview - self.webView = webView + webView.ready { + + // Add the webview + self.webView = webView + + webView.translatesAutoresizingMaskIntoConstraints = false + self.view.addSubview(webView, positioned: .below, relativeTo: self.statusBarView) + NSLayoutConstraint.activate([ + self.view.leadingAnchor.constraint(equalTo: webView.leadingAnchor), + self.view.trailingAnchor.constraint(equalTo: webView.trailingAnchor), + self.view.topAnchor.constraint(equalTo: webView.topAnchor), + self.view.bottomAnchor.constraint(equalTo: webView.bottomAnchor) + ]) + + webView.navigationDelegate = self - webView.translatesAutoresizingMaskIntoConstraints = false - self.view.addSubview(webView, positioned: .below, relativeTo: self.statusBarView) - NSLayoutConstraint.activate([ - self.view.leadingAnchor.constraint(equalTo: webView.leadingAnchor), - self.view.trailingAnchor.constraint(equalTo: webView.trailingAnchor), - self.view.topAnchor.constraint(equalTo: webView.topAnchor), - self.view.bottomAnchor.constraint(equalTo: webView.bottomAnchor) - ]) - - webView.navigationDelegate = self - - webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked) - webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown) - webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.mouseDidEnter) - webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.mouseDidExit) - webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.showFeedInspector) + webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked) + webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown) + webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.mouseDidEnter) + webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.mouseDidExit) + webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.showFeedInspector) - self.renderPage(webView) + self.renderPage(webView) + } } } diff --git a/iOS/Article/PreloadedWebView.swift b/iOS/Article/PreloadedWebView.swift index c5e0c2c90..3bf76a2ca 100644 --- a/iOS/Article/PreloadedWebView.swift +++ b/iOS/Article/PreloadedWebView.swift @@ -12,7 +12,7 @@ import WebKit class PreloadedWebView: WKWebView { private var isReady: Bool = false - private var readyCompletion: ((PreloadedWebView) -> Void)? + private var readyCompletion: (() -> Void)? init(articleIconSchemeHandler: ArticleIconSchemeHandler) { let preferences = WKPreferences() @@ -38,7 +38,7 @@ class PreloadedWebView: WKWebView { loadFileURL(ArticleRenderer.blank.url, allowingReadAccessTo: ArticleRenderer.blank.baseURL) } - func ready(completion: @escaping (PreloadedWebView) -> Void) { + func ready(completion: @escaping () -> Void) { if isReady { completeRequest(completion: completion) } else { @@ -66,10 +66,10 @@ extension PreloadedWebView: WKNavigationDelegate { private extension PreloadedWebView { - func completeRequest(completion: @escaping (PreloadedWebView) -> Void) { + func completeRequest(completion: @escaping () -> Void) { isReady = false navigationDelegate = nil - completion(self) + completion() } } diff --git a/iOS/Article/WebViewController.swift b/iOS/Article/WebViewController.swift index c6bd80fbf..ca3c0ff8e 100644 --- a/iOS/Article/WebViewController.swift +++ b/iOS/Article/WebViewController.swift @@ -483,37 +483,41 @@ private extension WebViewController { coordinator.webViewProvider.dequeueWebView() { webView in - // Add the webview - webView.translatesAutoresizingMaskIntoConstraints = false - self.view.insertSubview(webView, at: 0) - NSLayoutConstraint.activate([ - self.view.leadingAnchor.constraint(equalTo: webView.leadingAnchor), - self.view.trailingAnchor.constraint(equalTo: webView.trailingAnchor), - self.view.topAnchor.constraint(equalTo: webView.topAnchor), - self.view.bottomAnchor.constraint(equalTo: webView.bottomAnchor) - ]) - - // UISplitViewController reports the wrong size to WKWebView which can cause horizontal - // rubberbanding on the iPad. This interferes with our UIPageViewController preventing - // us from easily swiping between WKWebViews. This hack fixes that. - webView.scrollView.contentInset = UIEdgeInsets(top: 0, left: -1, bottom: 0, right: 0) + webView.ready { + + // Add the webview + webView.translatesAutoresizingMaskIntoConstraints = false + self.view.insertSubview(webView, at: 0) + NSLayoutConstraint.activate([ + self.view.leadingAnchor.constraint(equalTo: webView.leadingAnchor), + self.view.trailingAnchor.constraint(equalTo: webView.trailingAnchor), + self.view.topAnchor.constraint(equalTo: webView.topAnchor), + self.view.bottomAnchor.constraint(equalTo: webView.bottomAnchor) + ]) + + // UISplitViewController reports the wrong size to WKWebView which can cause horizontal + // rubberbanding on the iPad. This interferes with our UIPageViewController preventing + // us from easily swiping between WKWebViews. This hack fixes that. + webView.scrollView.contentInset = UIEdgeInsets(top: 0, left: -1, bottom: 0, right: 0) - webView.scrollView.setZoomScale(1.0, animated: false) + webView.scrollView.setZoomScale(1.0, animated: false) - self.view.setNeedsLayout() - self.view.layoutIfNeeded() + self.view.setNeedsLayout() + self.view.layoutIfNeeded() - // Configure the webview - webView.navigationDelegate = self - webView.uiDelegate = self - webView.scrollView.delegate = self - self.configureContextMenuInteraction() + // Configure the webview + webView.navigationDelegate = self + webView.uiDelegate = self + webView.scrollView.delegate = self + self.configureContextMenuInteraction() - webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked) - webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown) - webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.showFeedInspector) + webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked) + webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown) + webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.showFeedInspector) - self.renderPage(webView) + self.renderPage(webView) + + } } diff --git a/iOS/Article/WebViewProvider.swift b/iOS/Article/WebViewProvider.swift index 50c6e2780..4c49d3ed5 100644 --- a/iOS/Article/WebViewProvider.swift +++ b/iOS/Article/WebViewProvider.swift @@ -86,9 +86,7 @@ class WebViewProviderDequeueOperation: MainThreadOperation { func run() { if let webView = queue.lastObject as? PreloadedWebView { - webView.ready { preloadedWebView in - self.completion(preloadedWebView) - } + self.completion(webView) self.queue.remove(webView) self.operationDelegate?.operationDidComplete(self) return @@ -98,9 +96,7 @@ class WebViewProviderDequeueOperation: MainThreadOperation { let webView = PreloadedWebView(articleIconSchemeHandler: articleIconSchemeHandler) webView.preload() - webView.ready { preloadedWebView in - self.completion(preloadedWebView) - } + self.completion(webView) self.operationDelegate?.operationDidComplete(self) }