在 iOS 设备上跟踪行程

请选择平台: Android iOS JavaScript

跟踪行程时,您的消费者应用会向消费者显示相应车辆的位置。为此,您的应用需要开始跟踪行程、更新行程进度,并在行程结束时停止跟踪行程。

本文档介绍了该过程的运作方式。

开始跟踪行程

如需开始跟踪行程,请按以下步骤操作:

  • ViewController 收集所有用户输入,例如下车点和上车点。

  • 创建一个新的 ViewController 以直接开始跟踪行程。

以下示例展示了如何在视图加载后立即开始跟踪行程。

Swift

/*
 * MapViewController.swift
 */
override func viewDidLoad() {
  super.viewDidLoad()
  ...
  self.mapView = GMTCMapView(frame: UIScreen.main.bounds)
  self.mapView.delegate = self
  self.view.addSubview(self.mapView)
}

func mapViewDidInitializeCustomerState(_: GMTCMapView) {
  self.mapView.pickupLocation = self.selectedPickupLocation
  self.mapView.dropoffLocation = self.selectedDropoffLocation

  self.startConsumerMatchWithLocations(
    pickupLocation: self.mapView.pickupLocation!,
    dropoffLocation: self.mapView.dropoffLocation!
  ) { [weak self] (tripName, error) in
    guard let strongSelf = self else { return }
    if error != nil {
      // print error message.
      return
    }
    let tripService = GMTCServices.shared().tripService
    // Create a tripModel instance for listening the update of the trip
    // specified by this trip name.
    let tripModel = tripService.tripModel(forTripName: tripName)
    // Create a journeySharingSession instance based on the tripModel
    let journeySharingSession = GMTCJourneySharingSession(tripModel: tripModel)
    // Add the journeySharingSession instance on the mapView for UI updating.
    strongSelf.mapView.show(journeySharingSession)
    // Register for the trip update events.
    tripModel.register(strongSelf)

    strongSelf.currentTripModel = tripModel
    strongSelf.currentJourneySharingSession = journeySharingSession
    strongSelf.hideLoadingView()
  }

  self.showLoadingView()
}

Objective-C

/*
 * MapViewController.m
 */
- (void)viewDidLoad {
  [super viewDidLoad];
  ...
  self.mapView = [[GMTCMapView alloc] initWithFrame:CGRectZero];
  self.mapView.delegate = self;
  [self.view addSubview:self.mapView];
}

// Handle the callback when the GMTCMapView did initialized.
- (void)mapViewDidInitializeCustomerState:(GMTCMapView *)mapview {
  self.mapView.pickupLocation = self.selectedPickupLocation;
  self.mapView.dropoffLocation = self.selectedDropoffLocation;

  __weak __typeof(self) weakSelf = self;
  [self startTripBookingWithPickupLocation:self.selectedPickupLocation
                           dropoffLocation:self.selectedDropoffLocation
                                completion:^(NSString *tripName, NSError *error) {
                                  __typeof(self) strongSelf = weakSelf;
                                  GMTCTripService *tripService = [GMTCServices sharedServices].tripService;
                                  // Create a tripModel instance for listening to updates to the trip specified by this trip name.
                                  GMTCTripModel *tripModel = [tripService tripModelForTripName:tripName];
                                  // Create a journeySharingSession instance based on the tripModel.
                                  GMTCJourneySharingSession *journeySharingSession =
                                    [[GMTCJourneySharingSession alloc] initWithTripModel:tripModel];
                                  // Add the journeySharingSession instance on the mapView for updating the UI.
                                  [strongSelf.mapView showMapViewSession:journeySharingSession];
                                  // Register for trip update events.
                                  [tripModel registerSubscriber:self];

                                  strongSelf.currentTripModel = tripModel;
                                  strongSelf.currentJourneySharingSession = journeySharingSession;
                                  [strongSelf hideLoadingView];
                                }];
    [self showLoadingView];
}

停止关注行程

行程结束或取消后,您会停止跟踪该行程。以下示例展示了如何停止分享正在进行的行程。

Swift

/*
 * MapViewController.swift
 */
func cancelCurrentActiveTrip() {
  // Stop the tripModel
  self.currentTripModel.unregisterSubscriber(self)

  // Remove the journey sharing session from the mapView's UI stack.
  self.mapView.hide(journeySharingSession)
}

Objective-C

/*
 * MapViewController.m
 */
- (void)cancelCurrentActiveTrip {
  // Stop the tripModel
  [self.currentTripModel unregisterSubscriber:self];

  // Remove the journey sharing session from the mapView's UI stack.
  [self.mapView hideMapViewSession:journeySharingSession];
}

更新行程进度

在行程中,您可以按以下方式管理行程进度:

当行程完成或取消时,停止监听更新。如需查看示例,请参阅停止监听更新示例

示例:开始监听更新

以下示例展示了如何注册 tripModel 回调。

Swift

/*
 * MapViewController.swift
 */
override func viewDidLoad() {
  super.viewDidLoad()
  // Register for trip update events.
  self.currentTripModel.register(self)
}

Objective-C

/*
 * MapViewController.m
 */
- (void)viewDidLoad {
  [super viewDidLoad];
  // Register for trip update events.
  [self.currentTripModel registerSubscriber:self];
  ...
}

停止监听更新示例

以下示例展示了如何取消注册 tripModel 回调。

Swift

/*
 * MapViewController.swift
 */
deinit {
  self.currentTripModel.unregisterSubscriber(self)
}

Objective-C

/*
 * MapViewController.m
 */
- (void)dealloc {
  [self.currentTripModel unregisterSubscriber:self];
  ...
}

处理行程更新示例

以下示例展示了如何实现 GMTCTripModelSubscriber 协议,以便在行程状态更新时处理回调。

Swift

/*
 * MapViewController.swift
 */
func tripModel(_: GMTCTripModel, didUpdate trip: GMTSTrip?, updatedPropertyFields: GMTSTripPropertyFields) {
  // Update the UI with the new `trip` data.
  self.updateUI(with: trip)
}

func tripModel(_: GMTCTripModel, didUpdate tripStatus: GMTSTripStatus) {
  // Handle trip status did change.
}

func tripModel(_: GMTCTripModel, didUpdateActiveRouteRemainingDistance activeRouteRemainingDistance: Int32) {
  // Handle remaining distance of active route did update.
}

func tripModel(_: GMTCTripModel, didUpdateActiveRoute activeRoute: [GMTSLatLng]?) {
  // Handle trip active route did update.
}

func tripModel(_: GMTCTripModel, didUpdate vehicleLocation: GMTSVehicleLocation?) {
  // Handle vehicle location did update.
}

func tripModel(_: GMTCTripModel, didUpdatePickupLocation pickupLocation: GMTSTerminalLocation?) {
  // Handle pickup location did update.
}

func tripModel(_: GMTCTripModel, didUpdateDropoffLocation dropoffLocation: GMTSTerminalLocation?) {
  // Handle drop off location did update.
}

func tripModel(_: GMTCTripModel, didUpdatePickupETA pickupETA: TimeInterval) {
  // Handle the pickup ETA did update.
}

func tripModel(_: GMTCTripModel, didUpdateDropoffETA dropoffETA: TimeInterval) {
  // Handle the drop off ETA did update.
}

func tripModel(_: GMTCTripModel, didUpdateRemaining remainingWaypoints: [GMTSTripWaypoint]?) {
  // Handle updates to the pickup, dropoff or intermediate destinations of the trip.
}

func tripModel(_: GMTCTripModel, didFailUpdateTripWithError error: Error?) {
  // Handle the error.
}

func tripModel(_: GMTCTripModel, didUpdateIntermediateDestinations intermediateDestinations: [GMTSTerminalLocation]?) {
  // Handle the intermediate destinations being updated.
}

func tripModel(_: GMTCTripModel, didUpdateActiveRouteTraffic activeRouteTraffic: GMTSTrafficData?) {
  // Handle trip active route traffic being updated.
}

Objective-C

/*
 * MapViewController.m
 */
#pragma mark - GMTCTripModelSubscriber implementation

- (void)tripModel:(GMTCTripModel *)tripModel
            didUpdateTrip:(nullable GMTSTrip *)trip
    updatedPropertyFields:(enum GMTSTripPropertyFields)updatedPropertyFields {
  // Update the UI with the new `trip` data.
  [self updateUIWithTrip:trip];
  ...
}

- (void)tripModel:(GMTCTripModel *)tripModel didUpdateTripStatus:(enum GMTSTripStatus)tripStatus {
  // Handle trip status did change.
}

- (void)tripModel:(GMTCTripModel *)tripModel
    didUpdateActiveRouteRemainingDistance:(int32_t)activeRouteRemainingDistance {
   // Handle remaining distance of active route did update.
}

- (void)tripModel:(GMTCTripModel *)tripModel
    didUpdateActiveRoute:(nullable NSArray<GMTSLatLng *> *)activeRoute {
  // Handle trip active route did update.
}

- (void)tripModel:(GMTCTripModel *)tripModel
    didUpdateVehicleLocation:(nullable GMTSVehicleLocation *)vehicleLocation {
  // Handle vehicle location did update.
}

- (void)tripModel:(GMTCTripModel *)tripModel
    didUpdatePickupLocation:(nullable GMTSTerminalLocation *)pickupLocation {
  // Handle pickup location did update.
}

- (void)tripModel:(GMTCTripModel *)tripModel
    didUpdateDropoffLocation:(nullable GMTSTerminalLocation *)dropoffLocation {
  // Handle drop off location did update.
}

- (void)tripModel:(GMTCTripModel *)tripModel didUpdatePickupETA:(NSTimeInterval)pickupETA {
  // Handle the pickup ETA did update.
}

- (void)tripModel:(GMTCTripModel *)tripModel
    didUpdateRemainingWaypoints:(nullable NSArray<GMTSTripWaypoint *> *)remainingWaypoints {
  // Handle updates to the pickup, dropoff or intermediate destinations of the trip.
}

- (void)tripModel:(GMTCTripModel *)tripModel didUpdateDropoffETA:(NSTimeInterval)dropoffETA {
  // Handle the drop off ETA did update.
}

- (void)tripModel:(GMTCTripModel *)tripModel didFailUpdateTripWithError:(nullable NSError *)error {
  // Handle the error.
}

- (void)tripModel:(GMTCTripModel *)tripModel
    didUpdateIntermediateDestinations:
        (nullable NSArray<GMTSTerminalLocation *> *)intermediateDestinations {
  // Handle the intermediate destinations being updated.
}

- (void)tripModel:(GMTCTripModel *)tripModel
    didUpdateActiveRouteTraffic:(nullable GMTSTrafficData *)activeRouteTraffic {
  // Handle trip active route traffic being updated.
}

处理行程错误

如果您订阅了 tripModel 并发生错误,则可以通过实现委托方法 tripModel(_:didFailUpdateTripWithError:) 来获取 tripModel 的回调。错误消息遵循 Google Cloud 错误标准。如需详细了解错误消息定义和所有错误代码,请参阅 Google Cloud 错误文档

以下是行程监控期间可能会发生的一些常见错误:

HTTP RPC 说明
400 INVALID_ARGUMENT 客户端指定的的行程名称无效。行程名称必须采用 providers/{provider_id}/trips/{trip_id} 格式。provider_id 必须是服务提供商拥有的 Cloud 项目的 ID。
401 UNAUTHENTICATED 如果没有有效的身份验证凭据,您会收到此错误。 例如,如果 JWT 令牌已签名但没有行程 ID,或者 JWT 令牌已过期。
403 PERMISSION_DENIED 如果客户端没有足够的权限(例如,具有消费者角色的用户尝试调用 updateTrip),如果 JWT 令牌无效,或者客户端项目未启用该 API,您会收到此错误。 JWT 令牌可能缺失,或者令牌的签名所用的行程 ID 与请求的行程 ID 不匹配。
429 RESOURCE_EXHAUSTED 资源配额为零或流量速率超出限制。
503 UNAVAILABLE 服务不可用。通常是服务器已关闭。
504 DEADLINE_EXCEEDED 超出请求时限。仅当调用方设置的时限比方法的默认时限短(即请求的时限不足以让服务器处理请求)并且请求未在时限范围内完成时,才会发生此错误。

处理使用方 SDK 错误

使用方 SDK 会使用回调机制将行程更新错误发送到使用方应用。回调参数是平台专用返回类型(在 Android 上为 TripUpdateError,在 iOS 上为 NSError)。

提取状态代码

传递给回调的错误通常是 gRPC 错误,您还可以从中提取状态代码形式的其他信息。如需查看状态代码的完整列表,请参阅状态代码及其在 gRPC 中的使用

Swift

NSError 会在 tripModel(_:didFailUpdateTripWithError:) 中回调。

// Called when there is a trip update error.
func tripModel(_ tripModel: GMTCTripModel, didFailUpdateTripWithError error: Error?) {
  // Check to see if the error comes from gRPC.
  if let error = error as NSError?, error.domain == "io.grpc" {
    let gRPCErrorCode = error.code
    ...
  }
}

Objective-C

NSError 会在 tripModel:didFailUpdateTripWithError: 中回调。

// Called when there is a trip update error.
- (void)tripModel:(GMTCTripModel *)tripModel didFailUpdateTripWithError:(NSError *)error {
  // Check to see if the error comes from gRPC.
  if ([error.domain isEqualToString:@"io.grpc"]) {
    NSInteger gRPCErrorCode = error.code;
    ...
  }
}

解读状态代码

状态代码涵盖两种错误:服务器和网络相关错误,以及客户端错误。

服务器和网络错误

以下状态代码表示网络或服务器错误,您无需执行任何操作即可解决这些错误。而 Consumer SDK 会自动从中恢复。

状态代码说明
ABORTED 服务器停止发送响应。这通常是由服务器问题导致的。
已取消 服务器终止了外发响应。当
应用被发送到后台,或
消费者应用中的状态发生变化时,通常会发生这种情况。
INTERRUPTED
DEADLINE_EXCEEDED 服务器的响应时间过长。
UNAVAILABLE 服务器不可用。这通常是由网络问题导致的。

客户端错误

以下状态代码适用于客户端错误,您必须采取措施来解决这些错误。消费者 SDK 会继续重新尝试刷新行程,直到您结束行程分享,但只有在您采取行动后才会恢复。

状态代码说明
INVALID_ARGUMENT 消费者应用指定的的行程名称无效;行程名称必须采用 providers/{provider_id}/trips/{trip_id} 格式。
NOT_FOUND 此行程从未创建。
PERMISSION_DENIED 消费者应用权限不足。在以下情况下,会发生此错误:
  • 消费者应用没有权限
  • Google Cloud 控制台中的项目未启用使用方 SDK。
  • JWT 令牌缺失或无效。
  • JWT 令牌使用与请求的行程 ID 不匹配的行程 ID 进行签名。
RESOURCE_EXHAUSTED 资源配额为零,或者流量速率超过速度限制。
UNAUTHENTICATED 由于 JWT 令牌无效,请求未通过身份验证。如果 JWT 令牌签名时没有行程 ID,或者 JWT 令牌已过期,就会发生此错误。