HCaptcha-ios-sdk
HCaptcha-ios-sdk copied to clipboard
HCaptcha SDK for iOS
HCaptcha
-
HCaptcha
-
Installation
- Cocoapods
- SPM
- Requirements
-
Usage
- Change widget theme
- More params for Enterprise (optional)
- Enabling the visible checkbox flow
- Using landscape instead of portrait orientation
- SDK Events
- Disable new token fetch on expiry
- Change hCaptcha frame
- Known issues
- License
- Troubleshooting
-
Installation
Add hCaptcha to your project. This library automatically handles hCaptcha's events and returns a validation token, presenting the challenge via a modal if needed.
Warning ⚠️
To secure your application, you need to send the token received here to your backend for server-side validation via the api.hcaptcha.com/siteverify
endpoint.
Installation
HCaptcha is available through CocoaPods and packaged for Carthage and SPM (Swift Package Manager).
To install it, simply add the following line to your dependencies file:
Cocoapods
pod "HCaptcha"
# or
pod "HCaptcha/RxSwift"
Carthage
github "hCaptcha/HCaptcha-ios-sdk"
Carthage will create two different frameworks named HCaptcha
and HCaptcha_RxSwift
, the latter containing the RxSwift extension for the HCaptcha framework.
Known issues:
- Carthage doesn't support prebuilt zips for
xcframework
so use--no-use-binaries
- https://github.com/Carthage/Carthage/issues/3130 - Carthage has a
RxSwift
build issue, also avoidable via--no-use-binaries
- https://github.com/Carthage/Carthage/issues/3243
SPM
Standard SPM formula: uses Package.swift
Requirements
Platform | Requirements |
---|---|
iOS | :white_check_mark: >= 9.0 |
WatchOS | :heavy_multiplication_x: |
Usage
hCaptcha sitekeys can be specified as Info.plist keys or can be passed as parameters when instantiating HCaptcha()
.
For the Info.plist configuration, add HCaptchaKey
(sitekey) and HCaptchaDomain
(with a protocol, i.e. https://) to your Info.plist.
-
HCaptchaKey
is your hCaptcha sitekey. -
HCaptchaDomain
should be a string likehttps://www.your.com
-
baseURL
should matchHCaptchaDomain
if specified; it controls the URI used to initialize the hCaptcha session. Example:https://www.your.com
With these values set, run:
let hcaptcha = try? HCaptcha()
override func viewDidLoad() {
super.viewDidLoad()
hcaptcha?.configureWebView { [weak self] webview in
webview.frame = self?.view.bounds ?? CGRect.zero
}
}
func validate() {
hcaptcha?.validate(on: view) { [weak self] (result: HCaptchaResult) in
print(try? result.dematerialize())
}
}
Note: in case you need to show hCaptcha above UIVisualEffectView
make sure to pass visualEffectView.contentView
instead visualEffectView
. Per Apple's documentation:
After you add the visual effect view to the view hierarchy, add any subviews to the contentView property of the visual effect view. Do not add subviews directly to the visual effect view itself.
More details here.
If you prefer to keep the information out of the Info.plist, you can instead use:
let hcaptcha = try? HCaptcha(
apiKey: "YOUR_HCAPTCHA_KEY",
baseURL: URL(string: "YOUR_HCAPTCHA_DOMAIN")!
)
...
Note: in most cases baseURL
can be http://localhost
You can also install the reactive subpod and use it with RxSwift:
hcaptcha.rx.validate(on: view)
.subscribe(onNext: { (token: String) in
// Do something
})
Note: caller code is responsible for hiding the WebView
after challenge processing. This is illustrated in the Example app, and can be achieved with:
-
Regular Swift API:
... var captchaWebView: WKWebView? ... hcaptcha?.configureWebView { [weak self] webview in self?.captchaWebView = webview } ... hcaptcha.validate(on: view) { result in ... captchaWebView?.removeFromSuperview() }
-
RxSwift
API (check the example for more details):... hcaptcha?.configureWebView { [weak self] webview in webview.tag = "hCaptchaViewTag" } ... let disposeBag = DisposeBag() let validate = hcaptcha.rx.validate(on: view) ... validate .map { [weak self] _ in self?.view.viewWithTag("hCaptchaViewTag") } .subscribe(onNext: { webview in webview?.removeFromSuperview() }) .disposed(by: disposeBag)
Setting the host override (optional)
Since this SDK uses a local HTML file, you may want to set a host override for better tracking and enforcement of siteverify parameters.
You can achieve this by passing the extra param host
:
let hcaptcha = try? HCaptcha(
...
host: "your-domain.com",
...
)
...
Note: this should be the bare host, i.e. not including a protocol prefix like https://.
Change widget theme
The SDK supports three built-in themes: light
, dark
, and contrast
let hcaptcha = try? HCaptcha(
...
theme: "dark", // "light" or "contrast"
...
)
...
For Enterprise sitekeys we also support custom themes via the customTheme
parameter, described below.
Alternate endpoint (optional)
If you are an Enterprise user with first-party hosting access, you can use your own endpoint (i.e. verify.your.com).
You can then enable it in your code:
let hcaptcha = try? HCaptcha(
...
endpoint: URL("https://custom.endpoint")!,
...
)
...
More params for Enterprise (optional)
Enterprise params like:
-
rqdata
(string) -
reportapi
(string) -
assethost
(string) -
imghost
(string) -
sentry
(bool) -
customTheme
(string representation of JS Object or JSON; see Enterprise docs)
Can be passed via HCaptcha(...)
Please see the hCaptcha Enterprise documentation for more details.
Enabling the visible checkbox flow
This iOS SDK assumes by default that you want an "invisible" checkbox, i.e. that triggering the hCaptcha flow from within your app should either return a token or show the user a challenge directly.
If you instead want the classic "normal" or "compact" checkbox behavior of showing a checkbox to tick and then either closing or showing a challenge, you can pass size
to the HCaptcha initializer.
let hcaptcha = try? HCaptcha(size: .compact)
And you will now get the desired behavior.
Using landscape instead of portrait orientation
The orientation
argument can be set either .portrait
or .landscape
orientation to adjust challenge modal behavior.
let hcaptcha = try? HCaptcha(orientation: .landscape)
By default, orientation is portrait and does not reflow.
However, if you have an app used exclusively in landscape mode (e.g. a game) then you can also switch the challenge UI to match this design choice.
SDK Events
This SDK allows you to receive interaction events, for your analytics via the onEvent
method. At the moment the SDK supports:
-
open
fires when hCaptcha is opened and a challenge is visible to an app user -
expired
fires when the passcode response expires and the user must re-verify -
challengeExpired
fires when the user display of a challenge times out with no answer -
close
fires when the user dismisses a challenge. -
error
fires when an internal error happens during challenge verification, for example a network error. Details about the error will be provided by thedata
param, as in the example below. Note: This event is not intended for error handling, but only for analytics purposes. For error handling please see thevalidate
API call docs.
You can implement this with the code below:
let hcaptcha = try? HCaptcha(...)
...
hcaptcha.onEvent { (event, data) in
if event == .open {
...
} else if event == .error {
let error = data as? HCaptchaError
print("onEvent error: \(String(describing: error))")
...
}
}
For RxSwift
:
let hcaptcha = try? HCaptcha(...)
...
hcaptcha.rx.events()
.subscribe { [weak self] rxevent in
let event = rxevent.element?.0
if event == .open {
...
}
}
...
Disable new token fetch on expiry
By default the SDK will automatically fetch a new token upon expiry once you have requested a token via validate
. This behavior can be adjusted by passing resetOnError: false
to the validate
call:
hcaptcha.validate(on: view, resetOnError: false) { result in
...
}
Change hCaptcha frame
If you are customizing display beyond the defaults and need to resize or change the hCaptcha layout, for example after a visual challenge appears, you can use the following approach to trigger a redraw of the view:
let hcaptcha = try? HCaptcha(...)
var visualChallengeShown = false
...
hcaptcha?.configureWebView { [weak self] webview in
webview.tag = "hCaptchaViewTag"
if visualChallengeShown {
let padding = 10
webview.frame = CGRect(
x: padding,
y: padding,
width: view.frame.size.width - 2 * padding,
height: targetHeight - 2 * padding
)
} else {
webview.frame = self?.view.bounds ?? CGRect.zero
}
}
...
hcaptcha.onEvent { (event, data) in
if event == .open {
visualChallengeShown = true
hcaptcha.redrawView*()
} else if event == .error {
let error = data as? HCaptchaError
print("onEvent error: \(String(describing: error))")
...
}
}
...
hcaptcha.validate(on: view, resetOnError: false) { result in
visualChallengeShown = false
}
SwiftUI Example
HCaptcha
was originally designed to be used with UIKit. But you can easily use it with SwiftUI
as well. Check out the SwiftUI Example
Objective-C Example
HCaptcha
can be used from Objective-C code. Check out the Example Project
Compiled size: impact on including in your app
HCaptcha pod size: 140 KB as of Jan 2024. You can always see the latest number in the CI logs by searching for the "pod size" string.
Known issues
- WebView crashes on Simulator iOS 14.x (arm64) but not on real devices. More details
License
HCaptcha is available under the MIT license. See the LICENSE file for more info.
Troubleshooting
Q. I'm getting a "Could not load embedded HTML" exception. What does this mean?
A. Most likely you have not included an asset in your build. Please double-check assets, and see the example app for more details.
Q. I'm getting "xcconfig: unable to open file" after upgrading the SDK. (Or changing SDK and running Example app.)
A. In your app or the Example app dir, run pod deintegrate && pod install
to refresh paths.
Inspiration
Originally forked from fjcaetano's ReCaptcha IOS SDK, licensed under MIT.