reCAPTCHA を iOS アプリと統合する

このページでは、iOS アプリに reCAPTCHA を統合する方法について説明します。

画面サイズ、パフォーマンス、アプリの UI に関してモバイル デバイスは多様なため、視覚的なチェックボックスの reCAPTCHA による確認(私はロボットではありません)は iOS モバイルアプリでは使用できません。代わりに、MFA フローなどの独自の階層型適用戦略を実装し、不審なトラフィックに対する代替的な救済パスを提供できます。

SDK では、リフレクションと動的コードを利用して、すでにデプロイされているアプリケーションや SDK で検出システムを更新、調整できます。アプリケーションとの干渉を避けるために、システムで使用できる一連のクラスは慎重に管理されたリストに制限されています。

始める前に

  1. アプリの最小 SDK を iOS 12 に設定するか、新しいモバイルアプリを作成します。

  2. reCAPTCHA 用に環境を準備します

  3. iOS アプリ プラットフォーム用の reCAPTCHA キーを作成します

    あるいは、次のいずれかの方法で、iOS 用の既存の reCAPTCHA キーの ID をコピーできます。

    • Google Cloud コンソールから既存のチェックボックスのキーの ID をコピーするには、次の操作を行います。

      1. [reCAPTCHA] ページに移動します。

        [reCAPTCHA] に移動

      2. reCAPTCHA キーのリストで、コピーするキーの上にポインタを置き、[] をクリックします。
    • REST API を使用して既存のキーの ID をコピーするには、projects.keys.list メソッドを使用します。
    • gcloud CLI を使用して既存のキーの ID をコピーするには、gcloud recaptcha keys list コマンドを使用します。

  4. GitHub アカウントを用意します。

  5. Apple 関連のプライバシーの詳細を確認します。

iOS 環境を準備する

開発環境を準備する手順は次のとおりです。

  1. 最新バージョンの Xcode をダウンロードしてインストールし、空の新しい iOS シングルビュー アプリケーションを作成します。

  2. 次のいずれかを使用して SDK をダウンロードします。

    CocoaPods

    1. CocoaPods をダウンロードしてインストールします。
    2. Podfile を作成し、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
      
    3. pod update を実行して、必要な依存関係をインストールします。

    Swift Package Manager

    1. Xcode で [File] > [Add Packages] を選択し、[Search] または [Enter Package URL] フィールドに次の URL を入力します: https://github.com/GoogleCloudPlatform/recaptcha-enterprise-mobile-sdk
    2. [Xcode] ダイアログで次の項目を入力します。

      • GitHub のユーザー名。
      • GitHub の手順を使用して作成した個人用のアクセス トークン。 個人用アクセス トークンには、[XCode ログイン] ダイアログに表示されているスコープが必要です。

      Xcode で SDK と必要な依存関係がインストールされます。

    Flutter

    Flutter で reCAPTCHA を使用する方法について詳しくは、Flutter のドキュメントをご覧ください。

    ReactNative

    React Native で reCAPTCHA を使用する方法について詳しくは、React Native のドキュメントをご覧ください。

    直接ダウンロード

    1. SDK とその依存関係を xcframework としてダウンロードする場合は、クライアントをダウンロードしてください。

アプリを構成する

Swift または Objective-C でアプリを作成できます。

アプリを設定するには、アプリに次のファイルを追加します。

Swift

  1. アプリが Swift で記述されている場合は、次のインポートを含めます。

     import RecaptchaEnterprise
    

Objective-C

  1. アプリが Objective-C で記述されている場合は、ダミーの Swift ファイルを作成し、次のインポートを含めて、Xcode が Swift ライブラリを検出、リンクできるようにします。

    import Foundation
    
  2. Swift コードが正しくリンクされていることを確認します。[Target] > [Build Settings] > [Always Embed Swift Standard Libraries] に移動して、オプションが Yes に設定されていることを確認してください。

reCAPTCHA を iOS アプリと統合する

reCAPTCHA を iOS アプリと統合するには、Xcode で次の手順を行います。

  1. 作成した reCAPTCHA キー(KEY_ID)を使用して SDK をインスタンス化するには、次のコードでアプリを更新します。

    Swift とストーリーボード

    1. 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)).")
            }
          }
        }
      }
      

      アプリケーションの最小 OS バージョンが 13 未満の場合は、代わりに末尾クロージャを使用します。

      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 と SwiftUI

    1. ViewModel クラスを作成します。

      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)).")
            }
          }
        }
      }
      

      アプリケーションの最小 OS バージョンが 13 未満の場合は、代わりに末尾クロージャを使用します。

      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
          }
        }
      }
      
    2. ContentView.swiftViewModel をインスタンス化します。

      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

    1. ViewController.h を更新します。

      #import <RecaptchaEnterprise/RecaptchaEnterprise.h>
      
      @interface ViewController : UIViewController
      @property (strong, atomic) RecaptchaClient *recaptchaClient;
      @end
      
    2. 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
      

    SDK の初期化が完了するまでに数秒かかることがあります。このレイテンシを軽減するには、カスタム Application クラスの onCreate() 呼び出し時など、できるだけ早くクライアントを初期化します。UI 要素を reCAPTCHA SDK でブロックしないでください。

  2. reCAPTCHA を呼び出して execute() をトリガーするボタンを作成します。

    Swift とストーリーボード

    1. ストーリーボードにボタンを作成します。
    2. 作成したボタンにリンクされている ViewController にアクションを作成します。
    3. execute() メソッドを呼び出して Login アクションを渡し、次のコード スニペットを使用して reCAPTCHA トークンを返します。

      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)
        }
      }
      

      アプリケーションの最小 OS バージョンが 13 未満の場合は、代わりに末尾クロージャを使用します。

      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 と SwiftUI

    1. 実行コードで ViewModel.swift を更新します。

      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)
            }
          }
        }
      }
      

      アプリケーションの最小 OS バージョンが 13 未満の場合は、代わりに末尾クロージャを使用します。

      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)
        }
      }
      
    2. 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

    1. ストーリーボードにボタンを作成します。
    2. 作成したボタンにリンクされている ViewController にアクションを作成します。
    3. execute() メソッドを呼び出して Login アクションを渡し、reCAPTCHA トークンを返します。

      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);
      }];
      

    ネットワークの状態が遅い場合や、バックグラウンドの初期化が完了するのを待機している場合など、クライアントの execute API が完了するまでに数秒かかることがあります。execute() 呼び出しが、ボタンの押下など、UI イベントをブロックしていないことを確認します。

  3. アプリケーションをテストする

    1. reCAPTCHA は、検出エンジンの一部として Apple の AppAttest を使用します。ローカルでの開発に固定スコアを持つテストキーを使用する予定がない場合は、次の手順を行います。

      1. Xcode で、アプリに App Attest 機能を追加します。

      2. プロジェクトの .entitlements ファイルで、App Attest 環境を production に設定します。

    2. Xcode ビルド環境をクリーンアップするには、[Product] メニューで [Clean Build Folder] をクリックします。

    3. アプリケーションを実行するには、[Product] メニューで [Run] をクリックします。

    4. 読み込んだアプリケーションで、前に作成したボタンをクリックします。

    5. デバッグ出力ウィンドウを確認します。統合が成功した場合、reCAPTCHA トークン(英数字文字列)が返されます。

method API から fetchClient メソッドへの移行

fetchClient メソッドは、ネットワーク障害が発生した場合に初期化を再試行する RecaptchaClient を返します。クライアントの作成時にアプリがネットワークにアクセスできない場合、クライアントは再試行を続け、ネットワークが取得されると正常に初期化されます。

execute(timeout) を呼び出したときにクライアントの準備ができていない場合は、トークンまたは RecaptchaErrorCode を返す前に初期化を試みます。

次の例は、getClient から fetchClient に移行する方法を示しています。

Swift とストーリーボード

// 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)).")
    }
  }
}

アプリケーションの最小 OS バージョンが 13 未満の場合は、代わりに末尾クロージャを使用します。

// 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 と 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)).")
    }
  }
}

アプリケーションの最小 OS バージョンが 13 未満の場合は、代わりに末尾クロージャを使用します。

// 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

API 呼び出しのタイムアウトを設定する

execute API のタイムアウト値は、withTimeout プロパティを使用して指定できます。

Swift

  1. execute を呼び出すときのタイムアウトを設定します。

      Task {
        do {
          let token = try await recaptchaClient.execute(
            withAction: RecaptchaAction.login,
            withTimeout: 10000)
          print(token)
        } catch let error as RecaptchaError {
          print(error.errorMessage)
        }
      }
    

    アプリケーションの最小 OS バージョンが 13 未満の場合は、代わりに末尾クロージャを使用します。

      recaptchaClient.execute(
        withAction: RecaptchaAction.login,
        withTimeout: 10000
      ) { token, error in
        if let token = token {
          print(token)
        } else {
          print(error)
        }
      }
    

Objective-C

  1. 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);
      }];
    

エラーを処理する

アプリが reCAPTCHA サービスと通信できない場合は、API でエラーが発生したことが原因の可能性があります。このようなエラーを適切に処理するロジックをアプリに追加する必要があります。

一般的な API エラーの軽減の詳細については、RecaptchaErrorCode をご覧ください。

API リファレンス

iOS 用の reCAPTCHA API の詳細なリファレンスについては、RecaptchaEnterprise をご覧ください。

次のステップ

  • reCAPTCHA の応答トークンを評価するため、評価を作成する。