Report a driver's ETA to a server

In this example, a driver is using turn-by-turn directions and we want to report the driver's estimated time of arrival (ETA) at their destination to a server, and continue reporting it whenever it changes significantly. Specifically, we are reporting whenever the ETA changes by more than 1 minute, or whenever we have not reported the ETA for at least 5 minutes.

You will need to provide your own server for this. The data in our example is sent to the server with a PUT request in JSON format. Modify the URL and other aspects of the URLRequest as necessary to suit your server.

Before you do this:

In order to see results, first make sure you have turn-by-turn navigation up and running in your app. If you haven't done this yet, here's how .

In order to see a successful connection, you will also need a server that can accept the data. We provide a reference server that you can use for testing this.

After you present the turn-by-turn UI, you can register to be notified when the driver's location changes. (If you don't want to leave your desk to test this, it also works with our driving simulator , of course!)

NotificationCenter.default.addObserver(forName: NSNotification.Name.TGTelemetryCurrentPointChange, object: nil, queue: nil) { (notification) in
    guard let newETA = notification.userInfo?[TGTelemetryUserInfo.ETA] as? Date else {
        return
    }
    
    self.reportETA(newETA)
}
[NSNotificationCenter.defaultCenter addObserverForName:TGTelemetryCurrentPointChangeNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull notification) {
    NSDate *newETA = notification.userInfo[TGTelemetryUserInfoETA];
    if (newETA == nil) {
        return;
    }
    
    [self reportETA:newETA];
}];

Once we've done that, we need some properties to keep track of our reporting.

// You can change these configuration values to suit your needs.
let reportETADeltaTrigger: TimeInterval = 60
let reportETATimeTrigger: TimeInterval = (5 * 60)
let reportETAURL = URL(string: "http://localhost:3200/drivers/eta")!
let reportETAMethod = "PUT"

// Keep track of when and what we last reported.
var lastReportedETA: Date?
var lastReportedETATime: Date?
// Put these inside your @interface
// Keep track of when and what we last reported.
@property (nonatomic, nullable) NSDate *lastReportedETA;
@property (nonatomic, nullable) NSDate *lastReportedETATime;

// Put these inside your @implementation
// You can change these configuration values to suit your needs.
static NSTimeInterval const reportETADeltaTrigger = 60;
static NSTimeInterval const reportETATimeTrigger = (5 * 60);
static NSString *const reportETAURL = @"http://localhost:3200/drivers/eta";
static NSString *const reportETAMethod = @"PUT";

Now let's write the method to report our data to our server.

func reportETA(_ ETA: Date) {
    // We should only continue if we haven't reported yet, or if one of our triggers has been satisfied.
    guard (lastReportedETA == nil ||
        lastReportedETATime == nil ||
        abs(ETA.timeIntervalSince(lastReportedETA!)) > reportETADeltaTrigger ||
        Date().timeIntervalSince(lastReportedETATime!) > reportETATimeTrigger) else {
            
        return
    }
    
    let formatter = ISO8601DateFormatter()
    formatter.formatOptions = [.withInternetDateTime]
    
    // Collect the data we want to send
    let requestData = [
        "ETA": formatter.string(from: ETA),
    ]
    
    // Configure the request
    var request = URLRequest(url: reportETAURL)
    request.httpMethod = reportETAMethod
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    
    // Optional: send an ID for this session, so the server can tell different drivers apart
    //request.setValue(temporaryID, forHTTPHeaderField: "X-Temporary-ID")
    
    request.httpBody = try! JSONEncoder().encode(requestData)
    
    // Send the request
    let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
        if let error = error {
            NSLog("Error reporting ETA: \(error)")
            
        } else {
            // Optionally, you can do something with the successful response here.
        }
    }
    task.resume()
    
    // Remember that we did this
    lastReportedETA = ETA
    lastReportedETATime = Date()
}
- (void)reportETA:(nonnull NSDate *)ETA {
    // We should only continue if we haven't reported yet, or if one of our triggers has been satisfied.
    if (self.lastReportedETA != nil &&
        self.lastReportedETATime != nil &&
        fabs([ETA timeIntervalSinceDate:self.lastReportedETA]) <= reportETADeltaTrigger &&
        [NSDate.date timeIntervalSinceDate:self.lastReportedETATime] <= reportETATimeTrigger) {
        
        return;
    }
    
    NSISO8601DateFormatter *formatter = [NSISO8601DateFormatter new];
    formatter.formatOptions = NSISO8601DateFormatWithInternetDateTime;
    
    // Collect the data we want to send
    NSDictionary *requestData = @{
        @"ETA": [formatter stringFromDate:ETA],
    };
    
    // Configure the request
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:reportETAURL]];
    request.HTTPMethod = reportETAMethod;
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    
    // Optional: send an ID for this session, so the server can tell different drivers apart
    //[request setValue:self.temporaryID forHTTPHeaderField:@"X-Temporary-ID"];
    
    NSError *error = nil;
    NSData *encodedData = [NSJSONSerialization dataWithJSONObject:requestData options:0 error:&error];
    if (encodedData != nil) {
        request.HTTPBody = encodedData;
        
    } else {
        @throw error;
    }
    
    // Send the request
    NSURLSessionDataTask *task = [NSURLSession.sharedSession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        if (error != nil) {
            NSLog(@"Error reporting ETA: %@", error.userInfo);
            
        } else {
            // Optionally, you can do something with the successful response here.
        }
    }];
    [task resume];
    
    // Remember that we did this
    self.lastReportedETA = ETA;
    self.lastReportedETATime = NSDate.date;
}
Check out the iOS Reference App to see this example in action.

Look for example 402 in the app.