Key Concepts
In the following use case sections, we will guide you through the most common usage scenarios and reveal tips and easy-to-understand guidelines to help you get the most out of the HERE SDK for iOS.
How to use this Guide?
You can read this guide in any order. All sections are independent from each other, making it easy to skip any section and to dive straight into the topics which you are most interested in.
- In the example section, you can find the example apps accompanying this user guide.
- If you are interested in building your first app showing a HERE map, take a look at the Get Started section to guide you through the first simple steps.
- See an overview of the available use cases covered in this Developer's Guide.
Conventions
The HERE SDK uses completion handlers as callback functions. For all completion handlers available in the HERE SDK, you can either use closure expressions or function call expressions. By convention, for this guide, we preferably use function call expressions to show the full type of information and other details, such as method and callback name. An example for both can be found in the Search section.
Free Resources
When the app's lifetime has ended, you can free resources by calling SDKNativeEngine.sharedInstance = nil
. Also, all references to SDKNativeEngine
must be set to nil
(if any). After calling this, any related HERE SDK feature should no longer be used - unless you initialize the HERE SDK again: If you have created engines like SearchEngine()
or RoutingEngine()
using the default constructor, then these instances need to be recreated as well. Basically, all engines that used the same instance of the SDKNativeEngine
need to be recreated after it was set to nil.
Completion Handlers and Delegates
- The HERE SDK uses completion handlers for single event notification such as for search results.
- For reoccurring event notifications such as for gesture events, delegates are used. When multiple delegates can be set, then the method pattern
add_x()
and remove_x()
is used as naming convention. If only one delegate can be set at a time, properties are used that can be set to nil
to stop listening. - It is the responsibility of the developer to handle errors inside the scope of a completion handler or a delegate gracefully: As a guideline, code that can throw an exception should be handled.
Debug Logs
You can use the LogAppender
protocol to insert your own log class into the SDKNativeEngine
. This way you can log HERE SDK messages for various predefined log levels even for release builds of your app.
When running an iOS simulator, you can obtain logs without running Xcode by executing the following command from the terminal:
xcrun simctl spawn booted log stream --level debug
Code Snippets
The shown code snippets cover best practice example code ready to be used for your own applications. However, for the sake of simplicity and to not shadow the educational approach of this guide, not all edge scenarios may be handled, especially when it comes to error handling or robust threading. In some cases, the obvious code is left out, but it can be found in the accompanying example apps that can be instantly built and deployed on any supported device - given a set a of valid HERE credentials. As is the practice in Swift, forced unwrapping of optionals is avoided except when the HERE SDK guarantees by design that it is safe to do so.
Design Principles
The accompanying example apps follow the same structure. As much as possible the HERE SDK example code is decoupled from the surrounding platform code. We hope this makes it easier to see the relevant parts of the shown APIs. Each example app follows the same entry point from which the HERE SDK is initialized. Since each app is focusing on a different aspect of the HERE SDK, that code can be found in a single class postfixed with "...Example.swift" in its class name. Most often this class gets a reference to a MapView
to start its work.
Following a common platform separation principle, the example code is kept free of most iOS dependencies - instead it's mostly pure Swift code that shows how the HERE SDK can be used.
Dependency Management
Currently, dependency management via CocoaPods is not yet supported. This means that the HERE SDK framework must be copied locally to an application project as described in the Get Started section.
Using The HERE SDK With Other Frameworks
You can use the HERE SDK with other frameworks. For example, it is possible to combine an open street map with a SearchEngine
, if you wish to do so.
-
Xamarin: The HERE SDK does not support Xamarin, but customers can implement a wrapper for Xamarin on top of the public APIs offered by the HERE SDK. We do no commit to make changes to the HERE SDK to support the related test tooling for Xamarin.
-
React Native: React Native is not supported. However, customers can implement a wrapper on their own, but we do not provide any support for such a task.
Support for Objective-C
Currently, the HERE SDK does not support Objective-C and it is not possible to use bridging headers. If your code-base is written in Objective-C, consider to migrate to Swift.
However, it is possible to access the HERE SDK directly from Swift files that are integrated into an existing Objective-C project. It is possible to mix Swift and Objective-C in a single project, but for now you cannot access the HERE SDK directly from Objective-C.
Transactions and Usage Statistics
The HERE SDK does not offer direct APIs to query how many requests or transactions an app has made - however, you can use an external REST API for this. With the HERE Usage API you can request such data. Example for org ID org123456789
and a start and end date:
https://usage.bam.api.here.com/v2/usage/realms/org123456789?startDate=2022-07-01T10:39:51&endDate=2022-08-30T10:39:51
For more details, please refer to the cost management documentation.
Is the HERE SDK Thread Safe?
The HERE SDK is not guaranteed to be thread safe and it is recommended to make calls to the SDK from the main thread. Internally, the HERE SDK will offload most of its work to a background thread, but callbacks to your code will always occur on the main thread. In general, thread safety is the responsibility of the caller. For example, it is unsafe to reuse an engine on different threads unless your code is synchronized.
Use TaskHandles to Cancel Asynchronous Operations
Most asynchronous methods provide a TaskHandle
as immediate return value, while the final result of the operation will be returned in a completion handler with a delay. The TaskHandle
provides status information and it allows to abort an ongoing operation.
Unit Tests
It's easy to write unit tests for your app logic that uses the HERE SDK as all classes are fully mockable. Below you can see an example using XCTest
.
func testAngle() throws {
let angle = Angle.fromRadians(angle: 0.174533)
let angleInDegrees = angle.degrees
let roundedAngleInDegrees = round(angleInDegrees)
let expectedAngleInDegrees: Double = 10
XCTAssertEqual(roundedAngleInDegrees, expectedAngleInDegrees, "This is a message for a failed test.")
}
Check the UnitTesting example app to find more use case examples.
Coverage
Consult the coverage page to get detailed information on supported countries and languages per feature.
Defining a Scope
Optionally, you can define several scopes for an application - for example, to define a debugScope for testing your app. For a production version of your app, you may decide to leave the scope just empty, which is the default value. See the IAM Guide for more details on how to set up a project ID.
Each app belongs at least to one project. In the Projects Manager via your HERE platform account, select Projects: This allows you to see the available project IDs. You can also add more. Each project ID shows a HRN value.
Set the HRN value as scope in your plist
file as follows:
<key>HERECredentials</key>
<dict>
<key>AccessKeyId</key>
<string>YOUR_ACCESS_KEY_ID</string>
<key>AccessKeySecret</key>
<string>YOUR_ACCESS_KEY_SECRET</string>
<key>Scope</key>
<string>YOUR_PROJECT_SCOPE</string>
</dict>
Follow this guide to see how you can manage your projects.
Note that a project ID must be unique and cannot be changed for the lifetime of the organization's account. Project IDs must be between 4 and 16 characters in length. They affect also the HRN value. If the scope/HRN is set by an application, it will be used for authentication and the internal token creation of an application. If an unknown scope is set, any authentication attempt would fail and the application logs would indicate this.
Engines
The HERE SDK contains several modules - or engines as we call them - to execute specific tasks such as calculating a route with the RoutingEngine
or requesting search results via the SearchEngine
. There are many more engines you can use with the HERE SDK and you can read more about them in the dedicated chapters below. However, most engines share common concepts that makes it easier to use them. For example:
- All engines execute their tasks asynchronously and receive their results on the main thread.
- All engines share similar interfaces, callbacks and error handling.
- It is possible to start multiple instances of an engine in parallel.
- An online connection is required.
Note that all HERE SDK engines, except for the SDKNativeEngine
, can operate independently from each other and require HERE credentials to request data.
Note
The HERE SDK does not verify credentials at instantiation time. This only happens once a feature is used that requires authentification. In case of a failure, you will get with a dedicated error message. For example, the SearchEngine
will reply with a SearchError.authentificationFailed
error. Other engines provide similar error codes.
Freemium credentials will work for most features, but not for features like offline maps which are exclusive for editions such as the Navigate Edition. If the credentials are invalid, the logs contain "[ERROR]" messages with a text similar to "Failed to get the authentication token: Authentication failed, error code: 1". This indicates that the credentials are not accepted when using a service. If you want to ensure that your credentials are set up correctly, use a premium feature with a specialized engine and check if the engine replies with an error value.
Any engine can be created using a parameterless constructor. Example:
do {
try searchEngine = SearchEngine()
} catch let engineInstantiationError {
fatalError("Failed to initialize engine. Cause: \(engineInstantiationError)")
}
Using this default constructor requires that the HERE SDK is already initialized and that a shared instance of the SDKNativeEngine
was set either directly by calling SDKNativeEngine.sharedInstance = sdkNativeEngine
or implicitly by calling SDKNativeEngine.makeSharedInstance(options: options)
.
For rare use case scenarios, you can use an overloaded constructor that takes an individually created instance of the SDKNativeEngine
- as shown in the below section.
Initialization
Since HERE SDK 4.12.1.0, the HERE SDK needs to be initialized manually. Follow the Get Started guide to see how this can be done.
- If your app immediately wants to show a map view after app start, then for most use cases it's sufficient to follow the Get Started guide. However:
- If your app needs to show a map view at a later point in time or if you want to delay initialization, then it is also possible to initialize the HERE SDK at a later point in time.
- Note: The time of initialization of the HERE SDK does not have an impact on how transactions and monthly active users (MAU) are counted.
Initialize the HERE SDK by creating SDKOptions
and use them to create a new SDKNativeEngine
.
The creation of the SDKNativeEngine
and other engines such as the SearchEngine
happens synchronously in a neglectable short amount of time.
You can initialize the HERE SDK in two ways:
- Create a shared instance of the
SDKNativeEngine
with SDKNativeEngine.makeSharedInstance(options: options)
. - Create individual instances of the
SDKNativeEngine
via SDKNativeEngine(options: options)
.
By calling SDKNativeEngine.makeSharedInstance(options: options)
you create a shared instance of the SDKNativeEngine
that can be used by the HERE SDK. This singleton approach allows easy creation of engines such as the SearchEngine
, because you can use the default constructor (SearchEngine()
) that requires no parameters. Internally, the SearchEngine
will use the shared instance of the SDKNativeEngine
you have created at initialization time. Therefore, when you re-initialize the HERE SDK, also all engines that have been created using the parameterless constructor, need to be created again.
Alternatively, for rare use cases, you can set an individual SDKNativeEngine
instance for each of the feature engines:
try searchEngine = SearchEngine(sdkNativeEngine)
In general, it should not be necessary to initialize the SDKNativeEngine
multiple times. Only one shared instance can be set at a time, which happens implicitly when calling SDKNativeEngine.makeSharedInstance(options: options)
. Alternatively, you can use the available constructor to create an instance of the SDKNativeEngine
directly: If you create multiple instances individually, then no shared instance is set - and each feature engine needs to be created with an instance of a SDKNativeEngine
as constructor parameter - like shown above. This way you can use feature engines such as the SearchEngine
or the RoutingEngine
with different instances of the SDKNativeEngine
that can hold different SDKOptions
.
Note that any individual instance can be set explicitly as shared instance by calling SDKNativeEngine.sharedInstance = sdkNativeEngine
. Attention: In this case, make sure to dispose any previous instance by calling SDKNativeEngine.sharedInstance = nil
. Note that this is not necessary when creating a global instance by calling SDKNativeEngine.makeSharedInstance(options: options)
- for convenience, this call internally disposes any previously shared instance.
Note
For most use cases it is not recommended to create multiple instances: In case of doubt, we recommend to initialize the HERE SDK only via SDKNativeEngine.makeSharedInstance(options: options)
as shown in the Get Started guide. It is possible to use this initialization method each time the HERE SDK is needed and to dispose the SDKNativeEngine
thereafter: For example, for each view. However, for most cases it can be more efficient if the HERE SDK is initialized only once during the lifetime of an application.
Multiple SDKNativeEngine
instances can’t have the same access key id and the cache path is ensured to be unique per access key id. If a 2nd instance is created with an access key id that is already in use, then an InstantiationException
is thrown: As a rule of thumb, when you need multiple instances, you also need a distinct set of credentials for each.
Note
The access key id part of your credentials is tied to the cache path. When credentials are changed at runtime, it may look like that the previous cache is gone, but it still exists and it is not cleared automatically after disposing the SDKNativeEngine
or after setting new credentials. Since new credentials have been set, the cache path has just been changed. For example, when the previous credentials are restored, the previous cache can be used again.
More information on the cache can be found here.
Tip: Protect Credentials
Your credentials should be protected to provide misuse by other parties that are not intended to use them.
One option to keep credentials secure is to store them on a secure server and retrieve them by requests using SSL connections and data protection mechanisms such as Keychain data protection.
For best practice, consider:
- To avoid keeping sensitive data in plain text.
- To transfer credentials using a secure communication channel.
- To store credentials using encryption such as device security and strong encryption ciphers.
- To add anti-tampering and anti-debugging code, so that a potential attacker cannot intercept data during dynamic runtime execution.
- Track the application usage to detect anomalies.
Use Engines with or without a Map View
Engines do not need a map view to operate. Therefore it is possible to run an engine as a stand-alone, without any map view added to your application. This way, you can build an app solely around a specific engine. With or without a map view - the procedure to create a new engine is exactly the same. Just make sure to initialize the HERE SDK beforehand.
Get Access Tokens For Use With External REST APIs
Each time the HERE SDK is initialized, a new access token is generated internally. In case of multiple SDKNativeEngine
instances, each instance holds its own token. You can also refresh and fetch the token via Authentication.authenticate(SDKNativeEngine.sharedInstance, callback)
where the callback provides the token via authenticationData.token
- you can use this token to access external HERE REST APIs.
For using the HERE SDK itself, you do not need to know the token - it is only required under the hood and the HERE SDK is handling the token generation automatically.
To protect our backends against misusage, a rate limit may apply when too many tokens are generated or when too many services are accessed in a too short time frame. In such a case, an engine will respond with a requestLimitReached
error or similar. If you expect very high app usage, please talk to your HERE representative upfront to adapt the limits where needed.