Fix: multiple configure calls after app being idle for 10 mins

#40
This commit is contained in:
Flávio Caetano 2018-07-12 13:12:37 -04:00
parent 1c3af347e4
commit 66b70da874
4 changed files with 97 additions and 2 deletions

View File

@ -151,4 +151,47 @@ class DispatchQueue__Tests: XCTestCase {
waitForExpectations(timeout: 5)
}
// MARK: Once
func test__Once__Single_Dispatch() {
let token = 3
var dispatchCount = 0
// Does dispatch the given action
DispatchQueue.once(token: token) {
dispatchCount = 1
}
XCTAssertEqual(dispatchCount, 1)
// Does not dispatch again for the same token
DispatchQueue.once(token: token) {
dispatchCount = 2
}
XCTAssertEqual(dispatchCount, 1)
}
func test__Once__Multiple_Dispatches() {
let token1 = 4
var didDispatch1 = false
// Does dispatch the given action
DispatchQueue.once(token: token1) {
didDispatch1 = true
}
XCTAssertTrue(didDispatch1)
// Dispatch for a different token
let token2 = 6
var didDispatch2 = false
DispatchQueue.once(token: token2) {
didDispatch2 = true
}
XCTAssertTrue(didDispatch2)
}
}

View File

@ -210,6 +210,34 @@ class ReCaptchaWebViewManager__Tests: XCTestCase {
XCTAssertEqual(count, 1)
}
func test__Configure_Web_View__Called_Again_With_Reset() {
let exp0 = expectation(description: "configure webview 0")
let manager = ReCaptchaWebViewManager(messageBody: "{action: \"showReCaptcha\"}")
manager.validate(on: presenterView) { _ in
XCTFail("should not call completion")
}
// Configure Webview
manager.configureWebView { _ in
manager.webView.evaluateJavaScript("execute();") { XCTAssertNil($1) }
exp0.fulfill()
}
waitForExpectations(timeout: 10)
// Reset and ensure it calls again
let exp1 = expectation(description: "configure webview 1")
manager.configureWebView { _ in
manager.webView.evaluateJavaScript("execute();") { XCTAssertNil($1) }
exp1.fulfill()
}
manager.reset()
waitForExpectations(timeout: 10)
}
// MARK: Stop
func test__Stop() {

View File

@ -16,6 +16,9 @@ extension DispatchQueue {
/// Stores the last call times for a given context
private static var lastDebounceCallTimes = [AnyHashable: DispatchTime]()
/// Dispatched actions' token storage
private static var onceTokenStorage = Set<AnyHashable>()
/// An object representing a context if none is given
private static let nilContext = UUID()
@ -61,4 +64,21 @@ extension DispatchQueue {
DispatchQueue.lastDebounceCallTimes.removeValue(forKey: context)
}
}
/**
- parameters:
- token: The control token for each dispatched action
- action: The closure to be executed
Dispatch the action only once for each given token
*/
static func once(token: AnyHashable, action: () -> Void) {
guard !onceTokenStorage.contains(token) else { return }
defer { objc_sync_exit(self) }
objc_sync_enter(self)
onceTokenStorage.insert(token)
action()
}
}

View File

@ -122,6 +122,9 @@ internal class ReCaptchaWebViewManager {
/// Configures the webview for display when required
var configureWebView: ((WKWebView) -> Void)?
/// The dispatch token used to ensure `configureWebView` is only called once.
var configureWebViewDispatchToken = UUID()
/// If the ReCaptcha should be reset when it errors
var shouldResetOnError = true
@ -152,6 +155,7 @@ internal class ReCaptchaWebViewManager {
webview.accessibilityIdentifier = "webview"
webview.accessibilityTraits = UIAccessibilityTraitLink
webview.isHidden = true
print("HIDDEN")
return webview
}()
@ -211,6 +215,7 @@ internal class ReCaptchaWebViewManager {
*/
func reset() {
didFinishLoading = false
configureWebViewDispatchToken = UUID()
webviewDelegate.reset()
webView.evaluateJavaScript(Constants.ResetCommand) { [weak self] _, error in
@ -277,8 +282,7 @@ fileprivate extension ReCaptchaWebViewManager {
}
case .showReCaptcha:
// Ensures `configureWebView` won't get called multiple times in a short period
DispatchQueue.main.debounce(interval: 1) { [weak self] in
DispatchQueue.once(token: configureWebViewDispatchToken) { [weak self] in
guard let `self` = self else { return }
self.configureWebView?(self.webView)
}