您可以運用 Driver SDK 為「行程」和「訂單進度」應用程式提供強化的導航和追蹤功能。Driver SDK 會針對隨選乘車和配送解決方案機群提供車輛位置和工作更新。
驅動程式 SDK 可讓 Fleet Engine 服務和您的自訂服務掌握車輛的位置和狀態。舉例來說,車輛可以是 ONLINE
或 OFFLINE
,而車輛位置會隨著行程進展而變更。
基本系統需求
必要條件
本指南假設應用程式已經實作 Navigation SDK,且 Fleet Engine 後端已設定完成且可供使用。不過,範例程式碼會提供如何設定 Navigation SDK 的範例。
您也必須在 Google Cloud 專案中啟用 Maps SDK for iOS,然後取得 API 金鑰。
專案設定
您可以使用 CocoaPods 設定驅動程式 SDK。
CocoaPods
如要使用 CocoaPods 設定驅動程式 SDK,您需要下列項目:
- CocoaPods 工具:如要安裝這項工具,請開啟終端機並執行下列指令。
sudo gem install cocoapods
詳情請參閱 CocoaPods 入門指南。
- 為驅動程式 SDK 建立 Podfile,並用來安裝 API 及其依附元件:在專案目錄中建立名為 Podfile 的檔案。這個檔案定義了專案的依附元件。請編輯 Podfile 並新增依附元件。以下是包含依附元件的範例:
source "https://github.com/CocoaPods/Specs.git"
target 'YOUR_APPLICATION_TARGET_NAME_HERE' do
pod 'GoogleRidesharingDriver'
end
以下範例包含驅動程式 SDK 的 Alpha 和 Beta 版 Pod 做為依附元件:
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
- 儲存 Podfile。開啟終端機,並前往包含 Podfile 的目錄:
cd <path-to-project>
- 執行 pod install 指令。此操作會安裝 Podfile 中指定的 API,以及這些 API 的所有依附元件。
pod install
- 關閉 Xcode,然後開啟 (按兩下) 專案的 .xcworkspace 檔案以啟動 Xcode。從現在起,您必須使用 .xcworkspace 檔案來開啟專案。
手動安裝程式庫
XCFramework 是可用來安裝驅動程式 SDK 的二進位套件。您可以在多個平台使用這個套件,包括使用 Apple 晶片的電腦。本指南說明如何手動將包含驅動程式 SDK 的 XCFramework 新增至專案,並在 Xcode 中調整建構設定。
下載 SDK 二進位檔和資源:
將壓縮的檔案解壓縮,以存取 XCFramework 和資源。
啟動 Xcode,然後開啟現有專案或建立新專案。如果您是 iOS 新手,請建立新專案並選取 iOS 應用程式範本。
如果專案群組還沒有架構群組,請建立群組。
將
gRPCCertificates.bundle
檔案拖曳至 Xcode 專案的頂層目錄。系統提示時,視需要選取「複製項目」。如要安裝驅動程式 SDK,請將
GoogleRidesharingDriver.xcframework
檔案拖曳至「Frameworks、Library 和 Embedded Content」下方的專案。系統提示時,請視需要選取「Copy items」(複製項目)。將下載的
GoogleRidesharingDriver.bundle
拖曳至 Xcode 專案的頂層目錄。當系統提示時,請選取Copy items if needed
。從「Project Navigator」中選取專案,並選擇應用程式的目標。
開啟「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
選擇您的專案 (而不是特定目標),然後開啟「Build Settings」分頁。在「Other Linker Flags」部分中,新增偵錯和發布版本的
-ObjC
。如果沒有看到這些設定,請將建構設定列中的篩選器從「Basic」變更為「All」。
Alpha/Beta 版 SDK 版本
如要設定 iOS 適用的 Driver SDK Alpha 或 Beta 版,您需要下列項目:
- CocoaPods 工具:如要安裝這項工具,請開啟終端機並執行下列指令。
sudo gem install cocoapods
詳情請參閱 CocoaPods 入門指南。
- Google 存取清單上的開發帳戶。SDK 的 Alpha 和 Beta 版 Pod 存放區並非公開來源。如要存取這些版本,請與 Google 客戶工程師聯絡。工程師會將您的開發帳戶新增至存取清單,然後設定 Cookie 進行驗證。
專案加入存取清單後,您就可以存取 Pod。
- 為 iOS 版 Delivery SDK 建立 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
- 儲存 Podfile。開啟終端機,並前往包含 Podfile 的目錄:
cd <path-to-project>
- 執行 pod install 指令。這個指令會安裝 Podfile 中指定的 API,以及這些 API 可能擁有的任何依附元件。
pod install
- 關閉 Xcode,然後開啟 (按兩下) 專案的 .xcworkspace 檔案以啟動 Xcode。從現在起,您必須使用 .xcworkspace 檔案來開啟專案。
實作授權和驗證
當驅動程式應用程式產生更新並傳送至 Fleet Engine 後端時,要求必須包含有效的存取權杖。為了授權及驗證這些要求,驅動程式 SDK 會呼叫符合 GMTDAuthorization
通訊協定的物件。物件負責提供必要的存取憑證。
應用程式開發人員可以選擇產生權杖的方式。您的實作應能夠執行下列操作:
- 從 HTTPS 伺服器擷取可能是 JSON 格式的存取權杖。
- 剖析並快取權杖。
- 請在權杖過期時重新整理。
如要進一步瞭解 Fleet Engine 伺服器預期的權杖,請參閱建立 JSON Web Token (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、driverContext 和 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
為 true
時,GMTDVehicleReporter
會定期更新車輛。如要回應這類定期更新,任何物件只要符合 GMTDVehicleReporterListener
通訊協定,即可訂閱 GMTDVehicleReporter
事件。
您可以處理下列事件:
vehicleReporter(_:didSucceed:)
向驅動程式應用程式告知後端服務已成功收到車輛位置和狀態更新。
vehicleReporter(_:didFail: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
將 GMTDVehicleReporter 新增為 GMSRoadSnappedLocationProvider 的監聽器
如要為驅動程式 SDK 提供位置更新,GMTDVehicleReporter
必須設為 GMSRoadSnappedLocationProvider
的事件監聽器。
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
啟用位置追蹤功能
如要啟用位置追蹤,應用程式可以在 GMTDVehicleReporter
上將 locationTrackingEnabled
設為 true
。GMTDVehicleReporter
會自動傳送位置更新通知。當服務比對相符並將車輛指派給行程後,當 GMSNavigator
處於導航模式 (目的地設為 setDestinations
時),GMTDVehicleReporter
會自動傳送路線更新。
更新行程期間設定的路線,與駕駛人在導航期間前往的路線相同。因此,為了正確分享歷程,透過 setDestinations
設定的路線點應與 Fleet Engine 後端中設定的目的地相符。
如果將 locationTrackingEnabled
設為 true
,系統會根據為 locationUpdateInterval
的設定值,定期將行程和車輛更新傳送至 Fleet Engine 後端。如果 locationTrackingEnabled
設為 false
,系統就會停止更新,並將最終車輛更新要求傳送至 Fleet Engine 後端,以將車輛狀態設為 GMTDVehicleState.offline
。如要瞭解在 locationTrackingEnabled
設為 false
時處理失敗的特別注意事項,請參閱 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
變更。支援的更新間隔下限為 5 秒。支援的更新間隔上限為 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
如果遮罩為空白,就會發生 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
停用位置更新功能,並下車
應用程式可以停用更新功能,並下車。舉例來說,當駕駛人輪班結束時,應用程式可將 locationTrackingEnabled
設為 false
。如果停用更新,車輛在 Fleet Engine 後端上的車輛狀態也會設為 OFFLINE
。
Swift
vehicleReporter.locationTrackingEnabled = false
Objective-C
_vehicleReporter.locationTrackingEnabled = NO;