Documentation
This commit is contained in:
parent
20f68d61f7
commit
f34cb7fbb5
|
|
@ -36,13 +36,6 @@ xcuserdata
|
|||
.DS_Store
|
||||
|
||||
## CocoaPods
|
||||
**/Pods/
|
||||
Pods/
|
||||
|
||||
html/highwinds_credentials.json
|
||||
html/node_modules/
|
||||
html/fb-*.html
|
||||
html/ios-*.html
|
||||
html/mp4-*.html
|
||||
html/player-inject-*.js
|
||||
|
||||
.bitrise*
|
||||
docs/
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -1,6 +1,6 @@
|
|||
# ReCaptcha
|
||||
|
||||
[](https://travis-ci.org/fjcaetano/ReCaptcha)
|
||||
# [](https://travis-ci.org/fjcaetano/ReCaptcha)
|
||||
[](http://cocoapods.org/pods/ReCaptcha)
|
||||
[](http://cocoapods.org/pods/ReCaptcha)
|
||||
[](http://cocoapods.org/pods/ReCaptcha)
|
||||
|
|
@ -25,11 +25,13 @@ pod "ReCaptcha"
|
|||
Simply add `ReCaptchaKey` and `ReCaptchaDomain` to your Info.plist and run:
|
||||
|
||||
``` swift
|
||||
let recaptcha = try? ReCaptcha()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
recaptcha.presenterView = view
|
||||
recaptcha.configureWebView { [weak self] webview in
|
||||
recaptcha?.presenterView = view
|
||||
recaptcha?.configureWebView { [weak self] webview in
|
||||
webview.frame = self?.view.bounds ?? CGRect.zero
|
||||
webview.tag = ViewController.webViewTag
|
||||
}
|
||||
|
|
@ -37,7 +39,7 @@ override func viewDidLoad() {
|
|||
|
||||
|
||||
func validate() {
|
||||
recaptcha.validate { [weak self] result in
|
||||
recaptcha?.validate { [weak self] result in
|
||||
print(try? result.dematerialize())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,8 @@ Add Google invisible ReCaptcha to your app
|
|||
DESC
|
||||
|
||||
s.homepage = 'https://github.com/fjcaetano/ReCaptcha'
|
||||
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'fjcaetano' => 'flavio@vieiracaetano.com' }
|
||||
s.author = { 'Flávio Caetano' => 'flavio@vieiracaetano.com' }
|
||||
s.source = { :git => 'https://github.com/fjcaetano/ReCaptcha.git', :tag => s.version.to_s }
|
||||
s.social_media_url = 'https://twitter.com/flavio_caetano'
|
||||
|
||||
|
|
|
|||
|
|
@ -11,24 +11,47 @@ import Foundation
|
|||
/// The domain for ReCaptcha's errors
|
||||
fileprivate let kErrorDomain = "com.flaviocaetano.ReCaptcha"
|
||||
|
||||
/**
|
||||
/** Adds enum codes to ReCaptcha's errors
|
||||
*/
|
||||
extension NSError {
|
||||
enum Code: Int {
|
||||
|
||||
/** The codes of possible errors thrown by ReCaptcha
|
||||
|
||||
- undefined: Any unexpected errors
|
||||
- htmlLoadError: Could not load the HTML embedded in the bundle
|
||||
- apiKeyNotFound: ReCaptchaKey was not provided
|
||||
- baseURLNotFound: ReCaptchaDomain was not provided
|
||||
- wrongMessageFormat: Received an unexpeted message from javascript
|
||||
*/
|
||||
enum ReCaptchaCode: Int {
|
||||
/// Unexpected error
|
||||
case undefined
|
||||
|
||||
/// Could not load the HTML embedded in the bundle
|
||||
case htmlLoadError
|
||||
|
||||
/// ReCaptchaKey was not provided
|
||||
case apiKeyNotFound
|
||||
|
||||
/// ReCaptchaDomain was not provided
|
||||
case baseURLNotFound
|
||||
|
||||
/// Received an unexpeted message from javascript
|
||||
case wrongMessageFormat
|
||||
}
|
||||
|
||||
|
||||
var rc_code: Code? {
|
||||
return Code(rawValue: code)
|
||||
/// The error ReCaptchaCode
|
||||
var rc_code: ReCaptchaCode? {
|
||||
return ReCaptchaCode(rawValue: code)
|
||||
}
|
||||
|
||||
|
||||
convenience init(code: Code, userInfo: [AnyHashable: Any]? = nil) {
|
||||
/** Initializes the error with a Code and an userInfo
|
||||
- parameter code: A ReCaptchaCode
|
||||
- parameter userInfo: The error's userInfo
|
||||
*/
|
||||
convenience init(code: ReCaptchaCode, userInfo: [AnyHashable: Any]? = nil) {
|
||||
self.init(domain: kErrorDomain, code: code.rawValue, userInfo: userInfo)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import Foundation
|
|||
import WebKit
|
||||
|
||||
|
||||
/** The public facade of ReCaptcha
|
||||
*/
|
||||
open class ReCaptcha: ReCaptchaWebViewManager {
|
||||
fileprivate struct Constants {
|
||||
struct InfoDictKeys {
|
||||
|
|
@ -18,6 +20,22 @@ open class ReCaptcha: ReCaptchaWebViewManager {
|
|||
}
|
||||
}
|
||||
|
||||
/** Initializes a ReCaptcha object
|
||||
|
||||
Both `apiKey` and `baseURL` may be nil, in which case the lib will look for entries of `ReCaptchaKey` and
|
||||
`ReCaptchaDomain`, respectively, in the project's Info.plist
|
||||
|
||||
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.
|
||||
|
||||
- Throws:
|
||||
- `NSError.ReCaptchaCode.htmlLoadError` if is unable to load the HTML embedded in the bundle.
|
||||
- `NSError.ReCaptchaCode.apiKeyNotFound` if an `apiKey` is not provided and can't find one in the project's Info.plist.
|
||||
- `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 {
|
||||
guard let filePath = Bundle(for: ReCaptcha.self).path(forResource: "recaptcha", ofType: "html") else {
|
||||
throw NSError(code: .htmlLoadError)
|
||||
|
|
|
|||
|
|
@ -10,15 +10,31 @@ import Foundation
|
|||
import WebKit
|
||||
|
||||
|
||||
/** The Decoder of javascript messages from the webview
|
||||
*/
|
||||
class ReCaptchaDecoder: NSObject {
|
||||
/** The decoder result.
|
||||
|
||||
- token(String): A result token, if any
|
||||
- showReCaptcha: Indicates that the webview containing the challenge should be displayed.
|
||||
- error(NSError): Any errors
|
||||
*/
|
||||
enum Result {
|
||||
/// A result token, if any
|
||||
case token(String)
|
||||
|
||||
/// Indicates that the webview containing the challenge should be displayed.
|
||||
case showReCaptcha
|
||||
|
||||
/// Any errors
|
||||
case error(NSError)
|
||||
}
|
||||
|
||||
fileprivate let sendMessage: ((Result) -> Void)
|
||||
|
||||
/** Initializes a decoder with a completion closure.
|
||||
- parameter didReceiveMessage: A closure that receives a ReCaptchaDecoder.Result
|
||||
*/
|
||||
init(didReceiveMessage: @escaping (Result) -> Void) {
|
||||
sendMessage = didReceiveMessage
|
||||
|
||||
|
|
@ -26,6 +42,9 @@ class ReCaptchaDecoder: NSObject {
|
|||
}
|
||||
|
||||
|
||||
/** Sends an error to the completion closure
|
||||
- parameter error: The error to be sent.
|
||||
*/
|
||||
func send(error: NSError) {
|
||||
sendMessage(.error(error))
|
||||
}
|
||||
|
|
@ -33,6 +52,9 @@ class ReCaptchaDecoder: NSObject {
|
|||
|
||||
|
||||
// MARK: Script Handler
|
||||
|
||||
/** Makes ReCaptchaDecoder conform to `WKScriptMessageHandler`
|
||||
*/
|
||||
extension ReCaptchaDecoder: WKScriptMessageHandler {
|
||||
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||
guard let dict = message.body as? [String: Any] else {
|
||||
|
|
@ -45,7 +67,15 @@ extension ReCaptchaDecoder: WKScriptMessageHandler {
|
|||
|
||||
|
||||
// MARK: - Result
|
||||
|
||||
/** Private methods on `ReCaptchaDecoder.Result`
|
||||
*/
|
||||
fileprivate extension ReCaptchaDecoder.Result {
|
||||
|
||||
/** Parses a dict received from the webview onto a `ReCaptchaDecoder.Result`
|
||||
- parameter response: A dictionary containing the message to be parsed
|
||||
- returns: A decoded ReCaptchaDecoder.Result
|
||||
*/
|
||||
static func from(response: [String: Any]) -> ReCaptchaDecoder.Result {
|
||||
if let token = response["token"] as? String {
|
||||
return .token(token)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import WebKit
|
|||
import Result
|
||||
|
||||
|
||||
/** Handles comunications with the webview containing the ReCaptcha challenge.
|
||||
*/
|
||||
open class ReCaptchaWebViewManager: NSObject {
|
||||
public typealias Response = Result<String, NSError>
|
||||
|
||||
|
|
@ -18,6 +20,7 @@ open class ReCaptchaWebViewManager: NSObject {
|
|||
static let ExecuteJSCommand = "execute();"
|
||||
}
|
||||
|
||||
/// The view in which the webview may be presented.
|
||||
open weak var presenterView: UIView?
|
||||
|
||||
|
||||
|
|
@ -34,6 +37,12 @@ open class ReCaptchaWebViewManager: NSObject {
|
|||
}()
|
||||
|
||||
|
||||
/** Initializes the manager
|
||||
- parameters:
|
||||
- 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
|
||||
*/
|
||||
init(html: String, apiKey: String, baseURL: URL) {
|
||||
super.init()
|
||||
|
||||
|
|
@ -45,6 +54,10 @@ open class ReCaptchaWebViewManager: NSObject {
|
|||
}
|
||||
|
||||
|
||||
/** Starts the challenge validation
|
||||
|
||||
- parameter completion: A closure that receives a Result<String, NSError> which may contain a valid result token.
|
||||
*/
|
||||
open func validate(completion: @escaping (Response) -> Void) {
|
||||
self.completion = completion
|
||||
|
||||
|
|
@ -52,11 +65,19 @@ open class ReCaptchaWebViewManager: NSObject {
|
|||
}
|
||||
|
||||
|
||||
/// Stops the execution of the webview
|
||||
open func stop() {
|
||||
webView.stopLoading()
|
||||
}
|
||||
|
||||
|
||||
/** Provides a closure to configure the webview for presentation if necessary.
|
||||
|
||||
If presentation is required, the webview will already be a subview of `presenterView` if one is provided. Otherwise
|
||||
it might need to be added in a view currently visible.
|
||||
|
||||
- parameter configure: A closure that receives an instance of `WKWebView` for configuration.
|
||||
*/
|
||||
open func configureWebView(_ configure: @escaping (WKWebView) -> Void) {
|
||||
self.configureWebView = configure
|
||||
}
|
||||
|
|
@ -64,7 +85,15 @@ open class ReCaptchaWebViewManager: NSObject {
|
|||
|
||||
|
||||
// MARK: - Navigation
|
||||
|
||||
/** Makes ReCaptchaWebViewManager conform to `WKNavigationDelegate`
|
||||
*/
|
||||
extension ReCaptchaWebViewManager: WKNavigationDelegate {
|
||||
/** Called when the navigation is complete.
|
||||
|
||||
- parameter webView: The web view invoking the delegate method.
|
||||
- parameter navigation: The navigation object that finished.
|
||||
*/
|
||||
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
didFinishLoading = true
|
||||
|
||||
|
|
@ -81,7 +110,14 @@ extension ReCaptchaWebViewManager: WKNavigationDelegate {
|
|||
|
||||
|
||||
// MARK: - Private Methods
|
||||
|
||||
/** Private methods for ReCaptchaWebViewManager
|
||||
*/
|
||||
fileprivate extension ReCaptchaWebViewManager {
|
||||
|
||||
/** Executes the JS command that loads the ReCaptcha challenge.
|
||||
This method has no effect if the webview hasn't finished loading.
|
||||
*/
|
||||
func execute() {
|
||||
guard didFinishLoading else {
|
||||
// Hasn't finished loading the HTML yet
|
||||
|
|
@ -95,6 +131,9 @@ fileprivate extension ReCaptchaWebViewManager {
|
|||
}
|
||||
}
|
||||
|
||||
/** Creates a `WKWebViewConfiguration` to be added to the `WKWebView` instance.
|
||||
- returns: An instance of `WKWebViewConfiguration`
|
||||
*/
|
||||
func buildConfiguration() -> WKWebViewConfiguration {
|
||||
let controller = WKUserContentController()
|
||||
controller.add(decoder, name: "recaptcha")
|
||||
|
|
@ -105,6 +144,9 @@ fileprivate extension ReCaptchaWebViewManager {
|
|||
return conf
|
||||
}
|
||||
|
||||
/** Handles the decoder results received from the webview
|
||||
- Parameter result: A `ReCaptchaDecoder.Result` with the decoded message.
|
||||
*/
|
||||
func handle(result: ReCaptchaDecoder.Result) {
|
||||
switch result {
|
||||
case .token(let token):
|
||||
|
|
|
|||
|
|
@ -9,7 +9,17 @@
|
|||
import RxSwift
|
||||
|
||||
|
||||
/** Provides a public extension on ReCaptcha that makes it reactive.
|
||||
*/
|
||||
public extension Reactive where Base: ReCaptcha {
|
||||
|
||||
/** Starts the challenge validation uppon subscription.
|
||||
|
||||
The stream's element is a `Result<String, NSError>` that may contain a valid token.
|
||||
|
||||
- See:
|
||||
[ReCaptchaWebViewManager.validate](../Classes/ReCaptchaWebViewManager.html#/s:FC9ReCaptcha23ReCaptchaWebViewManager8validateFT10completionFGO6Result6ResultSSCSo7NSError_T__T_)
|
||||
*/
|
||||
public func validate() -> Observable<Base.Response> {
|
||||
return Observable<Base.Response>.create { [weak base] (observer: AnyObserver<Base.Response>) in
|
||||
base?.validate { response in
|
||||
|
|
|
|||
Loading…
Reference in New Issue