From 71f20d9acd3a8a561cb9a86a55c920032385824b Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Wed, 6 May 2020 16:35:53 -0700 Subject: [PATCH 1/2] Bump build to 45. --- xcconfig/common/NetNewsWire_ios_target_common.xcconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig index e34f4fec5..be0a9ff23 100644 --- a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig +++ b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig @@ -1,7 +1,7 @@ // High Level Settings common to both the iOS application and any extensions we bundle with it MARKETING_VERSION = 5.0.1 -CURRENT_PROJECT_VERSION = 44 +CURRENT_PROJECT_VERSION = 45 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon From 46a96a7d50d773af1286fcaf67bbc3ae4da36871 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Thu, 7 May 2020 11:32:11 -0500 Subject: [PATCH 2/2] Serialize access to the web view provider. Issue #2043 --- iOS/Article/WebViewProvider.swift | 111 ++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 22 deletions(-) diff --git a/iOS/Article/WebViewProvider.swift b/iOS/Article/WebViewProvider.swift index 9d05473a9..41c0e550c 100644 --- a/iOS/Article/WebViewProvider.swift +++ b/iOS/Article/WebViewProvider.swift @@ -7,42 +7,116 @@ // import Foundation +import RSCore import WebKit /// WKWebView has an awful behavior of a flash to white on first load when in dark mode. /// Keep a queue of WebViews where we've already done a trivial load so that by the time we need them in the UI, they're past the flash-to-shite part of their lifecycle. class WebViewProvider: NSObject { - let articleIconSchemeHandler: ArticleIconSchemeHandler - - private let minimumQueueDepth = 3 - private let maximumQueueDepth = 6 + private let articleIconSchemeHandler: ArticleIconSchemeHandler + private let operationQueue = MainThreadOperationQueue() private var queue = UIView() init(coordinator: SceneCoordinator, viewController: UIViewController) { articleIconSchemeHandler = ArticleIconSchemeHandler(coordinator: coordinator) super.init() viewController.view.insertSubview(queue, at: 0) - replenishQueueIfNeeded() } func flushQueue() { - queue.subviews.forEach { $0.removeFromSuperview() } + operationQueue.add(WebViewProviderFlushQueueOperation(queue: queue)) } func replenishQueueIfNeeded() { - while queue.subviews.count < minimumQueueDepth { - enqueueWebView(PreloadedWebView(articleIconSchemeHandler: articleIconSchemeHandler)) - } + operationQueue.add(WebViewProviderReplenishQueueOperation(queue: queue, articleIconSchemeHandler: articleIconSchemeHandler)) } func dequeueWebView(completion: @escaping (PreloadedWebView) -> ()) { + operationQueue.add(WebViewProviderDequeueOperation(queue: queue, articleIconSchemeHandler: articleIconSchemeHandler, completion: completion)) + operationQueue.add(WebViewProviderReplenishQueueOperation(queue: queue, articleIconSchemeHandler: articleIconSchemeHandler)) + } + +} + +class WebViewProviderFlushQueueOperation: MainThreadOperation { + + // MainThreadOperation + public var isCanceled = false + public var id: Int? + public weak var operationDelegate: MainThreadOperationDelegate? + public var name: String? = "WebViewProviderFlushQueueOperation" + public var completionBlock: MainThreadOperation.MainThreadOperationCompletionBlock? + + private var queue: UIView + + init(queue: UIView) { + self.queue = queue + } + + func run() { + queue.subviews.forEach { $0.removeFromSuperview() } + self.operationDelegate?.operationDidComplete(self) + } + +} + +class WebViewProviderReplenishQueueOperation: MainThreadOperation { + + // MainThreadOperation + public var isCanceled = false + public var id: Int? + public weak var operationDelegate: MainThreadOperationDelegate? + public var name: String? = "WebViewProviderReplenishQueueOperation" + public var completionBlock: MainThreadOperation.MainThreadOperationCompletionBlock? + + private let minimumQueueDepth = 3 + + private var queue: UIView + private var articleIconSchemeHandler: ArticleIconSchemeHandler + + init(queue: UIView, articleIconSchemeHandler: ArticleIconSchemeHandler) { + self.queue = queue + self.articleIconSchemeHandler = articleIconSchemeHandler + } + + func run() { + while queue.subviews.count < minimumQueueDepth { + let webView = PreloadedWebView(articleIconSchemeHandler: articleIconSchemeHandler) + queue.insertSubview(webView, at: 0) + webView.preload() + } + self.operationDelegate?.operationDidComplete(self) + } + +} + +class WebViewProviderDequeueOperation: MainThreadOperation { + + // MainThreadOperation + public var isCanceled = false + public var id: Int? + public weak var operationDelegate: MainThreadOperationDelegate? + public var name: String? = "WebViewProviderFlushQueueOperation" + public var completionBlock: MainThreadOperation.MainThreadOperationCompletionBlock? + + private var queue: UIView + private var articleIconSchemeHandler: ArticleIconSchemeHandler + private var completion: (PreloadedWebView) -> () + + init(queue: UIView, articleIconSchemeHandler: ArticleIconSchemeHandler, completion: @escaping (PreloadedWebView) -> ()) { + self.queue = queue + self.articleIconSchemeHandler = articleIconSchemeHandler + self.completion = completion + } + + func run() { if let webView = queue.subviews.last as? PreloadedWebView { webView.ready { preloadedWebView in preloadedWebView.removeFromSuperview() - self.replenishQueueIfNeeded() - completion(preloadedWebView) + self.completion(preloadedWebView) + self.operationDelegate?.operationDidComplete(self) } return } @@ -50,18 +124,11 @@ class WebViewProvider: NSObject { assertionFailure("Creating PreloadedWebView in \(#function); queue has run dry.") let webView = PreloadedWebView(articleIconSchemeHandler: articleIconSchemeHandler) - webView.ready { preloadedWebView in - self.replenishQueueIfNeeded() - completion(preloadedWebView) - } - } - - func enqueueWebView(_ webView: PreloadedWebView) { - guard queue.subviews.count < maximumQueueDepth else { - return - } - queue.insertSubview(webView, at: 0) webView.preload() + webView.ready { preloadedWebView in + self.completion(preloadedWebView) + self.operationDelegate?.operationDidComplete(self) + } } }