On-demand Rides and Deliveries Solution is currently available only to select partners.

Driver SDK for iOS スタートガイド

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

Driver SDK を使用して、移動や注文処理のアプリに高度なナビゲーションとトラッキングを提供できます。Driver SDK は車両の現在地とタスクの最新情報をオンデマンド配車および配達ソリューション フリート エンジンに提供します。

Driver SDK は Fleet Engine サービスとカスタム サービスに、車両の位置情報と状態を認識させます。たとえば、車両には ONLINE または OFFLINE を指定でき、車両の位置情報はルートの進行に伴って変化します。

最小システム要件

モバイル デバイスが iOS 12 以降を実行している必要があります。

Prerequisites

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

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

Project Configuration

Driver SDK は手動で、または CocoaPods を使用して構成できます。

CocoaPods を使用

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

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

    sudo gem install cocoapods
    

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

  • Google アクセスリストのデベロッパー アカウント。SDK の Pod リポジトリは一般公開されません。Pod にアクセスするには、Google カスタマー エンジニアに連絡してください。エンジニアが、開発用アカウントをアクセスリストに追加し、認証用の Cookie を設定します。

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

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

    source "https://cpdc-eap.googlesource.com/ridesharing-driver-sdk.git"
    source "https://cpdc-eap.googlesource.com/geo-nav-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 のインストール コマンドを実行します。これにより、Podfile で指定された API と、必要な依存関係がインストールされます。

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

手動で構成する

Driver SDK を手動で構成するには:

  1. Maps SDK for iOS をインストールする

    ライドシェアリング フレームワークを使用するには、Maps SDK for iOS(GoogleMaps フレームワーク)が必要です。詳しくは、Maps SDK for iOS のインストールについての記事をご覧ください。

  2. ライドシェアリングのフレームワークとリソースを追加する

    1. 受け取った元の ZIP ファイルを展開します。

    2. Xcode を起動し、既存のプロジェクトを開くか、新しいプロジェクトを作成します。

    3. プロジェクト ナビゲータからプロジェクトを選択し、アプリケーションのターゲットを選択します。

    4. [Build Phases] タブを開き、[Link Binary with Libraries] 内でプラス記号ボタン(+)をクリックします。

    5. [その他を追加] をクリックして、ライドシェアリングのフレームワークを選択します。

    6. ライドシェアリングのフレームワーク ディレクトリから、ライドシェアリングのフレームワーク GoogleRidesharingDriver.framework を選択します。

  3. リソースの追加

    1. Xcode でプロジェクトの [Build Phases] を開き、[Copy Bundle Resources] 内でプラス記号ボタン(+)をクリックします。

    2. [その他を追加] をクリックして、ライドシェアリングのフレームワークを選択します。

    3. ライドシェアリング フレームワーク ディレクトリから、次のファイルを選択します。

      • gRPCCertificates.bundle(プロジェクトですでに gRPC を使用している場合は必須ではありません)。
    4. [ファイルの追加オプションを選択する] で、[必要に応じてアイテムをコピー] をオンにします。

承認と認証を実装する

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

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

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

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

プロバイダ ID は Google Cloud プロジェクト ID と同じです。詳細については、Fleet Engine クイックスタート ガイドをご覧ください。

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

Swift

import GoogleRidesharingDriver

private let providerURL = "INSERT_YOUR_TOKEN_PROVIDER_URL"

class SampleAccessTokenProvider: NSObject, GMTDAuthorization {
  private struct AuthToken {
    // The cached vehicle token.
    let token: String
    // Keep track of when the token expires for caching.
    let expiration: TimeInterval
    // Keep track of the vehicle ID the cached token is for.
    let vehicleID: String
  }

  enum AccessTokenError: Error {
    case missingAuthorizationContext
    case missingData
  }

  private var authToken: AuthToken?

  func fetchToken(
    with authorizationContext: GMTDAuthorizationContext?,
    completion: @escaping GMTDAuthTokenFetchCompletionHandler
  ) {
    // Get the vehicle ID from the authorizationContext. This is set by the Driver SDK.
    guard let authorizationContext = authorizationContext else {
      completion(nil, AccessTokenError.missingAuthorizationContext)
      return
    }
    let vehicleID = authorizationContext.vehicleID

    // If appropriate, use the cached token.
    if let authToken = authToken,
      authToken.expiration > Date.now.timeIntervalSince1970 && authToken.vehicleID == vehicleID
    {
      completion(authToken.token, nil)
      return
    }

    // Otherwise, try to fetch a new token from your server.
    let request = URLRequest(url: URL(string: providerURL))
    let task = URLSession.shared.dataTask(with: request) { [weak self] data, _, error in
      guard let strongSelf = self else { return }
      guard error == nil else {
        completion(nil, error)
        return
      }

      // Replace the following key values with the appropriate keys based on your
      // server's expected response.
      let vehicleTokenKey = "VEHICLE_TOKEN_KEY"
      let tokenExpirationKey = "TOKEN_EXPIRATION"
      guard let data = data,
        let fetchData = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
        let token = fetchData[vehicleTokenKey] as? String,
        let expiration = fetchData[tokenExpirationKey] as? Double
      else {
        completion(nil, AccessTokenError.missingData)
        return
      }

      strongSelf.authToken = AuthToken(
        token: token, expiration: expiration, vehicleID: vehicleID)
      completion(token, nil)
    }
    task.resume()
  }
}

Objective-C

#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 {
  // 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 vehicletoken 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

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

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

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

Swift

import GoogleRidesharingDriver

private let providerID = "INSERT_YOUR_PROVIDER_ID"

class SampleViewController: UIViewController {
  private let mapView: GMSMapView

  override func viewDidLoad() {
    super.viewDidLoad()

    let vehicleID = "INSERT_CREATED_VEHICLE_ID"
    let accessTokenProvider = SampleAccessTokenProvider()
    let driverContext = GMTDDriverContext(
      accessTokenProvider: accessTokenProvider,
      providerID: providerID,
      vehicleID: vehicleID,
      navigator: mapView.navigator)
    let ridesharingDriverAPI = GMTDRidesharingDriverAPI(driverContext: driverContext)
  }
}

Objective-C

#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];

  GMTDRidesharingDriverAPI *ridesharingDriverAPI = [[GMTDRidesharingDriverAPI alloc] initWithDriverContext:driverContext];
}

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

locationTrackingEnabled が YES の場合、GMTDVehicleReporter は定期的に車両を更新します。こうした定期的な更新に対応するため、GMTDVehicleReporterListener プロトコルに準拠することにより、あらゆるオブジェクトが GMTDVehicleReporter イベントをサブスクライブできます。

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

  • vehicleReporter:didSucceedVehicleUpdate

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

  • vehicleReporter:didFailVehicleUpdate:withError

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

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

Swift

import GoogleRidesharingDriver

private let providerID = "INSERT_YOUR_PROVIDER_ID"

class SampleViewController: UIViewController, GMTDVehicleReporterListener {
  private let mapView: GMSMapView

  override func viewDidLoad() {
    // Assumes you have implemented the sample code up to this step.
    ridesharingDriverAPI.vehicleReporter.add(self)
  }

  func vehicleReporter(_ vehicleReporter: GMTDVehicleReporter, didSucceed vehicleUpdate: GMTDVehicleUpdate) {
    // Handle update succeeded.
  }

  func vehicleReporter(_ vehicleReporter: GMTDVehicleReporter, didFail vehicleUpdate: GMTDVehicleUpdate, withError error: Error) {
    // Handle update failed.
  }
}

Objective-C

/*
 * 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 have implemented the sample code up to this step.
  [ridesharingDriverAPI.vehicleReporter addListener:self];
}

- (void)vehicleReporter:(GMTDVehicleReporter *)vehicleReporter didSucceedVehicleUpdate:(GMTDVehicleUpdate *)vehicleUpdate {
  // Handle update succeeded.
}

- (void)vehicleReporter:(GMTDVehicleReporter *)vehicleReporter didFailVehicleUpdate:(GMTDVehicleUpdate *)vehicleUpdate withError:(NSError *)error {
  // Handle update failed.
}

@end

GMSRoadSnappedLocationProvider にリスナーとして GMTDVehicleReporter を追加

Driver SDK に位置情報の更新を提供するには、GMTDVehicleReporterGMSRoadSnappedLocationProvider のリスナーとして設定する必要があります。

Swift

import GoogleRidesharingDriver

private let providerID = "INSERT_YOUR_PROVIDER_ID"

class SampleViewController: UIViewController, GMTDVehicleReporterListener {
  private let mapView: GMSMapView

  override func viewDidLoad() {
    // Assumes you have implemented the sample code up to this step.
    if let roadSnappedLocationProvider = mapView.roadSnappedLocationProvider {
      roadSnappedLocationProvider.add(ridesharingDriverAPI.vehicleReporter)
      roadSnappedLocationProvider.startUpdatingLocation()
    }
  }
}

Objective-C

/*
 * 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 have implemented the sample code up to this step.
  [_mapView.roadSnappedLocationProvider addListener:ridesharingDriverAPI.vehicleReporter];
  [_mapView.roadSnappedLocationProvider startUpdatingLocation];
}

@end

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

位置情報の追跡を有効にするには、アプリの GMTDVehicleReporterlocationTrackingEnabledYES に設定します。GMTDVehicleReporter は、位置情報の更新を自動的に送信します。これらのサービスが一致し、車両がルートに割り当てられると、GMSNavigator がナビゲーション モードのとき(setDestinations: で目的地が設定されている場合)、GMTDVehicleReporter はルートの更新を自動的に送信します。

ルートの更新中に設定されたルートは、ナビゲーション セッション中にドライバーが移動したのと同じルートになります。したがって、ジャーニー共有を適切に機能させるには、setDestinations を介して設定された地点が Fleet Engine バックエンドに設定された宛先と一致する必要があります。

locationTrackingEnabledYES に設定されている場合、ルートと車両の更新は、locationUpdateInterval に設定された値に基づいて定期的に Fleet Engine バックエンドに送信されます。locationTrackingEnabledNO に設定されている場合、更新は停止し、車両の最終更新リクエストが Fleet Engine バックエンドに送信され、車両の状態が GMTDVehicleStateOffline に設定されます。locationTrackingEnabledNO に設定されている場合の障害の処理に関する特別な考慮事項については、updateVehicleState をご覧ください。

次の例では、位置情報トラッキングを有効にしています。

Swift

import GoogleRidesharingDriver

private let providerID = "INSERT_YOUR_PROVIDER_ID"

class SampleViewController: UIViewController, GMTDVehicleReporterListener {
  private let mapView: GMSMapView

  override func viewDidLoad() {
    // Assumes you have implemented the sample code up to this step.
    ridesharingDriverAPI.vehicleReporter.locationTrackingEnabled = true
  }
}

Objective-C

/*
 * 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 have implemented the sample code up to this step.
  ridesharingDriverAPI.vehicleReporter.locationTrackingEnabled = YES;
}

@end

レポート間隔はデフォルトで 10 秒ですが、レポート間隔は locationUpdateInterval で変更できます。サポートされている最小更新間隔は 1 秒です。サポートされる最大更新間隔は 60 秒です。更新頻度を上げると、リクエストとエラーが遅くなる可能性があります。

車両の状態を更新する

次の例は、車両の状態を ONLINE に設定する方法を示しています。詳しくは、updateVehicleState をご覧ください。

Swift

import GoogleRidesharingDriver

private let providerID = "INSERT_YOUR_PROVIDER_ID"

class SampleViewController: UIViewController, GMTDVehicleReporterListener {
  private let mapView: GMSMapView

  override func viewDidLoad() {
    // Assumes you have implemented the sample code up to this step.
    ridesharingDriverAPI.vehicleReporter.update(.online)
  }
}

Objective-C

#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 have implemented the sample code up to this step.
  [ridesharingDriverAPI.vehicleReporter
                                   updateVehicleState:GMTDVehicleStateOnline];
}

@end

位置情報の更新を無効にして、車をオフラインにする

アプリでは、更新を無効にして車両をオフラインにできます。たとえば、ドライバーのシフトが終了したら、アプリは locationTrackingEnabledNO に設定できます。更新を無効にすると、Fleet Engine バックエンドで車両のステータスも OFFLINE に設定されます。

Swift

vehicleReporter.locationTrackingEnabled = false

Objective-C

_vehicleReporter.locationTrackingEnabled = NO;