מעקב אחר נסיעה ב-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];
  ...
}

דוגמה להפסקת ההאזנה לעדכונים

בדוגמה הבאה אפשר לראות איך לבטל את הרישום של הקריאה החוזרת (callback) tripModel.

Swift

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

Objective-C

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

דוגמה לטיפול בעדכונים לגבי נסיעה

בדוגמה הבאה אפשר לראות איך מטמיעים את הפרוטוקול GMTCTripModelSubscriber כדי לטפל בקריאות חוזרות (callbacks) כשמצב הנסיעה מתעדכן.

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 ומתרחשת שגיאה, תוכלו לקבל את הקריאה החוזרת (callback) של tripModel על ידי הטמעה של שיטת הנציג tripModel(_:didFailUpdateTripWithError:). הודעות השגיאה הן בהתאם לתקן של Google Cloud Error. הגדרות מפורטות של הודעות שגיאה וכל קודי השגיאה מפורטים במאמרי העזרה בנושא שגיאות ב-Google Cloud.

הנה כמה שגיאות נפוצות שיכולות להתרחש במהלך מעקב אחר נסיעות:

HTTP הכנסה לקליק תיאור
400 INVALID_ARGUMENT הלקוח ציין שם נסיעה לא חוקי. שם הנסיעה חייב להיות בפורמט providers/{provider_id}/trips/{trip_id}. הערך של provider_id צריך להיות מזהה פרויקט Cloud שבבעלות ספק השירות.
401 UNAUTHENTICATED השגיאה הזו מופיעה אם אין פרטי כניסה תקפים לאימות. לדוגמה, אם אסימון ה-JWT נחתם בלי מזהה נסיעה או אם פג תוקף אסימון ה-JWT.
403 PERMISSION_DENIED השגיאה הזו מופיעה אם ללקוח אין מספיק הרשאות (לדוגמה, משתמש עם תפקיד צרכן מנסה לקרוא ל-updateTrip), אם אסימון ה-JWT לא תקין או אם ה-API לא מופעל בפרויקט של הלקוח. יכול להיות שאסימון ה-JWT חסר או שהאסימון חתום עם מזהה נסיעה שלא תואם למזהה הנסיעה המבוקש.
429 RESOURCE_EXHAUSTED המכסה של המשאב היא אפס או שקצב התנועה חורג מהמגבלה.
503 UNAVAILABLE השירות לא זמין. בדרך כלל השרת מושבת.
504 DEADLINE_EXCEEDED המועד האחרון לבקשה חלף. השגיאה הזו מתרחשת רק אם המתקשר מגדיר מועד אחרון שהוא קצר יותר מהמועד האחרון שמוגדר כברירת מחדל לשיטה (כלומר, המועד האחרון שנדרש לא מספיק לשרת כדי לעבד את הבקשה), והבקשה לא הסתיימה לפני המועד האחרון.

טיפול בשגיאות ב-Consumer SDK

ה-SDK לצרכנים שולח שגיאות בעדכוני נסיעות לאפליקציה למשתמשים באמצעות מנגנון של קריאה חוזרת (callback). פרמטר הקריאה החוזרת הוא סוג החזרה ספציפי לפלטפורמה (‎ TripUpdateError‎ ב-Android ו-‎ NSError‎ ב-iOS).

חילוץ קודי סטטוס

השגיאות שמועברות לקריאה החוזרת הן בדרך כלל שגיאות 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;
    ...
  }
}

הסבר על קודי סטטוס

קודי הסטטוסים כוללים שני סוגים של שגיאות: שגיאות שקשורות לשרת ולרשת, ושגיאות בצד הלקוח.

שגיאות בשרת וברשת

קודי הסטטוס הבאים מציינים שגיאות ברשת או בשרת, ולא צריך לבצע פעולה כלשהי כדי לפתור אותן. ה-SDK לצרכנים מתאושש מהם באופן אוטומטי.

קוד סטטוסתיאור
בוטלה השרת הפסיק לשלוח את התשובה. בדרך כלל, הסיבה לכך היא בעיה בשרת.
בוטלה השרת סיים את התגובה היוצאת. זה קורה בדרך כלל כש

האפליקציה מועברת לרקע, או כשמתרחש שינוי במצב באפליקציה למשתמשים.
ההורדה הופסקה
DEADLINE_EXCEEDED זמן התגובה של השרת היה ארוך מדי.
UNAVAILABLE השרת לא היה זמין. הסיבה לכך היא בדרך כלל בעיה ברשת.

שגיאות בצד הלקוח

קודי הסטטוס הבאים הם של שגיאות בצד הלקוח, וצריך לבצע פעולה כדי לפתור אותן. ‫Consumer SDK ימשיך לנסות לרענן את הנסיעה עד שתסיימו את שיתוף הנסיעה, אבל הוא לא יתאושש עד שתבצעו פעולה.

קוד סטטוסתיאור
INVALID_ARGUMENT באפליקציה למשתמשים צוין שם נסיעה לא תקין. שם הנסיעה חייב להיות בפורמט providers/{provider_id}/trips/{trip_id}.
NOT_FOUND הנסיעה לא נוצרה.
PERMISSION_DENIED לאפליקציה למשתמשים אין מספיק הרשאות. השגיאה הזו מתרחשת במקרים הבאים:
  • לאפליקציה למשתמשים אין הרשאות
  • ה-SDK לצרכנים לא מופעל בפרויקט במסוף Google Cloud.
  • טוקן ה-JWT חסר או לא תקין.
  • אסימון ה-JWT חתום עם מזהה נסיעה שלא תואם לנסיעה המבוקשת.
RESOURCE_EXHAUSTED מכסת המשאבים היא אפס, או שקצב זרימת התנועה חורג ממגבלת המהירות.
UNAUTHENTICATED האימות של הבקשה נכשל בגלל טוקן JWT לא תקין. השגיאה הזו מתרחשת כשאסימון ה-JWT נחתם ללא מזהה נסיעה, או כשתוקף אסימון ה-JWT פג.