parent
4789110253
commit
43d9ae7495
|
|
@ -254,7 +254,7 @@
|
|||
attributes = {
|
||||
LastSwiftUpdateCheck = 0830;
|
||||
LastUpgradeCheck = 0820;
|
||||
ORGANIZATIONNAME = CocoaPods;
|
||||
ORGANIZATIONNAME = ReCaptcha;
|
||||
TargetAttributes = {
|
||||
607FACCF1AFB9204008FA782 = {
|
||||
CreatedOnToolsVersion = 6.3.1;
|
||||
|
|
|
|||
|
|
@ -198,4 +198,50 @@ class ReCaptchaWebViewManager__Tests: XCTestCase {
|
|||
|
||||
waitForExpectations(timeout: 3)
|
||||
}
|
||||
|
||||
// MARK: Setup
|
||||
|
||||
func test__Key_Setup() {
|
||||
let exp = expectation(description: "setup key")
|
||||
var result: ReCaptchaWebViewManager.Response?
|
||||
|
||||
// Validate
|
||||
let manager = ReCaptchaWebViewManager(messageBody: "{token: key}", apiKey: apiKey)
|
||||
manager.configureWebView { _ in
|
||||
XCTFail("should not ask to configure the webview")
|
||||
}
|
||||
|
||||
manager.validate(on: presenterView) { response in
|
||||
result = response
|
||||
exp.fulfill()
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 3)
|
||||
|
||||
XCTAssertNotNil(result)
|
||||
XCTAssertNil(result?.error)
|
||||
XCTAssertEqual(result?.value, apiKey)
|
||||
}
|
||||
|
||||
func test__Endpoint_Setup() {
|
||||
let exp = expectation(description: "setup endpoint")
|
||||
let endpoint = String(describing: arc4random())
|
||||
var result: ReCaptchaWebViewManager.Response?
|
||||
|
||||
let manager = ReCaptchaWebViewManager(messageBody: "{token: endpoint}", endpoint: endpoint)
|
||||
manager.configureWebView { _ in
|
||||
XCTFail("should not ask to configure the webview")
|
||||
}
|
||||
|
||||
manager.validate(on: presenterView) { response in
|
||||
result = response
|
||||
exp.fulfill()
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 3)
|
||||
|
||||
XCTAssertNotNil(result)
|
||||
XCTAssertNil(result?.error)
|
||||
XCTAssertEqual(result?.value, endpoint)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,18 @@ import Foundation
|
|||
|
||||
|
||||
extension ReCaptchaWebViewManager {
|
||||
convenience init(messageBody: String, apiKey: String? = nil) {
|
||||
convenience init(messageBody: String, apiKey: String? = nil, endpoint: String? = nil) {
|
||||
let localhost = URL(string: "http://localhost")!
|
||||
let html = Bundle(for: ReCaptchaWebViewManager__Tests.self)
|
||||
.path(forResource: "mock", ofType: "html")
|
||||
.flatMap { try? String(contentsOfFile: $0) }
|
||||
.map { String(format: $0, "%@", messageBody) }
|
||||
.map { String(format: $0, arguments: ["message": messageBody]) }
|
||||
|
||||
self.init(html: html!, apiKey: apiKey ?? String(arc4random()), baseURL: URL(string: "http://localhost")!)
|
||||
self.init(
|
||||
html: html!,
|
||||
apiKey: apiKey ?? String(arc4random()),
|
||||
baseURL: localhost,
|
||||
endpoint: endpoint ?? localhost.absoluteString
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<script type="text/javascript">
|
||||
var key = "%@";
|
||||
var key = "${apiKey}";
|
||||
var execute = function() {
|
||||
window.webkit.messageHandlers.recaptcha.postMessage(%@);
|
||||
window.webkit.messageHandlers.recaptcha.postMessage(${message});
|
||||
}
|
||||
var endpoint = "${endpoint}";
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
13
README.md
13
README.md
|
|
@ -63,6 +63,19 @@ recaptcha.rx.validate(on: view)
|
|||
})
|
||||
```
|
||||
|
||||
#### Alternte endpoint
|
||||
|
||||
If your app has firewall limitations that may be blocking Google's API, the JS endpoint may be changed on initialization.
|
||||
It'll then point to `https://www.recaptcha.net/recaptcha/api.js`:
|
||||
|
||||
``` swift
|
||||
public enum Endpoint {
|
||||
case default, alternate
|
||||
}
|
||||
|
||||
let recaptcha = try? ReCaptcha(endpoint: .alternate) // Defaults to `default` when unset
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
ReCaptcha is available under the MIT license. See the LICENSE file for more info.
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
var onloadCallback = function() {
|
||||
grecaptcha.render('submit', {
|
||||
'sitekey' : '%@',
|
||||
'sitekey' : '${apiKey}',
|
||||
'callback' : onSubmit,
|
||||
'size': 'invisible'
|
||||
});
|
||||
|
|
@ -35,6 +35,6 @@
|
|||
</head>
|
||||
<body>
|
||||
<span id="submit" style="visibility: hidden;"></span>
|
||||
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>
|
||||
<script src="${endpoint}" async defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// NSError+ReCaptcha.swift
|
||||
// Pods
|
||||
// ReCaptcha
|
||||
//
|
||||
// Created by Flávio Caetano on 22/03/17.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// ReCaptcha.swift
|
||||
// Pods
|
||||
// ReCaptcha
|
||||
//
|
||||
// Created by Flávio Caetano on 22/03/17.
|
||||
//
|
||||
|
|
@ -20,6 +20,22 @@ open class ReCaptcha: ReCaptchaWebViewManager {
|
|||
}
|
||||
}
|
||||
|
||||
/** The JS API endpoint to be loaded onto the HTML file.
|
||||
|
||||
- default: Google's default endpoint. Points to https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit
|
||||
- alternate: Alternate endpoint. Points to https://www.recaptcha.net/recaptcha/api.js
|
||||
*/
|
||||
public enum Endpoint {
|
||||
case `default`, alternate
|
||||
|
||||
fileprivate var url: String {
|
||||
switch self {
|
||||
case .default: return "https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit"
|
||||
case .alternate: return "https://www.recaptcha.net/recaptcha/api.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Internal data model for DI in unit tests
|
||||
*/
|
||||
struct Config {
|
||||
|
|
@ -73,8 +89,9 @@ open class ReCaptcha: ReCaptchaWebViewManager {
|
|||
|
||||
A key may be aquired here: https://www.google.com/recaptcha/admin#list
|
||||
|
||||
- parameter apiKey: The API key to be provided to Google's ReCaptcha. Overrides the Info.plist entry.
|
||||
- parameter baseURL: A url domain to be load onto the webview. Overrides the Info.plist entry.
|
||||
- parameter apiKey: The API key to be provided to Google's ReCaptcha. Overrides the Info.plist entry. Defaults to `nil`.
|
||||
- parameter baseURL: A url domain to be load onto the webview. Overrides the Info.plist entry. Defaults to `nil`.
|
||||
- parameter endpoint: The JS API endpoint to be loaded onto the HTML file. Defaults to `.default`.
|
||||
|
||||
- Throws:
|
||||
- `NSError.ReCaptchaCode.htmlLoadError` if is unable to load the HTML embedded in the bundle.
|
||||
|
|
@ -82,13 +99,13 @@ open class ReCaptcha: ReCaptchaWebViewManager {
|
|||
- `NSError.ReCaptchaCode.baseURLNotFound` if a `baseURL` is not provided and can't find one in the project's Info.plist.
|
||||
- Rethrows any exceptions thrown by `String(contentsOfFile:)`
|
||||
*/
|
||||
public init(apiKey: String? = nil, baseURL: URL? = nil) throws {
|
||||
public init(apiKey: String? = nil, baseURL: URL? = nil, endpoint: Endpoint = .default) throws {
|
||||
let infoDict = Bundle.main.infoDictionary
|
||||
|
||||
let plistApiKey = infoDict?[Constants.InfoDictKeys.APIKey] as? String
|
||||
let plistDomain = (infoDict?[Constants.InfoDictKeys.Domain] as? String).flatMap(URL.init(string:))
|
||||
|
||||
let config = try Config(apiKey: apiKey, infoPlistKey: plistApiKey, baseURL: baseURL, infoPlistURL: plistDomain)
|
||||
super.init(html: config.html, apiKey: config.apiKey, baseURL: config.baseURL)
|
||||
super.init(html: config.html, apiKey: config.apiKey, baseURL: config.baseURL, endpoint: endpoint.url)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// ReCaptchaDecoder.swift
|
||||
// Pods
|
||||
// ReCaptcha
|
||||
//
|
||||
// Created by Flávio Caetano on 22/03/17.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// ReCaptchaWebViewManager.swift
|
||||
// Pods
|
||||
// ReCaptcha
|
||||
//
|
||||
// Created by Flávio Caetano on 22/03/17.
|
||||
//
|
||||
|
|
@ -39,15 +39,16 @@ open class ReCaptchaWebViewManager: NSObject {
|
|||
- html: The HTML string to be loaded onto the webview
|
||||
- apiKey: The Google's ReCaptcha API Key
|
||||
- baseURL: The URL configured with the API Key
|
||||
- endpoint: The JS API endpoint to be loaded onto the HTML file.
|
||||
*/
|
||||
init(html: String, apiKey: String, baseURL: URL) {
|
||||
init(html: String, apiKey: String, baseURL: URL, endpoint: String) {
|
||||
super.init()
|
||||
|
||||
decoder = ReCaptchaDecoder { [weak self] result in
|
||||
self?.handle(result: result)
|
||||
}
|
||||
|
||||
let formattedHTML = String(format: html, apiKey)
|
||||
let formattedHTML = String(format: html, arguments: ["apiKey": apiKey, "endpoint": endpoint])
|
||||
|
||||
if let window = UIApplication.shared.keyWindow {
|
||||
setupWebview(on: window, html: formattedHTML, url: baseURL)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// ReCaptcha+Rx.swift
|
||||
// Pods
|
||||
// ReCaptcha
|
||||
//
|
||||
// Created by Flávio Caetano on 11/04/17.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// String+Dict.swift
|
||||
// ReCaptcha
|
||||
//
|
||||
// Created by Flávio Caetano on 10/10/17.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
extension String {
|
||||
init(format: String, arguments: [String: CustomStringConvertible]) {
|
||||
self.init(describing: arguments.reduce(format)
|
||||
{ (format: String, args: (key: String, value: CustomStringConvertible)) -> String in
|
||||
format.replacingOccurrences(of: "${\(args.key)}", with: args.value.description)
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue