開始使用 iOS 版 Driver SDK

Driver SDK 是整合至驅動程式應用程式的程式庫,負責更新 Fleet Engine 中的驅動程式位置、路線、剩餘距離和預計到達時間。此外,它也整合了 Navigation SDK,為駕駛人提供即時路線導航指示。

基本系統需求

  • 行動裝置必須搭載 iOS 14 以上版本。
  • Xcode 15 以上版本。
  • 先備知識

    本指南假設應用程式已實作 Navigation SDK,且 Fleet Engine 後端已設定完成並可供使用。不過,範例程式碼提供了設定 Navigation SDK 的範例。

    您也必須在 Google Cloud 專案中啟用 Maps SDK for iOS,然後取得 API 金鑰

    取得使用權限

    如果您是 Google Workspace 客戶,請建立 Workspace 群組 (例如在新手上路期間的 google-maps-platform-sdk-users@workspacedomain.com),並將名稱提供給 Google。此為建議做法。您的 Workspace 群組會加入許可清單,並允許存取正確的 CocoaPods 存放區。確認需要存取權的使用者電子郵件和服務帳戶電子郵件地址都列在這份清單中。

    如果貴機構無法建立 Workspace 群組,請將需要存取這些構件的使用者及服務帳戶電子郵件清單傳送給 Google。

    本機開發

    要進行本機開發,只需透過 Cloud SDK 登入即可。

    gcloud

    gcloud auth login
    

    用來登入的電子郵件地址必須是 Workspace 群組的成員。

    自動化 (建構系統或持續整合)

    請根據最佳做法設定自動化主機:

    • 如果您的程序是在 Google Cloud 環境中執行,請使用自動憑證偵測功能。

    • 否則,請將服務帳戶金鑰檔案儲存在主機檔案系統的安全位置,並適當設定 GOOGLE_APPLICATION_CREDENTIALS 環境變數。

    與憑證相關聯的服務帳戶電子郵件地址必須是 Workspace Goup 的成員。

    專案設定

    您可以使用 CocoaPods 設定驅動程式 SDK。

    使用 CocoaPods

    如要使用 CocoaPods 設定 Driver SDK,您必須備妥下列項目:

    • CocoaPods 工具:如要安裝這項工具,請開啟終端機並執行下列指令。shell sudo gem install cocoapods 詳情請參閱 CocoaPods 入門指南
    1. 為驅動程式 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 安裝指令。這項操作會安裝 Podfile 中指定的 API,以及這些 API 的所有依附元件。

      pod install
      
    4. 關閉 Xcode,然後開啟 (按兩下) 專案的 .xcworkspace 檔案來啟動 Xcode。從現在起,您必須使用 .xcworkspace 檔案來開啟專案。

    Alpha/Beta 版 SDK 版本

    如要設定 iOS 版驅動程式 SDK 的 Alpha 或 Beta 版本,您需要下列項目:

    • CocoaPods 工具:如要安裝這項工具,請開啟終端機並執行下列指令。

      sudo gem install cocoapods
      

      詳情請參閱 CocoaPods 入門指南

    • Google 存取清單上的開發人員帳戶。SDK Alpha 和 Beta 版的 Pod 存放區並非公開來源。如要存取這些版本,請與 Google 客戶工程師聯絡。工程師會將您的開發帳戶新增至存取清單,然後設定用於驗證的 Cookie

    當專案列入存取清單後,您就可以存取 Pod。

    1. 建立 iOS 專用驅動程式 SDK 的 Podfile,並使用這個 Pod 安裝 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 安裝指令。這項操作會安裝 Podfile 中指定的 API,以及這些 API 的所有依附元件。

      pod install
      
    4. 關閉 Xcode,然後開啟 (按兩下) 專案的 .xcworkspace 檔案來啟動 Xcode。從現在起,您必須使用 .xcworkspace 檔案來開啟專案。

    安裝 XCFramework

    XCFramework 是用於安裝驅動程式 SDK 的二進位套件。這個套件可用於多個平台,包括使用 M1 晶片組的機器。本指南說明如何手動將含有驅動程式 SDK 的 XCFramework 新增至專案,並在 Xcode 中進行建構設定。

    下載 SDK 二進位檔和資源:

    1. 將壓縮的檔案解壓縮,以存取 XCFramework 和資源。

    2. 啟動 Xcode,然後開啟現有專案,或建立新專案。如果您是 iOS 新手,請建立新專案並選取 iOS 應用程式範本。

    3. 如果沒有架構群組,請在專案群組下建立。

    4. 將下載的 gRPCCertificates.bundle 檔案拖曳至 Xcode 專案的頂層目錄。畫面上出現提示時,請視需要選取「複製項目」。

    5. 如要安裝驅動程式 SDK,請將 GoogleRidesharingDriver.xcframework 檔案拖曳到專案「Frameworks、Library 和 Embedded Content」下方。畫面上出現提示時,請視需要選取「複製項目」。

    6. 將下載的 GoogleRidesharingDriver.bundle 拖曳至 Xcode 專案的頂層目錄。系統提示時,請選取 Copy items if needed

    7. Select your project from the Project Navigator, and choose your application's target.

    8. 開啟「建構階段」分頁,然後在連結二進位檔和程式庫的連結二進位檔中,加入下列架構和程式庫 (如果尚未加入):

      • 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。如果您沒有看到這些設定,請將「Build Settings」列中的篩選器從「Basic」變更為「All」

    實作授權和驗證

    當驅動程式應用程式產生更新並傳送至 Fleet Engine 後端時,要求必須包含有效的存取權杖。為了授權及驗證這些要求,驅動程式 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、driveContext 和 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 事件

    如果 locationTrackingEnabled 為「是」,GMTDDeliveryVehicleReporter 會定期更新車輛資料。為回應這些定期更新,任何物件皆可依照 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
    

    啟用位置追蹤功能

    如要啟用位置追蹤功能,應用程式可以將 GMTDDeliveryVehicleReporter 上的 locationTrackingEnabled 設為 YES。則 GMTDDeliveryVehicleReporter 會 = 自動傳送位置更新資訊。如果 GMSNavigator 處於導航模式 (目的地透過 setDestinations 設定),且 locationTrackingEnabled 設為 YESGMTDDeliveryVehicleReporter 也會自動傳送路線和預計到達時間。

    這些更新期間設定的路徑,與駕駛人在導航工作階段中前往的路徑相同。因此,為了讓運送追蹤正常運作,透過 -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 秒。頻繁的更新可能會導致要求和錯誤變慢。

    停用位置更新通知

    您的應用程式可以停用車輛的位置更新通知。舉例來說,當駕駛人的位移結束時,應用程式可以將 locationTrackingEnabled 設為 NO

      _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