HERE iOS SDK Developer's Guide

Search and Discovery

The HERE iOS SDK includes a Places API which provides functionality to search, discover, and obtain more information about places in the real world.

HERE Places helps to determine whether a business meets your needs through reviews and photos from real people. In addition to basic information such as opening hours and contact details, HERE Places can also include editorials from popular guides to help identify the best Places for you to visit.

Steps for Performing a Search

  1. Implement the NMAResultListener protocol to handle the completion of the search
  2. Create a request using the NMAPlaces factory
  3. Invoke the request by calling NMARequest startWithListener:
  4. The NMAResultListener request:didCompleteWithData:error: callback is triggered when the request is finished
Note: Applications that use the Places API must honor the following prescribed workflow:
  1. Search
  2. Request for Details
  3. Perform Actions
Do not preload results that are linked from a response in order to improve performance, as doing so violates HERE's guidelines. For more information about usage restrictions, consult the API Implementation Check List section in the Places RESTful API documentation.

Discovery Requests

The HERE Places API supports the following discovery requests. Requests are created through factory methods in NMAPlaces.

Request NMAPlaces method Purpose
Search createSearchRequestWithLocation: query: Finds places that match user-provided search terms.
Explore createExploreRequestWithLocation:searchArea:filters: Finds interesting places nearby, or in the map viewport, sorted by popularity. Use this type of request if you are trying to answer the question "What are the interesting places near here?" The results may be optionally restricted to a given set of categories, which acts as a filter in terms of what places get returned.
Here createHereRequestWithLocation:filters: Helps users identify places at the given location by finding places of interest near a given point, sorted by distance. Use this type of request if you are trying to answer the question "What is near this location?" or "Where am I?" You can use this endpoint to implement features like "check-in" (by identifying places at the user's current position) or "tap to get more information about this place of interest".
Note: Normally, the closest known places are returned with the Here Discovery request, but if the uncertainty in the given position is high, then some nearer places are excluded from the result in favor of more popular places in the area of uncertainty.
The following code example demonstrates how to perform a search discovery request. You need to implement the NMAResultListener protocol by implementing the request:didCompleteWithData:error callback method, and also initialize the request by calling request startWithListener::

// Sample Search request listener
@interface NMASearchTest : NSObject<NMAResultListener> {
  NMADiscoveryPage* _result;
}
@end
@implementation NMASearchTest

// NMAResultListener protocol callback implementation
- (void)request:(NMARequest*)request
    didCompleteWithData:(id)data
    error:(NSError*)error
{
  if ( ( [request isKindOfClass:[NMADiscoveryRequest class]]) &&
    ( error.code == NMARequestErrorNone ) )
  {
    // Process result NMADiscoveryPage objects
    _result = (NMADiscoveryPage*) data;
  }
  else
  {
    // Handle error
    ...
  }
}
- (void) startSearch
{
  // Create a request to search for restaurants in Vancouver
  NMAGeoCoordinates* vancouver =
  [[NMAGeoCoordinates alloc] initWithLatitude:48.263392
          longitude:-123.12203];

  NMADiscoveryRequest* request =
  [[NMAPlaces sharedPlaces] createSearchRequestWithLocation:vancouver
          query:@"restaurant"];

  // optionally, you can set a bounding box to limit the results within it.
  NMAGeoCoordinates *boundingTopLeftCoords = [[NMAGeoCoordinates alloc] initWithLatitude:49.277484 longitude:-123.133693];
  NMAGeoCoordinates *boundingBottomRightCoords = [[NMAGeoCoordinates alloc] initWithLatitude:49.257209 longitude:-123.11275];
  NMAGeoBoundingBox *bounding = [[NMAGeoBoundingBox alloc] initWithTopLeft:boundingTopLeftCoords bottomRight:boundingBottomRightCoords];

  request.viewport = bounding;

  // limit number of items in each result page to 10
  request.collectionSize = 10;

  NSError* error = [request startWithListener:self];
  if (error.code != NMARequestErrorNone)
  {
    // Handle request error
    ...
  }
}
@end

To ensure that your application get the best search results, you can set a location context to your search request by setting a bounding box to the viewport property. In the previous example, you can also replace the NMAGeoBoundingBox with the viewport from NMAMapView.

The result of a search or explore discovery request is an NMADiscoveryPage. The NMADiscoveryPage represents a paginated collection of items from which the following can be retrieved:
  • Next page request - an NMADiscoveryRequest used to retrieve additional pages of search items
  • Items for the current page - an NSArray of NMALink, either NMAPlaceLink or NMADiscoveryLink

If NMADiscoveryPage.nextPageRequest is nil, no additional results are available.

The following is an example:


...
@interface NMANextPageTest : NSObject<NMAResultListener> {
  NMADiscoveryPage* _page;  // valid NMADiscoveryPage instance
}
@end
@implementation NMANextPageTest
- (void)onNextPageAction
{
  NSError* error = [_page.nextPageRequest startWithListener:self];
  if ( error.code == NMARequestErrorNone )
  {
    // More data is available
  }
}

// NMAResultListener protocol callback implementation
- (void)request:(NMARequest*)request
    didCompleteWithData:(id)data
    error:(NSError*)error
{
  if ( ( [request isKindOfClass:[NMADiscoveryRequest class]] ) &&
    ( error.code == NMARequestErrorNone ) )
  {
    // Process NMADiscoveryPage objects
  }
  else
  {
    // Handle error
    ...
  }
}
...
@end
The NMADiscoveryPage discoveryResults property contains an array of NMALink objects. The items are actually a collection of NMALink subclasses:
  • NMAPlaceLink - Represents discovery information about a NMAPlace. The NMAPlaceLink contains a brief summary about a place. Details about a place are available from the NMAPlace that the NMAPlaceLink references.
  • NMADiscoveryLink - Represents a discovery-related API link used to retrieve additional NMADiscoveryPage instances. This type of NMALink can be a result item in an Explore or Here type of search. The NMADiscoveryLink references refine discovery requests resulting in more specific results. For example, the NMADiscoveryLink may link to a discovery request to search for 'Eat & Drink', 'Going Out', 'Accommodation', and so on.
It is recommended that each type of NMADiscoveryPage be checked before it is used. In the following example, it is shown how an NMAPlace is retrieved through a NMAPlaceLink:

@interface NMASearchTest : NSObject<NMAResultListener> {
  NMADiscoveryPage* _result;
}
@end
@implementation NMASearchTest
// Retrieve the place details when the user selects a displayed PlaceLink.
- (void)onPlaceLinkSelected:(NMAPlaceLink*)placeLink
{
  NSError* error = [[placeLink detailsRequest] startWithListener:self];
  if ( error.code == NMARequestErrorNone )
  {
    // More data will available.
    ...
  }
}

// NMAResultListener protocol callback implementation
- (void)request:(NMARequest*)request
    didCompleteWithData:(id)data
    error:(NSError*)error
{
  if ( ( [request isKindOfClass:[NMADiscoveryRequest class]]) &&
     ( error.code == NMARequestErrorNone ) )
  {
    _result = (NMADiscoveryPage*) data;
    NSArray* discoveryResult = _result.discoveryResults;

    for ( NMALink* link in discoveryResult )
    {
      if ( link isKindOfClass:[NMADiscoveryLink class] )
      {
        NMADiscoveryLink* discoveryLink = (NMADiscoveryLink*) link;

        // NMADiscoveryLink can also be presented to the user.
        // When a NMADiscoveryLink is selected, another search request should be
        // performed to retrieve results for a specific category.
        ...
      }
      else if ( link isKindOfClass:[NMAPlaceLink class] )
      {
        NMAPlaceLink* placeLink = (NMAPlaceLink*) link;

        // NMAPlaceLink should be presented to the user, so the link can be
        // selected in order to retrieve additional details about a place
        // of interest.
        ...
      }
    }
  }
  else  if ( ( [request isKindOfClass:[NMAPlaceRequest class]]) &&
         ( error.code == NMARequestErrorNone ) )
  {
    NMAPlace* place = (NMAPlace*)data;
    // Access to additional details about a place of interest.
  }
  else
  {
    // Handle error
    ...
  }
}
@end

The NMAPlace Class

The NMAPlace class represents a detailed set of data about a physical place, acting as a container for various attributes, collections of media about a place, and key-value pairs of related places. An NMAPlace object can belong to a specific NMACategory, and has attributes such as:
  • A unique identifier (ID)
  • A name
  • An NMAPlaceLocation object representing the physical location of the place. NMAPlaceLocation also contains a street address and a list of the geocoordinate positions to access this place.
  • An array of NMACategory objects that link to the categories assigned to the place
  • An NMALink object containing a link to the origin of supplied information, typically a website of the supplier
  • An NSString representing a URL to an icon (optional)
  • Optional information, such as related places, user ratings, reviews, and other editorial media

Category Filters

A place of interest can be associated with categories such as museum, restaurant, and coffee shop. While creating an Explore or Here discovery request, you can choose to provide category filters in order to get a more specific set of results. For example, you may want to search for sushi restaurants near the Vancouver city hall.

To get a list of categories, call the topLevelCategories method in NMAPlaces. From this list of categories, you can then retrieve one or more level of sub-categories. For example, "Bars/Pubs" under the "Restaurant" category. Once you have the categories, you can then create an NMACategoryFilter object and call the addCategoryFilterFromUniqueId method. Note that each NMACategoryFilter object can represent multiple categories.


NSArray* categories = [[NMAPlaces sharedPlaces] topLevelCategories];

for (id category in categories)
{
  if (category.uniqueId == "restaurant" )
  {
    NMACategory* restCategory = category;
    NMAGeoCoordinates* vancouver = [[NMAGeoCoordinates alloc]
                    initWithLatitude:47.592229
                    longitude:-122.315147];

    NMACategoryFilter *categoryFilter = [NMACategoryFilter new];
    [categoryFilter addCategoryFilterFromUniqueId:restCategory.uniqueId];

    NMADiscoveryRequest* request = [ [NMAPlaces sharedPlaces]
                createHereRequestWithLocation:vancouver
                filters:categoryFilter];
    //...
  }
}

Text AutoSuggestion Requests

The HERE Places Search API also supports text autosuggestion requests. This type of request is used for retrieveing a list of suggested search terms (NMAAutoSuggestTypeQuery), instant results (NMAAutoSuggestTypePlace), and refined search links (NMAAutoSuggestTypeSearch) that are related to a specified location context and a partial search term. For example, if you make a request with the String "rest" in Berlin, the results then contain search terms such as "Restaurant", "Rest area", and "Restorf, Höhbeck, Germany".

To use text autosuggestions, implement a listener to handle a list of NMAAutoSuggest and call createAutoSuggestionRequestWithLocation:partialTerm: as follows:

// Sample Search request listener
@interface NMATextAutoSuggestionSearchTest : NSObject<NMAResultListener> {

}
@end
@implementation NMATextAutoSuggestionSearchTest

// NMAResultListener protocol callback implementation
- (void)request:(NMARequest*)request
  didCompleteWithData:(id)data
      error:(NSError*)error
{
  if ( ( [request isKindOfClass:[NMAAutoSuggestionRequest class]]) &&
     ( error.code == NMARequestErrorNone ) )
  {
    // Results are held in an array of NMAAutoSuggest objects
    // You can then check the subclass type using the NMAAutoSuggest.type property
    NSArray* textAutoSuggestionResult = (NSArray*) data;
  }
  else
  {
    // Handle error
    ...
  }
}
- (void) startSearch
{
  NMAGeoCoordinates* vancouver =
      [[NMAGeoCoordinates alloc] initWithLatitude:47.592229
            longitude:-122.315147];

  NMAAutoSuggestionRequest* request =
      [[NMAPlaces sharedPlaces] createAutoSuggestionRequestWithLocation:vancouver
            partialTerm:@"rest"];

  // limit number of items in each result page to 10
  request.collectionSize = 10;

  NSError* error = [request startWithListener:self];
  if (error.code != NMARequestErrorNone)
  {
    // Handle request error
    ...
  }
}
@end

You can retrieve the results of NMAAutoSuggestionRequest by first checking the NMAAutoSuggest object type, as shown in the following example. If the object is NMAAutoSuggestTypeSearch, it contains additional paginated results through its NMADiscoveryRequest object. If the object is NMAAutoSuggestTypePlace, you can request for more details through its NMAPlaceRequest. NMAAutoSuggestTypeQuery contains additional results through the NMAAutoSuggestionRequest object.

+(BOOL)checkAutoSuggestResults:(NSArray*)array
{
  for (id item in array) {
    NMAAutoSuggestType type = ((NMAAutoSuggest*)item).type;
    NSString *typeString;
    switch (type){
      caseNMAAutoSuggestTypePlace:
      {
        typeString = @"Place";
      }
        break;
      caseNMAAutoSuggestTypeSearch:
      {
        typeString = @"Search";
      }
        break;
      caseNMAAutoSuggestTypeQuery:
      {
        typeString = @"Query";
      }
        break;
    }

    NSLog(@"Type = %@", typeString);

    NMAAutoSuggest* suggestItem = (NMAAutoSuggest*)item;

    //Retrieve information such as suggestItem.title

    if (type == NMAAutoSuggestTypePlace) {

      NMAAutoSuggestPlace* suggestPlace = (NMAAutoSuggestPlace*)item;

      //Retrieve information such as suggestPlace.vicinityDescription

      NMAPlaceRequest* detailsRequest = suggestPlace.placeDetailsRequest;

      // Get NMAPlaceResult by calling detailsRequest startWithListener:
      // ...

    } else if (type == NMAAutoSuggestTypeSearch) {

      NMAAutoSuggestSearch* suggestSearch = (NMAAutoSuggestPlace*)item;

      //Retrieve information such as suggestSearch.position

      NMADiscoveryPage* discoveryPage;
      NMADiscoveryRequest* discoveryRequest = suggestSearch.suggestedSearchRequest;

      // Get discoveryPage by calling [discoveryRequest startWithListener:]
      // ...
    }
    else if (type == NMAAutoSuggestTypeQuery) {

      NMAAutoSuggestionRequest* autoSuggestionRequest = ((NMAAutoSuggestQuery*)item).autoSuggestionRequest;

      // Call autoSuggestionRequest startWithBlock:^(NMARequest, id, NSError) {}
      // ...
    }
  }
  return YES;
}
      

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.