Driver SDK for iOS のスタートガイド

Driver SDK は、ドライバアプリに統合するライブラリです。ドライバーの位置、ルート、残りの距離、到着予定時刻で Fleet Engine を更新します。また、ターンバイターン方式のナビをドライバーに提供する Navigation SDK とも統合されています。

最小システム要件

  • モバイル デバイスに iOS 14 以降が搭載されている必要があります。
  • Xcode バージョン 14 以降。
  • 前提条件

    このガイドでは、アプリに Navigation SDK がすでに実装されており、Fleet Engine バックエンドが設定され、使用可能であることを前提としています。ただし、サンプルコードでは、Navigation SDK のセットアップ方法のサンプルを用意しています。

    また、Google Cloud プロジェクトで Maps SDK for iOS を有効にし、API キーを取得する必要があります。

    アクセスの取得

    Google Workspace を使用している場合は、オンボーディング時に google-maps-platform-sdk-users@workspacedomain.com などの Workspace グループを作成し、その名前を Google に提供します。これはおすすめの方法です。正しい CocoaPods リポジトリへのアクセス権を付与する許可リストにワークスペース グループが追加されます。アクセス権が必要なユーザーのメールアドレスとサービス アカウントのメールアドレスがこのリストに含まれていることを確認します。

    組織で Workspace グループを作成できない場合は、これらのアーティファクトにアクセスする必要があるユーザーとサービス アカウントのメールアドレスのリストを Google に送信します。

    ローカルでの開発

    ローカル開発の場合は、Cloud SDK でログインするだけで十分です。

    gcloud

    gcloud auth login
    

    ログインに使用するメールアドレスは、Workspace グループのメンバーである必要があります。

    自動化(ビルドシステムまたは継続的インテグレーション)

    ベスト プラクティスに従って自動化ホストを設定します。

    • プロセスが Google Cloud 環境内で実行されている場合は、認証情報の自動検出を使用します。

    • それ以外の場合は、ホストのファイル システム上の安全な場所にサービス アカウント キーファイルを保存し、GOOGLE_APPLICATION_CREDENTIALS 環境変数を適切に設定します。

    認証情報に関連付けられたサービス アカウントのメールアドレスは、Workspace Goup のメンバーである必要があります。

    Project Configuration

    CocoaPods を使用して Driver SDK を構成できます。

    CocoaPods を使用

    CocoaPods を使用して Driver SDK を構成するには、次のものが必要です。

    • CocoaPods ツール: このツールをインストールするには、ターミナルを開いて次のコマンドを実行します。shell sudo gem install cocoapods 詳しくは、CocoaPods スタートガイドをご覧ください。
    1. Driver SDK の Podfile を作成し、それを使用して API とその依存関係をインストールします。プロジェクト ディレクトリに Podfile という名前のファイルを作成します。このファイルでプロジェクトの依存関係を定義します。Podfile を編集して、依存関係を追加します。依存関係を含む例を次に示します。

      source "https://github.com/CocoaPods/Specs.git"
      
      target 'YOUR_APPLICATION_TARGET_NAME_HERE' do
        pod 'GoogleRidesharingDriver'
      end
      
    2. Podfile を保存します。ターミナルを開き、Podfile があるディレクトリに移動します。

      cd <path-to-project>
      
    3. pod install コマンドを実行します。これにより、Podfile で指定された API と、その依存関係がインストールされます。

      pod install
      
    4. Xcode を終了し、プロジェクトの .xcworkspace ファイルを開いて(ダブルクリックして)Xcode を起動します。これ以降は、.xcworkspace ファイルを使用してプロジェクトを開く必要があります。

    アルファ版/ベータ版 SDK バージョン

    Driver SDK for iOS のアルファ版またはベータ版を構成するには、次のものが必要です。

    • CocoaPods ツール: このツールをインストールするには、ターミナルを開いて次のコマンドを実行します。

      sudo gem install cocoapods
      

      詳しくは、CocoaPods スタートガイドをご覧ください。

    • Google アクセスリストに登録されている開発アカウント。SDK のアルファ版とベータ版の Pod リポジトリは、公開ソースではありません。これらのバージョンにアクセスするには、Google カスタマー エンジニアにお問い合わせください。エンジニアが開発用アカウントをアクセスリストに追加し、認証用の Cookie を設定します。

    プロジェクトがアクセスリストに登録すると、Pod にアクセスできるようになります。

    1. Driver SDK for iOS の Podfile を作成し、それを使用して API とその依存関係をインストールします。プロジェクト ディレクトリに Podfile という名前のファイルを作成します。このファイルでプロジェクトの依存関係を定義します。Podfile を編集して、依存関係を追加します。依存関係を含む例を次に示します。

      source "https://cpdc-eap.googlesource.com/ridesharing-driver-sdk.git"
      source "https://github.com/CocoaPods/Specs.git"
      
      target 'YOUR_APPLICATION_TARGET_NAME_HERE' do
        pod 'GoogleRidesharingDriver'
      end
      
    2. Podfile を保存します。ターミナルを開き、Podfile があるディレクトリに移動します。

      cd <path-to-project>
      
    3. pod install コマンドを実行します。これにより、Podfile で指定された API と、その依存関係がインストールされます。

      pod install
      
    4. Xcode を終了し、プロジェクトの .xcworkspace ファイルを開いて(ダブルクリックして)Xcode を起動します。これ以降は、.xcworkspace ファイルを使用してプロジェクトを開く必要があります。

    XCFramework をインストールする

    XCFramework は、Driver SDK のインストールに使用するバイナリ パッケージです。このパッケージは、M1 チップセットを搭載したマシンなど、複数のプラットフォームで使用できます。このガイドでは、Driver SDK を含む XCFramework をプロジェクトに手動で追加し、Xcode でビルド設定を構成する方法について説明します。

    SDK のバイナリとリソースをダウンロードします。

    1. 圧縮したファイルを解凍して、XCFramework とリソースにアクセスします。

    2. Xcode を起動し、既存のプロジェクトを開くか、新しいプロジェクトを作成します。iOS を初めて使用する場合は、新しいプロジェクトを作成し、iOS App テンプレートを選択します。

    3. フレームワーク グループが存在しない場合は、プロジェクト グループにフレームワーク グループを作成します。

    4. ダウンロードした gRPCCertificates.bundle ファイルを Xcode プロジェクトの最上位ディレクトリにドラッグします。プロンプトが表示されたら、[必要に応じてコピー] を選択します。

    5. Driver SDK をインストールするには、[Frameworks, Libraries, and Embedded Content] に GoogleRidesharingDriver.xcframework ファイルをドラッグします。プロンプトが表示されたら、[必要に応じてコピー] を選択します。

    6. ダウンロードした GoogleRidesharingDriver.bundle を Xcode プロジェクトの最上位ディレクトリにドラッグします。プロンプトが表示されたら、[Copy items if needed] を選択します。

    7. Project Navigator のプロジェクトを選択し、アプリのターゲットを選択します。

    8. [Build Phases] タブを開き、[Link Binary with Libraries] で、次のフレームワークとライブラリを追加します(まだ存在しない場合)。

      • Accelerate.framework
      • AudioToolbox.framework
      • AVFoundation.framework
      • CoreData.framework
      • CoreGraphics.framework
      • CoreLocation.framework
      • CoreTelephony.framework
      • CoreText.framework
      • GLKit.framework
      • ImageIO.framework
      • libc++.tbd
      • libxml2.tbd
      • libz.tbd
      • LocalAuthentication.framework
      • OpenGLES.framework
      • QuartzCore.framework
      • SystemConfiguration.framework
      • UIKit.framework
      • WebKit.framework
    9. 特定のターゲットではなくプロジェクトを選択し、[Build Settings] タブを開きます。[Other Linker Flags] セクションで、デバッグ用とリリース用の両方に ‑ObjC を追加します。これらの設定が表示されない場合は、ビルド設定バーのフィルタを [Basic] から [All] に変更します。

    認可と認証を実装する

    ドライバ アプリが更新を生成して Fleet Engine バックエンドに送信する場合、リクエストに有効なアクセス トークンを含める必要があります。これらのリクエストを承認して認証するために、Driver SDK は GMTDAuthorization プロトコルに従ってオブジェクトを呼び出します。オブジェクトは、必要なアクセス トークンを提供する役割を担います。

    トークンの生成方法はアプリ デベロッパーが選択できます。実装では、次の機能を提供する必要があります。

    • HTTPS サーバーからアクセス トークンを(JSON 形式で取得できる)取得します。
    • トークンを解析してキャッシュに保存します。
    • トークンの有効期限が切れたら更新する。

    Fleet Engine サーバーで想定されるトークンの詳細については、認可用の JSON Web Token(JWT)の作成をご覧ください。

    プロバイダ ID は Google Cloud プロジェクト ID と同じです。詳しくは、Fleet Engine Deliveries API ユーザーガイドをご覧ください。

    次の例では、アクセス トークン プロバイダを実装します。

    #import "SampleAccessTokenProvider.h"
    #import <GoogleRidesharingDriver/GoogleRidesharingDriver.h>
    
    // SampleAccessTokenProvider.h
    @interface SampleAccessTokenProvider : NSObject<GMTDAuthorization>
    @end
    
    static NSString *const PROVIDER_URL = @"INSERT_YOUR_TOKEN_PROVIDER_URL";
    
    // SampleAccessTokenProvider.m
    @implementation SampleAccessTokenProvider{
      // The cached vehicle token.
      NSString *_cachedVehicleToken;
      // Keep track of the vehicle ID the cached token is for.
      NSString *_lastKnownVehicleID;
      // Keep track of when tokens expire for caching.
      NSTimeInterval _tokenExpiration;
    }
    
    - (void)fetchTokenWithContext:(nullable GMTDAuthorizationContext *)authorizationContext
                       completion:(nonnull GMTDAuthTokenFetchCompletionHandler)completion {
      if (!completion) {
        NSAssert(NO, @"%s encountered an unexpected nil completion.", __PRETTY_FUNCTION__);
        return;
      }
    
      // Get the vehicle ID from the authorizationContext. This is set by the Driver SDK.
      NSString *vehicleID = authorizationContext.vehicleID;
      if (!vehicleID) {
        NSAssert(NO, @"Vehicle ID is missing from authorizationContext.");
        return;
      }
    
    // Clear cached vehicle token if vehicle ID has changed.
      if (![_lastKnownVehicleID isEqual:vehicleID]) {
        _tokenExpiration = 0.0;
        _cachedVehicleToken = nil;
      }
      _lastKnownVehicleID = vehicleID;
    
      // Clear cached vehicle token if it has expired.
      if ([[NSDate date] timeIntervalSince1970] > _tokenExpiration) {
        _cachedVehicleToken = nil;
      }
    
      // If appropriate, use the cached token.
      if (_cachedVehicleToken) {
        completion(_cachedVehicleToken, nil);
        return;
      }
      // Otherwise, try to fetch a new token from your server.
      NSURL *requestURL = [NSURL URLWithString:PROVIDER_URL];
      NSMutableURLRequest *request = 
                              [[NSMutableURLRequest alloc] initWithURL:requestURL];
      request.HTTPMethod = @"GET";
      // Replace the following key values with the appropriate keys based on your
      // server's expected response.
      NSString *vehicleTokenKey = @"VEHICLE_TOKEN_KEY";
      NSString *tokenExpirationKey = @"TOKEN_EXPIRATION";
      __weak typeof(self) weakSelf = self;
      void (^handler)(NSData *_Nullable data, NSURLResponse *_Nullable response,
                      NSError *_Nullable error) =
          ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
            typeof(self) strongSelf = weakSelf;
            if (error) {
              completion(nil, error);
              return;
            }
    
            NSError *JSONError;
            NSMutableDictionary *JSONResponse =
                [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&JSONError];
    
            if (JSONError) {
              completion(nil, JSONError);
              return;
            } else {
              // Sample code only. No validation logic.
              id expirationData = JSONResponse[tokenExpirationKey];
              if ([expirationData isKindOfClass:[NSNumber class]]) {
                NSTimeInterval expirationTime = ((NSNumber *)expirationData).doubleValue;
                strongSelf->_tokenExpiration = [[NSDate date] timeIntervalSince1970] + expirationTime;
              }
              strongSelf->_cachedVehicleToken = JSONResponse[vehicleTokenKey];
              completion(JSONResponse[vehicleTokenKey], nil);
            }
        };
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *mainQueueURLSession =  
           [NSURLSession  sessionWithConfiguration:config delegate:nil
    delegateQueue:[NSOperationQueue mainQueue]];
    NSURLSessionDataTask *task = [mainQueueURLSession dataTaskWithRequest:request completionHandler:handler];
    [task resume];
    }
    
    @end
    

    DeliveryDriverAPI インスタンスを作成する

    GMTDDeliveryVehicleReporter インスタンスを取得するには、まず providerID、vehicleID、driverContext、accessTokenProvider を使用して GMTDDeliveryDriverAPI インスタンスを作成する必要があります。providerID は Google Cloud プロジェクト ID と同じです。また、ドライバ API から直接 GMTDDeliveryVehicleReporter インスタンスにアクセスできます。

    次の例では、GMTDDeliveryDriverAPI インスタンスを作成します。

    #import “SampleViewController.h”
    #import “SampleAccessTokenProvider.h”
    #import <GoogleRidesharingDriver/GoogleRidesharingDriver.h>
    
    static NSString *const PROVIDER_ID = @"INSERT_YOUR_PROVIDER_ID";
    
    @implementation SampleViewController {
     GMSMapView *_mapView;
    }
    
    - (void)viewDidLoad {
      NSString *vehicleID = @"INSERT_CREATED_VEHICLE_ID";
      SampleAccessTokenProvider *accessTokenProvider = 
                                    [[SampleAccessTokenProvider alloc] init];
      GMTDDriverContext *driverContext = 
         [[GMTDDriverContext alloc] initWithAccessTokenProvider:accessTokenProvider
                                                     providerID:PROVIDER_ID 
                                                  vehicleID:vehicleID 
          navigator:_mapView.navigator];
    
      GMTDDeliveryDriverAPI *deliveryDriverAPI = [[GMTDDeliveryDriverAPI alloc] initWithDriverContext:driverContext];
    }
    

    必要に応じて VehicleReporter イベントをリッスンします。

    GMTDDeliveryVehicleReporter は、locationTrackingEnabled が YES の場合に車両を定期的に更新します。こうした定期的な更新に応答するために、任意のオブジェクトは、GMTDVehicleReporterListener プロトコルに準拠して GMTDDeliveryVehicleReporter イベントをサブスクライブできます。

    次のイベントを処理できます。

    • vehicleReporter:didSucceedVehicleUpdate

      バックエンド サービスが車両の位置情報と状態の更新を正常に受信したことをドライバ アプリに通知します。

    • vehicleReporter:didFailVehicleUpdate:withError

      車両の更新に失敗したことをリスナーに通知します。位置情報の追跡が有効になっている限り、GMTDDeliveryVehicleReporter は引き続き最新のデータを Fleet Engine バックエンドに送信します。

    次の例では、これらのイベントを処理します。

    SampleViewController.h
    @interface SampleViewController : UIViewController<GMTDVehicleReporterListener>
    @end
    
    SampleViewController.m
    #import “SampleViewController.h”
    #import “SampleAccessTokenProvider.h”
    #import <GoogleRidesharingDriver/GoogleRidesharingDriver.h>
    
    static NSString *const PROVIDER_ID = @"INSERT_YOUR_PROVIDER_ID";
    
    @implementation SampleViewController {
     GMSMapView *_mapView;
    }
    
    
    - (void)viewDidLoad {
      // ASSUMES YOU IMPLEMENTED HAVE THE SAMPLE CODE UP TO THIS STEP.
      [ridesharingDriverAPI.vehicleReporter addListener:self];
    }
    
    - (void)vehicleReporter:(GMTDDeliveryVehicleReporter *)vehicleReporter didSucceedVehicleUpdate:(GMTDVehicleUpdate *)vehicleUpdate {
      // Handle update succeeded.
    }
    
    - (void)vehicleReporter:(GMTDDeliveryVehicleReporter *)vehicleReporter didFailVehicleUpdate:(GMTDVehicleUpdate *)vehicleUpdate withError:(NSError *)error {
      // Handle update failed.
    }
    
    @end
    

    位置情報の記録を有効にする

    位置情報の追跡を有効にするには、アプリで GMTDDeliveryVehicleReporterlocationTrackingEnabledYES に設定します。そうすると、GMTDDeliveryVehicleReporter は最新の位置情報を自動的に送信します。GMSNavigator がナビゲーション モード(setDestinations で目的地が設定されている場合)で、locationTrackingEnabledYES に設定されている場合、GMTDDeliveryVehicleReporter はルートと到着予定時刻の更新も自動的に送信します。

    更新中に設定されたルートは、ドライバーがナビゲーション セッション中にナビゲートするルートと同じになります。したがって、配送追跡が正しく機能するには、-setDestinations:callback: で設定されたウェイポイントが Fleet Engine バックエンドで設定された配送先と一致している必要があります。

    次の例では、位置情報の追跡を有効にしています。

    SampleViewController.m
    #import “SampleViewController.h”
    #import “SampleAccessTokenProvider.h”
    #import <GoogleRidesharingDriver/GoogleRidesharingDriver.h>
    
    static NSString *const PROVIDER_ID = @"INSERT_YOUR_PROVIDER_ID";
    
    @implementation SampleViewController {
     GMSMapView *_mapView; 
    }
    
    - (void)viewDidLoad {
      // ASSUMES YOU IMPLEMENTED HAVE THE SAMPLE CODE UP TO THIS STEP.
      deliveryDriverAPI.vehicleReporter.locationTrackingEnabled = YES;
    }
    
    @end
    

    デフォルトのレポート間隔は 10 秒ですが、locationUpdateInterval で変更できます。サポートされる最小更新間隔は 5 秒です。サポートされる最大更新間隔は 60 秒です。更新を頻繁に行うと、リクエストやエラーが遅くなる可能性があります。

    位置情報の更新を無効にする

    アプリで車両の位置情報の更新を無効にできます。たとえば、ドライバーのシフトが終了したときに、アプリで locationTrackingEnabledNO に設定できます。

      _vehicleReporter.locationTrackingEnabled = NO
    

    update_mask エラーを処理する

    GMTDDeliveryVehicleReporter が車両のアップデートを送信すると、マスクが空の場合に update_mask エラーが発生することがあります。これは通常、起動後の最初のアップデートで発生します。次の例は、このエラーの処理方法を示しています。

    Swift

    import GoogleRidesharingDriver
    
    class VehicleReporterListener: NSObject, GMTDVehicleReporterListener {
      func vehicleReporter(
        _ vehicleReporter: GMTDVehicleReporter,
        didFail vehicleUpdate: GMTDVehicleUpdate,
        withError error: Error
      ) {
        let fullError = error as NSError
        if let innerError = fullError.userInfo[NSUnderlyingErrorKey] as? NSError {
          let innerFullError = innerError as NSError
          if innerFullError.localizedDescription.contains("update_mask cannot be empty") {
            emptyMaskUpdates += 1
            return
          }
        }
        failedUpdates += 1
      }
    
      override init() {
        emptyMaskUpdates = 0
        failedUpdates = 0
      }
    }
    
    

    Objective-C

    #import "VehicleReporterListener.h"
    #import <GoogleRidesharingDriver/GoogleRidesharingDriver.h>
    
    @implementation VehicleReporterListener {
      NSInteger emptyMaskUpdates = 0;
      NSInteger failedUpdates = 0;
    }
    
    - (void)vehicleReporter:(GMTDVehicleReporter *)vehicleReporter
      didFailVehicleUpdate:(GMTDVehicleUpdate *)vehicleUpdate
                 withError:(NSError *)error {
      for (NSError *underlyingError in error.underlyingErrors) {
        if ([underlyingError.localizedDescription containsString:@"update_mask cannot be empty"]) {
          emptyMaskUpdates += 1;
          return;
        }
      }
      failedUpdates += 1
    }
    
    @end