HERE iOS SDK Developer's Guide

Using CLE2 Offline

You can perform search requests online to the server or offline to the local device. To enable offline searches against local data, the HERE SDK provides different ways for you to pre-fetch data.

Use offline mode as much as possible, since it provides the following advantages:

  • Resilience to network instability.
  • More efficient use of network bandwidth. Instead of sending one request per object, you can aggregate requests locally and transmit data in batches.
  • Savings in network bandwidth. Your app can cache and update data that is only near the user's current location or pre-download a layer only when a WiFi network becomes available.
  • Potentially making the application more responsive and improving the user experience and interface interactions, since the data is already available locally on the device.
  • Create or modify geometries with the HERE SDK, and then store them locally, effectively using the HERE SDK as a data source and a storage of information.

The offline CLE2 feature is designed to be simple to use. Since all database synchronization and geospatial-related complexities are handled by the SDK, you can focus on other parts of app development.

Querying the Local Storage

To search using locally stored data, set the request mode to NMACLE2Offline for any of your NMACLE2Request and perform the request:


//Create any request as normal; In this example, we use the proximity request:

NMACLE2ProximityRequest * proximityRequest;
proximityRequest= [[NMACLE2ProximityRequest alloc] initWithLayer:@"HERE_SITES"
     center:[NMAGeoCoordinates geoCoordinatesWithLatitude:50.113905
        longitude:8.677608]
     radius:500]; // 500 meters

//Set to offline mode:
[proximityRequest setRequestMode: NMACLE2Offline];

//Now perform the request
[proximityRequest startWithBlock:^(NMACLE2Request *request, NMACLE2Result * result, NSError *error) {
  if(!error) {
    NSLog(@"Geometries returned in result.geometriesArray came from the device's local storage.");
  }
  }];

You can configure the search request to hybrid or automatic mode, indicating that if during an online request the connection drops or there is a network error, then the request automatically falls back to an offline operation. You can see whether the search was performed online or offline by checking the connectivity mode that was used to perform the search. This can be done by inspecting the requestMode property on the NMACLE2Result object.

//Create any request as normal; In this example, we use the proximity request:

NMACLE2ProximityRequest * proximityRequest;
proximityRequest= [[NMACLE2ProximityRequest alloc] initWithLayer:@"HERE_SITES"
     center:[NMAGeoCoordinates geoCoordinatesWithLatitude:50.113905
        longitude:8.677608]
     radius:500]; // 500 meters


//Set to automatic mode
[proximityRequest setRequestMode: NMACLE2Automatic];


//now calling a startWithBlock will try an online request, if it fails the offline storage kicks in.
[proximityRequest startWithBlock:^(NMACLE2Request *request, NMACLE2Result * result, NSError *error) {
  if(!error) {
    NSLog(@"Geometries returned in result.geometriesArray.");
    //to find out whether this response came from the local storage or was
    //obtained from the server (online request), check the requestMode property:
    NMACLE2RequestMode * requestMode = result.requestMode;
  }
  }];

Ways to Populate the Local Storage

By default, offline features are disabled and the local storage contains no data. There are currently three ways to add geometries to make them available for offline searches:

  1. Enable caching when performing one or more requests (for example, using the NMACLE2ProximityRequest).
  2. Download one or more layers.
  3. Direct insertion of data into the local database.

After populating the database, you can query for the data in offline mode as usual by switching the connectivity mode of the respective request to NMACLE2ConnectivityModeOffline.

NMACLE2DataManager and NMACLE2Task

The NMACLE2DataManager object is the central interaction point with the local storage. With it, it is possible to:

  • Download all geometries of a specific layer
  • Check how many geometries are currently stored in total, or in a specific layer
  • Delete geometries belonging to a specific layer
  • Purge the local storage by deleting all items
  • Create, update, or delete a local or remote geometry

All the operations relating to data management that NMACLE2DataManager exposes make use of a NMACLE2Task that represent a unit of work. Since all of the data management operations involve database access, network communication or both, NMACLE2Task runs asynchronously. You can obtain a NMACLE2Task object from NMACLE2DataManager.

With NMACLE2Task, you can:

  • Pass it to other parts of your code. NMACLE2Task is a self-contained unit of work.
  • Subscribe for results of the operation. Multiple subscribers are supported and they are called on the main thread.
  • Start execution of the task. Tasks are reusable. You can run them repeatedly multiple times, which makes retrying a failed operation very easy.
  • Cancel a running task.
  • Check if the task is started.
  • Check if the task has finished.
  • Wait for the task to finish.
  • Retrieve the status of a finished operation directly from the task (check for errors).
  • Retrieve the result of a successfully finished operation directly from the task.

Storing Data by Caching Search Results

When caching is enabled in an NMACLE2Request, any returned geometries are automatically stored locally. To activate it, set the cacheEnabled property to YES before performing the request:


//Create any request as normal; In this example, we use the proximity request:

NMACLE2ProximityRequest * proximityRequest;
proximityRequest= [[NMACLE2ProximityRequest alloc] initWithLayer:@"HERE_SITES"
     center:[NMAGeoCoordinates geoCoordinatesWithLatitude:50.113905
        longitude:8.677608]
     radius:500]; // 500 meters

//activate caching:
[proximityRequest setCacheEnabled: YES];

//Now perform the request
[proximityRequest startWithBlock:^(NMACLE2Request *request, NMACLE2Result * result, NSError *error) {
  if(!error) {
    NSLog(@"Geometries returned in result.geometriesArray are now stored in the local database.");
  }
  }];

//now some geometries are in local storage. At a later point in time if we'd like to make an offline search,
//simply switch the requesMode to offline only in the request:
[proximityRequest setRequestMode: NMACLE2Offline];

//now calling a startWithBlock will operate completely offline:
[proximityRequest startWithBlock:^(NMACLE2Request *request, NMACLE2Result * result, NSError *error) {
  if(!error) {
    NSLog(@"Geometries returned in result.geometriesArray are now stored in the local database.");
  }
  }];

Storing Data by Downloading Layers

The second option is to use NMACLE2DataManager to insert data to the local storage using the downloadLayer method.

The following is an example of how to use the downloadLayer method:


//Usage examples of NMACLE2DataManager

NMACLE2DataManager * dataManager = [NMACLE2DataManager sharedInstance];

//1 - Download a layer previously uploaded to the server
[storage downloadLayer:@"LOCAL_SHOPS" completionHandler:^(NSError * _Nullable error) {
      if(error)
      {
        NSLog(@"Unable to download layer. Error: %@", [error description]);
      }
      else
      {
        NSLog(@"Layers downloaded successfully.");
      }
    }];

//2 - Print the total number of stored geometries (sum of all downloaded layers),
//plus any cached geometries (e.g., from a proximity request with cache enabled)

NSNumber * numberOfGeometries;
[storage numberOfStoredGeometries: &numberOfGeometries];
NSLog(@"Total count: %lli", [numberOfStoredGeometries longLongValue]);

//3 - Delete all geometries from a specific layer
NSError * error = [[NMACLE2DataManager sharedManager] deleteLayer:@"POKEMONS"];
if(error)
{
   NSLog(@"Unable to delete layer. Error: %@", [error description]);
}
else
{
  NSLog(@"Layers deleted successfully.");
}

//4 - Completely delete all stored data
[[NMACLE2DataManager sharedManager] deleteAll];

Storing Data by Inserting Geometries

You can generate location-based data and persist it locally, remotely, or both, by using the method geometryTask:onLayer:withGeometries:inStorage: from the NMACLE2DataManager class. This factory method returns a NMACLE2Task object that can be used to start, cancel, or to fetch results of operations at any given time.

geometryTask:(NMACLE2Operation)operationType
     onLayer:(NSString*)layerId
withGeometries:(NSArray<NMACLE2Geometry *>*)geometriesArray
   inStorage:(NMACLE2StorageType)storage;
  • The first parameter in this method describes the operation type, which can be one of the following:
    • NMACLE2OperationCreate
    • NMACLE2OperationUpdate
    • NMACLE2OperationDelete
    Note that querying for geometries is accomplished through the respective NMACLE2Request specialized classes, so there is no "read" opreation here.
  • The second parameter is the layer the operation should be applied to.
  • The third parameter is a list with the geometries themselves.
  • The last parameter defines whether to operate on local storage (NMACLE2StorageTypeLocal), or remote storage (NMACLE2StorageTypeRemote) using the HERE CLE2 server.
Note: While this section covers usage of this method for the local option, all operations (create, update, delete) can also be used to change remote layers.

The following is an example on how to create a geometry and store it locally:

NSMutableArray <NMACLE2Geometry *>* geometriesToAdd = [NSMutableArray new];

for (int i = 0; i < 100; ++i) {
  NMACLE2GeometryPoint * newPoint = [[NMACLE2GeometryPoint alloc] init];
  NSString * key = [NSString stringWithFormat:@"key_%i", i];
  NSString * valueString = @"hello";
  newPoint.attributes = [@{ key : @"value",
               @"HERE_SDK" : @"3.4",
               @"Some_numbers" : valueString,
               @"New_Feature" : @"Offline functionality \U0001F44C"} mutableCopy];
  newPoint.coordinates = [[NMAGeoCoordinates alloc] initWithLatitude: i % 180 longitude:i % 359];
  [geometriesToAdd addObject:newPoint];
}
NMACLE2Task<NMACLE2OperationResult*> * task;
task = [[NMACLE2DataManager sharedManager] geometryTask:NMACLE2OperationCreate
                        onLayer:LOCAL_POINTS_TEST_LAYER
                     withGeometries:geometriesToAdd
                        inStorage:NMACLE2StorageTypeLocal];
[task subscribeWithBlock:^(NMACLE2OperationResult * _Nonnull result, NSError * _Nonnull error) {
      finished_ = YES;
      error_ = error;
      if(error)
      {
        NSLog(@"error: %@", [error description]);
      }
      else
      {
        NSLog(@"Finished for layer %@", LOCAL_POINTS_TEST_LAYER);
      }
}];

[task start];

Uploading a Local Layer

It is possible to upload a locally-stored layer to the server. Since this requires two operations (fetch from local storage and upload), it's a good candidate to run individual tasks in synchronous manner to avoid callback hell creeping in. Of course, this needs to be done on it's own thread, for example using AsyncTask.


//first fetch all geometries

NMACLE2Task<NSMutableArray<NMACLE2Geometry *> *> *fetchTask

fetchTask = [dataManager fetchLocalLayersTask:[NSArray arrayWithObjects:layerName, nil]];
[fetchTask startWithBlock:^(NSMutableArray < NMACLE2Geometry * > *_Nonnull result, NSError *_Nonnull error) {
      if (error) {
        NSLog(@"Error fetching local geometries: %@", error.description);
        return;
      }
      //Success, upload all geometries from the selected local layer
      NMACLE2Task<NMACLE2OperationResult *> *uploadtask;
      uploadtask = [dataManager uploadLayerTask:layerName withGeometries:result];
      //start the upload
      [uploadtask startWithBlock:^(NMACLE2OperationResult *_Nonnull result, NSError *_Nonnull error) {
        if (error) {
          NSLog(@"Upload error: %@", error.description);
          return;
        }
        NSLog(@"Layer Upload Complete. %@ geometries uploaded.", result.affectedItemCount);
      }];
    }];

Data Management Considerations

The following are a few tips to help with data management when using CLE2 in an offline context.

Local-only Geometries

All CLE2Geometry objects have the following properties:

  1. Geometry ID, accessible with geometry_id
  2. Locality flag, accessible with is_local

The geometry ID is unique to a layer. If a geometry object has just been created, its geometry ID is null and the locality flag is false.

The locality flag tells whether this geometry belongs to a local context only, meaning it was not retrieved or passed through the CLE2 server. A geometry with a true locality flag has a locally generated unique geometry ID. Otherwise, it contains a server-provided ID. This server-provided ID is not related to the locally generated IDs of geometries stored directly in the database created via geometryTask:onLayer:withGeometries:inStorage:.

Note: The functionality of locally storing geometries without passing through the server is provided so that you do not need to manage data persistence on these objects when a connection is not available.

For simplicity, when saving geometries directly to the local database, keep them using a separate layer name. If at a later desired point in time these geometries should be shared with the server, fetch all local geometries using fetchLocalLayersTask: method of NMACLE2DataManager and then upload them either using uploadLayerTask:withGeometries: or geometryTask:onLayer:withGeometries:inStorage: with a create operation (NMACLE2OperationCreate). This avoids the requirement to check for the is_local property.

By using these concepts, you can move geometries to different layers, contexts, and use these tools to organize data.

Data Consistency

Use of uploadLayerTask:withGeometries: should be primarily restricted to administrative users, because this method deletes all existing geometries in the server and recreate the layer with the provided ones. If the user does not have the latest information for this layer, data loss may occur, as it can overwrite another user's upload.

Therefore, for a scenario with continuous or concurrent geometry upload, use the geometryTask:onLayer:withGeometries:inStorage: method with NMACLE2OperationCreate or NMACLE2OperationUpdate. Operating in a "append only" manner or only updating the existing geometries avoids data loss, even if users are uploading geometries concurrently to the server.

Current Limitations

Currently, individualized user account management for the CLE2 server is not available. For security reasons, care must be taken that your app credentials are kept well hidden. Please contact HERE if your application requires a user account access feature.

Note: Since geospatial queries are the focus of CLE2, HERE SDK does not support attribute searches in offline mode. You can filter the data using one of the geospatial queries (such as proximity) to narrow down the results to a small enough number that most applications do not suffer performance impact by iterating the geometry’s attributes key-value dictionary to filter results further.

You cannot use this account to purchase a commercial plan on Developer Portal, as it is already associated to plans with different payment methods.

To purchase a commercial plan on Developer Portal, please register for or sign in with a different HERE Account.

Something took longer than expected.

The project should be available soon under your projects page.

Sorry, our services are not available in this region.

Something seems to have gone wrong. Please try again later.

We've detected that your account is set to Australian Dollars (AUD).
Unfortunately, we do not offer checkouts in AUD anymore.
You can continue using your current plan as normal, but to subscribe to one of our new plans,
please register for a new HERE account or contact us for billing questions on selfservesupport@here.com.