您可以使用 Driver SDK 为“行程和订单进度”应用提供增强的导航和跟踪功能。Driver SDK 可为按需行程和配送解决方案舰队引擎提供车辆位置信息和任务更新。
Driver SDK 可让 Fleet Engine 服务和自定义服务了解车辆的位置和状态。例如,车辆可以是 ONLINE
或 OFFLINE
,并且车辆位置会随着行程的进行而变化。
最低系统要求
前提条件
本指南假定您的应用已实现 Navigation SDK,并且 Fleet Engine 后端已设置且可用。不过,示例代码提供了有关如何设置 Navigation SDK 的示例。
您还必须在 Google Cloud 项目中启用 Maps SDK for iOS 并获取 API 密钥。
项目配置
您可以使用 CocoaPods 配置驱动程序 SDK。
使用 CocoaPods
如需使用 CocoaPods 配置驱动程序 SDK,您需要以下各项:
- CocoaPods 工具:如需安装此工具,请打开终端并运行以下命令。
shell sudo gem install cocoapods
如需了解详情,请参阅 CocoaPods 入门指南。
为 Driver 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 安装命令。这将安装 Podfile 中指定的 API 及其可能具有的任何依赖项。
pod install
关闭 Xcode,然后打开(双击)您项目的 .xcworkspace 文件以启动 Xcode。从此刻开始,您必须使用 .xcworkspace 文件打开项目。
Alpha/Beta 版 SDK 版本
如需配置适用于 iOS 的 Alpha 版或 Beta 版驱动程序 SDK,您需要以下各项:
CocoaPods 工具:如需安装此工具,请打开终端并运行以下命令。
sudo gem install cocoapods
如需了解详情,请参阅 CocoaPods 入门指南。
您的开发帐号在 Google 访问权限列表中。Alpha 版和 Beta 版 SDK 的 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 安装命令。此命令会安装 Podfile 中指定的 API 及其可能具有的任何依赖项。
pod install
关闭 Xcode,然后打开(双击)您项目的 .xcworkspace 文件以启动 Xcode。从此刻开始,您必须使用 .xcworkspace 文件打开项目。
实现授权和身份验证
当驱动程序应用生成更新并将其发送到 Fleet Engine 后端时,请求必须包含有效的访问令牌。为了对这些请求进行授权和身份验证,驱动程序 SDK 会按照 GMTDAuthorization
协议调用对象。该对象负责提供所需的访问令牌。
作为应用开发者,您可以选择令牌的生成方式。您的实现应能够执行以下操作:
- 从 HTTPS 服务器获取访问令牌(可能采用 JSON 格式)。
- 解析并缓存令牌。
- 请在令牌过期时刷新令牌。
如需详细了解 Fleet Engine 服务器预期的令牌,请参阅创建 JSON Web 令牌 (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
为了向 Driver 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;