Skip to content

Commit

Permalink
Fix: multiple configure calls after app being idle for 10 mins
Browse files Browse the repository at this point in the history
  • Loading branch information
fjcaetano committed Sep 6, 2018
1 parent 1c3af34 commit 66b70da
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 2 deletions.
43 changes: 43 additions & 0 deletions Example/ReCaptcha_Tests/Core/DispatchQueue__Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
28 changes: 28 additions & 0 deletions Example/ReCaptcha_Tests/Core/ReCaptchaWebViewManager__Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
20 changes: 20 additions & 0 deletions ReCaptcha/Classes/DispatchQueue+Throttle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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()
}
}
8 changes: 6 additions & 2 deletions ReCaptcha/Classes/ReCaptchaWebViewManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -152,6 +155,7 @@ internal class ReCaptchaWebViewManager {
webview.accessibilityIdentifier = "webview"
webview.accessibilityTraits = UIAccessibilityTraitLink
webview.isHidden = true
print("HIDDEN")

return webview
}()
Expand Down Expand Up @@ -211,6 +215,7 @@ internal class ReCaptchaWebViewManager {
*/
func reset() {
didFinishLoading = false
configureWebViewDispatchToken = UUID()
webviewDelegate.reset()

webView.evaluateJavaScript(Constants.ResetCommand) { [weak self] _, error in
Expand Down Expand Up @@ -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)
}
Expand Down

0 comments on commit 66b70da

Please sign in to comment.