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.
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.
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.
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.
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.
Coverage
Consult the coverage page to get detailed information on supported countries and languages per feature.
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.
Pre-Initialize the HERE SDK
Usually, the HERE SDK is initialized together with the map view. Below you can find several ways to initialize the HERE SDK manually. This can help to speed up loading time of the map view - as by default, the HERE SDK is initialized automatically when a map view is shown.
- 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 - and the HERE SDK will be automatically initialized together with the map view. However:
- If your app needs to show a map view at a later point in time, consider to manually initialize the HERE SDK beforehand at a suitable point in time. Then, when the map view needs to be shown, the HERE SDK is already initialized and thus, less loading time is needed to show the map.
For example, when you are sure that your app will show a map view, consider to initialize the HERE SDK as early as possible. You can call the following in the AppDelegate
after application launch - before the MapView
is initialized and any other parts of the HERE SDK are used:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
do {
try SDKInitializer.initializeIfNecessary()
} catch {
fatalError("Failed to initialize HERE SDK. Cause: \(error)")
}
return true
}
The initializeIfNecessary()
class method will initialize the HERE SDK when it is not already initialized. As a result, when the map view is loaded at a later point in time, then initialization is already done and the map view can be shown faster.
If the HERE SDK is already initialized, then initializeIfNecessary()
will do nothing.
Note that calling initializeIfNecessary()
is also necessary when you want to use the HERE SDK without a map view - as by default, the HERE SDK is initialized automatically when a map view is shown.
Below you can found more options to manually initialize the HERE SDK. This also allows to inject credentials programmatically - together with other initialization options.
Set HERE Credentials from Property List or Programmatically
All HERE SDK engines, except for SDKNativeEngine
, can operate independently from each other and require HERE credentials to request data. The credentials can be set in the Info.plist
file as shown in the Get Started guide - or programmatically. This can be useful, for example, to inject credentials at runtime from a web service.
By default, when using a map view in your app, the HERE SDK is initialized automatically and it is reading the credentials from the Info.plist
file. In addition, a default cache path is used for caching map data.
Use SDKOptions to Set HERE Credentials and Cache Path
When you want to set the credentials programmatically, you must take care to initialize the HERE SDK yourself. A good place to do this is the AppDelegate
after application launch - before the MapView
is initialized and any other parts of the HERE SDK are used:
let cachePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.path ?? ""
let sdkOptions = SDKOptions(accessKeyId: "YOUR_ACCESS_KEY_ID",
accessKeySecret: "YOUR_ACCESS_KEY_SECRET",
cachePath: cachePath)
do {
try SDKInitializer.initialize(options: sdkOptions)
} catch {
fatalError("Failed to initialize HERE SDK. Cause: \(error)")
}
This initializes a shared SDKNativeEngine
instance that will then be used for all other engines such as the SearchEngine
- under the hood.
This is all you need to do, and the HERE SDK is then initialized.
Once done - if you wish - you can optionally recreate your own instance of the SDKNativeEngine
, which can then be used to set or to change your HERE SDK credentials again at a later time:
let cachePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.path ?? ""
let sdkOptions = SDKOptions(accessKeyId: "YOUR_ACCESS_KEY_ID",
accessKeySecret: "YOUR_ACCESS_KEY_SECRET",
cachePath: cachePath)
var sdkNativeEngine: SDKNativeEngine
do {
try sdkNativeEngine = SDKNativeEngine(options: sdkOptions)
} catch let engineInstantiationError {
fatalError("Failed to initialize engine. Cause: \(engineInstantiationError)")
}
SDKNativeEngine.sharedInstance = sdkNativeEngine
This sets a shared instance that will then be used for all engines under the hood.
You can either initialize the HERE SDK manually, or add a MapView
. When you show a MapView
, the HERE SDK and the shared SDKNativeEngine
instance gets initialized automatically, and you can start using the SDK, when the map view was loaded (see the Get Started section for this).
When the HERE SDK is initialized, you can proceed to use one of the many available specialized engines. Below, we initialize the SearchEngine
as an example:
do {
try searchEngine = SearchEngine()
} catch let engineInstantiationError {
fatalError("Failed to initialize engine. Cause: \(engineInstantiationError)")
}
Above, we have set an empty string as cachePath
to keep the default cache path - which is also accessible via SDKNativeEngine.sharedInstance?.options.cachePath
.
Note
It is also possible to specify the cache path from the Info.plist
file. Consult the API Reference for the SDKNativeEngine
to see an example.
Multiple SDKNativeEngine
instances can’t have the same access key id and the cache path is ensured to be unique per access key id. After creating a new SDKNativeEngine
, the access key id cannot be changed. Only the secret key can be changed afterwards.
In general, it should not be necessary to initialize the HERE SDK multiple times and only one sharedInstance
can be set.
Alternatively, in rare use cases it may be useful to set an individual SDKNativeEngine
instance for each of the feature engines:
try searchEngine = SearchEngine(sdkNativeEngine)
By default, when you call SDKInitializer.initializeIfNecessary()
without setting SDKOptions
, the HERE SDK's SDKInitializer
will look for the credentials in your Info.plist
file. Therefore, when setting credentials programmatically at a later time, keep the tags holding dummy values for id and secret, like shown in the snippet below. Empty values will lead to an exception:
<key>HERECredentials</key>
<dict>
<key>AccessKeyId</key>
<string>cdefgabc</string>
<key>AccessKeySecret</key>
<string>cdefgabc</string>
</dict>
If you don't set your credentials programmatically, the HERE SDK will be initialized automatically using the values found in the Info.plist
. Either way, invalid credentials will not block execution until these credentials are used to authenticate your app when you start to use an engine to request data - or when you want to show a map view.
Tip: One option to keep credentials secure is to store them on a secure server and retrieve them by requests using SSL connections. Credentials stored in Info.plist
are easy to unveil, a better option can be to use 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 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
It is possible to run an engine as a stand-alone, which means that you do not need to add a map view to your application. This way, you can build an app solely around a specific engine. If you are using an engine as a stand-alone, you must take care to initialize the HERE SDK yourself.
A good place to do this is the AppDelegate
after application launch - before the MapView
is initialized and any other parts of the HERE SDK are used:
do {
try SDKInitializer.initializeIfNecessary()
} catch {
fatalError("Failed to initialize HERE SDK. Cause: \(error)")
}
The SDKInitializer
will look into Info.plist
for the credentials to use. Alternatively, set the credentials programmatically - as shown in the previous section.
Then you can proceed to create a new engine. With or without a map view - the procedure to create a new engine is exactly the same:
do {
try searchEngine = SearchEngine()
} catch let engineInstantiationError {
fatalError("Failed to initialize engine. Cause: \(engineInstantiationError)")
}
When you use the default constructor to initialize an engine for stand-alone usage, the HERE SDK will use a shared SDKNativeEngine
under the hood to take the credentials as found in the Info.plist
file.