This page explains how to integrate reCAPTCHA in your iOS app.
Due to the variation in mobile devices in terms of screen size, performance, and UIs of the apps, the visual checkbox reCAPTCHA challenge (I'm not a robot) is not available for iOS mobile apps. You can instead implement your own tiered enforcement strategy, such as an MFA flow to provide an alternative redemption path for suspicious traffic.
The SDK makes use of reflection and dynamic code to allow for the update and refinement of the detection system in already deployed applications or SDKs. To avoid interference with the application, the set of classes available in the system are restricted to a carefully controlled list.
Before you begin
Set the minimum SDK of your app to iOS 12 or create a new mobile app.
Create a reCAPTCHA key for the iOS app platform.
Alternatively, you can copy the ID of an existing reCAPTCHA key for iOS by performing one of the following steps:
To copy the ID of an existing key from the Google Cloud console, do the following:
Go to the reCAPTCHA page.
- In the reCAPTCHA keys list, hold the pointer over the key you want to copy, and then click .
- To copy the ID of an existing key using the REST API, use the projects.keys.list method.
- To copy the ID of an existing key using the gcloud CLI, use the gcloud recaptcha keys list command.
Have a GitHub account.
Read the Apple privacy details.
Prepare your iOS environment
To prepare your development environment, do the following:
Download and install the latest version of Xcode and create a new blank iOS single view application.
Download the SDK by using one of the following:
CocoaPods
- Download and install CocoaPods.
Create a Podfile and add the following lines to your Podfile:
source "https://github.com/CocoaPods/Specs.git" target 'AppTarget' do # Podfiles must include use_frameworks! or # use_frameworks! :linkage => :static use_frameworks! pod "RecaptchaEnterprise", "18.6.0" ... end
Install the required dependencies by running
pod update
.
Swift Package Manager
- In Xcode, select File > Add Packages, and enter the
following URL in the Search or Enter Package URL field:
https://github.com/GoogleCloudPlatform/recaptcha-enterprise-mobile-sdk
In the Xcode dialog, enter the following details:
- GitHub username.
- A personal access token that you created using GitHub's instructions. The Personal Access Token must have the scopes listed in the XCode Sign In dialog.
Xcode installs the SDK and its required dependencies.
Flutter
For detailed instructions about using reCAPTCHA through Flutter, see the Flutter documentation.
ReactNative
For detailed instructions about using reCAPTCHA through React Native, see the React Native documentation.
Direct download
If you want to download the SDK and its dependencies as xcframeworks, download the client.
Configure the app
You can write your apps in Swift or Objective-C.
To configure your app, add the following files in your app:
Swift
If your app is written in Swift include the following import:
import RecaptchaEnterprise
Objective-C
If your app is written in Objective-C, create a dummy Swift file and include the following import to make sure Xcode can find and link the Swift libraries.
import Foundation
To ensure that the
Swift
code is linked correctly navigate to Target > Build Settings > Always Embed Swift Standard Libraries and verify that the option is set toYes
.
Integrate reCAPTCHA with your iOS app
To integrate reCAPTCHA with your iOS app, follow these steps in Xcode:
To instantiate the SDK with the reCAPTCHA key (KEY_ID) that you created, update the app with the following code:
Swift with Storyboard
Update
ViewController.swift
.import RecaptchaEnterprise class ViewController: UIViewController { var recaptchaClient: RecaptchaClient? override func viewDidLoad() { super.viewDidLoad() Task { do { self.recaptchaClient = try await Recaptcha.fetchClient(withSiteKey: "KEY_ID") } catch let error as RecaptchaError { print("RecaptchaClient creation error: \(String(describing: error.errorMessage)).") } } } }
If your application's minimum OS version is less than 13, use a trailing closure instead:
import RecaptchaEnterprise class ViewController: UIViewController { var recaptchaClient: RecaptchaClient? override func viewDidLoad() { super.viewDidLoad() Recaptcha.fetchClient(withSiteKey: "KEY_ID") { client, error in guard let client = client else { print("RecaptchaClient creation error: \(error).") return } self.recaptchaClient = client } } }
Swift with SwiftUI
Create a
ViewModel
class.import RecaptchaEnterprise @MainActor class ViewModel: ObservableObject { private var recaptchaClient: RecaptchaClient? init() { Task { do { self.recaptchaClient = try await Recaptcha.fetchClient(withSiteKey: "KEY_ID") } catch let error as RecaptchaError { print("RecaptchaClient creation error: \(String(describing: error.errorMessage)).") } } } }
If your application's minimum OS version is less than 13, use a trailing closure instead:
import RecaptchaEnterprise class ViewController: UIViewController { var recaptchaClient: RecaptchaClient? override func viewDidLoad() { super.viewDidLoad() Recaptcha.fetchClient(withSiteKey: "KEY_ID") { client, error in guard let client = client else { print("RecaptchaClient creation error: \(error).") return } self.recaptchaClient = client } } }
Instantiate
ViewModel
inContentView.swift
.import SwiftUI import RecaptchaEnterprise struct ContentView: View { @StateObject private var viewModel = ViewModel() var body: some View { } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
Objective-C
Update
ViewController.h
.#import <RecaptchaEnterprise/RecaptchaEnterprise.h> @interface ViewController : UIViewController @property (strong, atomic) RecaptchaClient *recaptchaClient; @end
Update
ViewController.m
.@implementation ViewController [Recaptcha fetchClientWithSiteKey:@"KEY_ID" completion:^void(RecaptchaClient* recaptchaClient, NSError* error) { if (!recaptchaClient) { NSLog(@"%@", (RecaptchaError *)error.errorMessage); return; } self->_recaptchaClient = recaptchaClient; } ]; @end
Initialization of the SDK can take several seconds to complete. To mitigate this latency, initialize the client as early as possible, such as during the
onCreate()
call of a customApplication
class. You shouldn't make UI elements block on the reCAPTCHA SDK.Create a button to call reCAPTCHA and trigger
execute()
.Swift with Storyboard
- In the storyboard, create a button.
- Create an action in
ViewController
linked to the button that you created. Call the
execute()
method passing aLogin
action to return a reCAPTCHA token by using the following code snippet:guard let recaptchaClient = recaptchaClient else { print("RecaptchaClient creation failed.") return } Task { do { let token = try await recaptchaClient.execute(withAction: RecaptchaAction.login) print(token) } catch let error as RecaptchaError { print(error.errorMessage) } }
If your application's minimum OS version is less than 13, use a trailing closure instead:
guard let recaptchaClient = recaptchaClient else { print("RecaptchaClient creation failed.") return } recaptchaClient.execute(withAction: RecaptchaAction.login) { token, error in if let token = token { print(token) } else { print(error) } }
Swift with SwiftUI
Update ViewModel.swift with the execute code:
import RecaptchaEnterprise @MainActor class ViewModel: ObservableObject { func execute() { guard let recaptchaClient = self.recaptchaClient else { print("Client not initialized correctly.") return } Task { do { let token = try await recaptchaClient.execute(withAction: RecaptchaAction.login) print(token) } catch let error as RecaptchaError { print(error.errorMessage) } } } }
If your application's minimum OS version is less than 13, use a trailing closure instead:
guard let recaptchaClient = recaptchaClient else { print("RecaptchaClient creation failed.") return } recaptchaClient.execute(withAction: RecaptchaAction.login) { token, error in if let token = token { print(token) } else { print(error) } }
Update ContentView.swift.
import SwiftUI import RecaptchaEnterprise struct ContentView: View { @StateObject private var viewModel = ViewModel() var body: some View { Button { viewModel.execute() } label: { Text("Execute") }.padding() Spacer() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
Objective-C
- In the storyboard, create a button.
- Create an action in
ViewController
linked to the button that you created. Call the
execute()
method passing aLogin
action to return a reCAPTCHA token:if (!self->_recaptchaClient) { return; } [recaptchaClient execute:RecaptchaAction.login completion:^void(NSString* _Nullable token, NSError* _Nullable error) { if (!token) { NSLog (@"%@", (RecaptchaError *)error.errorMessage); return; } NSLog (@"%@", token); }];
The
execute
API of the client can take several seconds to complete, such as in slow networking conditions or if it is waiting for background initialization to complete. Ensure that theexecute()
calls do not block a UI event, such as pressing a button.Test your application:
reCAPTCHA uses Apple's AppAttest as part of its detection engine. If you don't plan to use a testing key with a fixed score for local development, do the following:
In Xcode, add the App Attest capability to your app.
In your project's
.entitlements
file, set the App Attest environment toproduction
.
To clean your Xcode build environment, in the Product menu, click Clean Build Folder.
To run the application, in the Product menu, click Run.
In your loaded application, click the button that you created earlier.
Observe your debug output window for a reCAPTCHA token (alpha-numeric string), which is returned if the integration is successful.
Migrating from method API to fetchClient method
The fetchClient
method returns a RecaptchaClient
that retries initialization over network failures. If the app doesn't have
network access when the client is created, the client keeps retrying and
successfully initializes when a network is acquired.
If you call execute(timeout)
and the client is not yet ready, it tries
to initialize before returning a token or a RecaptchaErrorCode.
The following example shows how to migrate from getClient
to fetchClient
.
Swift with Storyboard
// Migrate from getClient
func initializeWithGetClient() {
Task {
do {
self.recaptchaClient = try await Recaptcha.getClient(withSiteKey: "KEY_ID")
} catch let error as RecaptchaError {
print("RecaptchaClient creation error: \(String(describing: error.errorMessage)).")
}
}
}
// Migrate to fetchClient
func initializeWithFetchClient() {
Task {
do {
self.recaptchaClient = try await Recaptcha.fetchClient(withSiteKey: "KEY_ID")
} catch let error as RecaptchaError {
print("RecaptchaClient creation error: \(String(describing: error.errorMessage)).")
}
}
}
If your application's minimum OS version is less than 13, use a trailing closure instead:
// Migrate from getClient
override func initializeWithGetClient() {
Recaptcha.getClient(withSiteKey: "KEY_ID") { client, error in
guard let client = client else {
print("RecaptchaClient creation error: \(error).")
return
}
self.recaptchaClient = client
}
}
// Migrate to fetchClient
override func initializeWithFetchClient() {
Recaptcha.fetchClient(withSiteKey: "KEY_ID") { client, error in
guard let client = client else {
print("RecaptchaClient creation error: \(error).")
return
}
self.recaptchaClient = client
}
}
Swift with SwiftUI
// Migrate from getClient
initializeWithGetClient() {
Task {
do {
self.recaptchaClient = try await Recaptcha.getClient(withSiteKey: "KEY_ID")
} catch let error as RecaptchaError {
print("RecaptchaClient creation error: \(String(describing: error.errorMessage)).")
}
}
}
// Migrate to fetchClient
initializeWithFetchClient() {
Task {
do {
self.recaptchaClient = try await Recaptcha.fetchClient(withSiteKey: "KEY_ID")
} catch let error as RecaptchaError {
print("RecaptchaClient creation error: \(String(describing: error.errorMessage)).")
}
}
}
If your application's minimum OS version is less than 13, use a trailing closure instead:
// Migrate from getClient
func initializeWithGetClient() {
super.viewDidLoad()
Recaptcha.getClient(withSiteKey: "KEY_ID") { client, error in
guard let client = client else {
print("RecaptchaClient creation error: \(error).")
return
}
self.recaptchaClient = client
}
}
// Migrate to fetchClient
func initializeWithFetchClient() {
super.viewDidLoad()
Recaptcha.fetchClient(withSiteKey: "KEY_ID") { client, error in
guard let client = client else {
print("RecaptchaClient creation error: \(error).")
return
}
self.recaptchaClient = client
}
}
Objective-C
// Migrate from getClient
@implementation ViewController
[Recaptcha getClientWithSiteKey:@"KEY_ID"
completion:^void(RecaptchaClient* recaptchaClient, NSError* error) {
if (!recaptchaClient) {
NSLog(@"%@", (RecaptchaError *)error.errorMessage);
return;
}
self->_recaptchaClient = recaptchaClient;
}
];
@end
// Migrate to fetchClient
@implementation ViewController
[Recaptcha fetchClientWithSiteKey:@"KEY_ID"
completion:^void(RecaptchaClient* recaptchaClient, NSError* error) {
if (!recaptchaClient) {
NSLog(@"%@", (RecaptchaError *)error.errorMessage);
return;
}
self->_recaptchaClient = recaptchaClient;
}
];
@end
Set a timeout for API calls
You can specify a timeout value for the execute
APIs by using the
withTimeout
property.
Swift
Set timeout when calling
execute
.Task { do { let token = try await recaptchaClient.execute( withAction: RecaptchaAction.login, withTimeout: 10000) print(token) } catch let error as RecaptchaError { print(error.errorMessage) } }
If your application's minimum OS version is less than 13, use a trailing closure instead:
recaptchaClient.execute( withAction: RecaptchaAction.login, withTimeout: 10000 ) { token, error in if let token = token { print(token) } else { print(error) } }
Objective-C
Set timeout when calling
execute
.[recaptchaClient execute:RecaptchaAction.login witTimeout:10000.0 completion:^void(NSString* _Nullable token, NSError* _Nullable error) { if (!token) { NSLog (@"%@", (RecaptchaError *)error.errorMessage); return; } NSLog (@"%@", token); }];
Handle errors
If your app cannot communicate with the reCAPTCHA service successfully, it might be because the API encountered an error. You must add logic in your app to gracefully handle such errors.
For more details about mitigations for common API errors, see RecaptchaErrorCode.
API reference
For a complete reference of the reCAPTCHA API for iOS, see
RecaptchaEnterprise
.
What's next
To assess the reCAPTCHA response token, create an assessment.