GuidesFlutter API ReferencesHERE SDK for Android API referencesHERE SDK for iOS API references
Guides

Best practices

In the following 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.

Optimization strategies

Explore further options to enhance the performance of the HERE SDK:

  • Optimize map caching: Adjust the caching mechanisms by following the guidelines in our map caching documentation.
  • Conditional HERE SDK initialization: To conserve resources, initialize the HERE SDK only when it is necessary.
  • Adjust the frame fate: Modify the frame rate settings of the MapView to balance performance and visual fluidity. Detailed guidance can be found in the frame rate adjustment section below.
  • Simplify visuals with custom map styles: Employ custom map styles that include fewer elements to render, thereby enhancing rendering efficiency and reducing computational load.
  • Preload map data: (only available for Navigate) Use offline maps to download and store map data in advance, allowing for smoother access and reduced reliance on live data connections.

Additionally, the HERE SDK provides versatile configuration options across various engines. For instance, with the SearchEngine, you can define SearchOptions to refine and limit the scope of returned search results.

Handle low memory states

When your application runs on a memory-constrained device or under heavy load, it is important to manage memory efficiently to avoid crashes. The HERE SDK provides mechanisms to optimize its memory usage under such scenarios.

Detect and respond to low memory warnings

To respond to memory pressure from the operating system (e.g., through UIApplication.didReceiveMemoryWarningNotification), implement a handler that listens to memory warnings and takes action accordingly. Below is a possible implementation:

func observeMemoryWarnings() {
  NotificationCenter.default.addObserver(forName: UIApplication.didReceiveMemoryWarningNotification,
                                                           object: nil,
                                                           queue: .main) { _ in
    print("Low memory warning received.")
    self.handleLowMemory()
  }
}   

A possible implementation for handleLowMemory() is shown below

Enable low memory mode and purge the cache

To proactively reduce memory consumption, enable lowMemoryMode in SDKOptions and optionally purge the cache:

func handleLowMemory() {          
  print("System is running extremely low on memory!")
  print("Clearing HERE SDK's internal memory caches.")

  SDKNativeEngine.sharedInstance!.purgeMemoryCaches(strategy: SDKNativeEngine.PurgeMemoryStrategy.full)

  let authenticationMode = AuthenticationMode.withKeySecret(accessKeyId: "YOUR_ACCESS_KEY_ID", accessKeySecret: "YOUR_ACCESS_KEY_SECRET")
  var options = SDKOptions(authenticationMode: authenticationMode)
  options.lowMemoryMode = true

  do {
    try SDKNativeEngine.makeSharedInstance(options: options)
  } catch let engineInstantiationError {
    print("Failed to initialize the HERE SDK. Cause: \(engineInstantiationError.localizedDescription)")
    return
  }
}

Setting lowMemoryMode = true configures the SDK’s internal caches to use less memory. This can be beneficial on devices with limited RAM or during times of high memory pressure.

Turning on the lowMemoryMode will impact the performance of several HERE SDK features such as rendering the map view. Therefore, it may make sense to enable it only after you receive a low-memory warning from the operation system. The next section describes how to do this.

When the system sends memory pressure signals, use the following method to clear the HERE SDK’s internal caches:

SDKNativeEngine.sharedInstance!.purgeMemoryCaches(strategy: SDKNativeEngine.PurgeMemoryStrategy.full)

This will release memory held by all in-memory caches and will help prevent OOM (Out of Memory) crashes.

Currently, only PurgeMemoryStrategy.full is supported, which clears all cached content without preserving any data.

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.

Remove unused voice and font files to reduce the package size

By removing unused voice files like voice_package_ar-SA you can reduce the package size of the HERE SDK. These voice packages are only needed when you want to use turn-by-turn navigation with text-to-speech engines. Note that voice files are only included as part of Navigate.

On top, the HERE SDK contains several font files to render map labels for all supported languages. If you want to optimize the size of the overall app, you can remove selected fonts.

For example, the DroidSansFallback font uses the Simplified Chinese ideographs for shared Unicode code points.

Do the following to remove the unused files:

  • Inside heresdk.framework open the folder ios-arm64/voice_assets and remove the voice packages you do not need.

Here you can find a list of all supported voice languages together with the name of the related voice skin that is stored inside the HERE SDK framework.

On top, the HERE SDK contains several font files to render map labels for all supported languages. If you want to optimize the size of the overall app, you can remove selected fonts.

For example, the DroidSansFallback font uses the Simplified Chinese ideographs for shared Unicode code points.

To remove this font, open the heresdk.framework file and remove the following file:

  • assets/geoviz/fonts/DroidSansFallback.ttf

As another example, if you do not need to render the full set of Japanese characters, you can remove:

  • assets/geoviz/fonts/NotoSansJP-Regular.otf

Note that such fonts may not only be used in Japan and China, but also for other areas around the world when users switch their device language to Japanese or Chinese.

Note

When you remove a font, it is recommended to switch the map language to any other language than the removed font. At least one font needs to be left to see any map labels. Note that each time a label needs to be displayed in a language for a font that was removed an error message is logged - for each character that is missing.

Note that the size of a font file may range from a few hundred kilobytes to a few megabytes in size. After removing selected fonts your application consumes less space when it is installed on a device. The maximum amount that you can save with these steps is approximately 11 MB.

Remove unused truck restriction icons to reduce the package size

You can manually remove unused truck restriction icons from the following paths inside the HERE SDK binary:

- /assets/geoviz/assets/oslo/truck_restriction/night/ui/
- /assets/geoviz/assets/oslo/truck_restriction/day/ui/

These icons are only used when creating vehicle restriction icons with IconProvider.createVehicleRestrictionIcon() and the IconProviderAssetType.ui setting. They are better suited for UI rendering compared to their IconProviderAssetType.map counterparts. If these files are removed, the HERE SDK will automatically fall back to using the map assets.

These SVG icons are included in the HERE SDK binary and occupy approximately 264 KB when compressed.

If you do not plan to use the IconProvider and if you do not need to show the MapFeatures.vehicleRestriction layer, then you can save considerably more space by removing the entire folder assets/geoviz/assets/oslo/truck_restriction.

To remove these files, follow the steps outlined in the section above.

Adjust the frame rate

By default, the MapView is rendered with 60 frames per second (FPS). Via mapView.frameRate the maximum frame rate can be adjusted - for example, to reduce CPU / GPU usage on low end devices. It is also possible to deactivate automatic render cycles by setting FPS to 0. Setting negative values has no effect. The value can be set individually per MapView instance - in case your app contains multiple MapView's.

Another option is to use custom map styles that contain less elements to render.

Protect your 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.

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.
  • Most delegate properties are declared as weak references. Therefore, it is not necessary to manually set those delegates to nil to break the retain cycle between conforming objects and their respective delegate variables.
  • 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.

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.

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.

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.

Prepare your app for distribution

The HERE SDK framework (heresdk.xcframework) is not a fat binary, but it contains set of frameworks for several architectures - built for device (arm64) and simulator (x86_64). Therefore it contains multiple architectures. It consists of:

  • A framework with architecture for device, this framework is used on end user’s device.
  • A framework with two architectures for simulator, this framework is usually necessary only during development.
  • Debug symbols for the device, which are uploaded automatically to Apple's crash analytics server.

This allows easy deployment on a simulator and on a real device. The debug symbols (dSYM) are used to symbolicate crashes in Xcode. Find more details on how to use the dSYM files to symbolicate crashes here.

Note

The file size is optimized by Xcode at deployment time, with unnecessary frameworks being stripped out. Although you can manually remove files such as the dSYM debug symbols, this is not recommended. These files are relatively large, occupying around 80% of the size of the XCFW. However, while they are not necessarily needed during development, the debug symbols are crucial to get symbolized crash stacks for release builds. Note that symbolized crash stacks are treated with higher priority.

Since the HERE SDK framework conforms to Apple's XCFramework bundle type (XCFW), it is ready to be used for the distribution of your app. To build an IPA (iOS App Store Package) file in Xcode, follow below steps:

  1. Before building an IPA, make sure you have the appropriate distribution certificate and provisioning profile for your app. You can obtain these from the Apple Developer portal. Install the distribution certificate in your keychain, and download the provisioning profile to your computer.
  2. Open your project in Xcode: open your app's .xcodeproj or .xcworkspace file in Xcode.
  3. In the top left corner of the Xcode window, select the Active scheme dropdown menu (next to the play and stop buttons). Choose the target device or simulator for which you want to build the IPA.
  4. Select your project in the project navigator (the left sidebar) and navigate to the Signing & Capabilities tab. Under the Signing section, select your development team and ensure the Automatically manage signing checkbox is enabled. Xcode will select the appropriate provisioning profile for you.
  5. Click on your project in the Project navigator, and then click on Edit Scheme in the top menu. In the scheme editor, select the Run action and set the Build Configuration dropdown to Release. Click Close to save your changes.
  6. To build an IPA, you need to archive the project. In Xcode's top menu, click on Product and then choose Clean Build Folder to clean the project. Next, click on Product again and choose Archive to create an archive of your project.
  7. Once the archiving process is complete, the Organizer window will appear. Select your archive in the left column, and then click the Distribute App button on the right side. Choose App Store Connect as the distribution method, and click Next. Choose Upload or Export depending on whether you want to upload the IPA directly to App Store Connect or save it to your computer.

As it is the practice, in Xcode you have to select a development team to sign your app, select a Generic iOS Device and select Product -> Archive.

The binary size of an exported app (that is resulting IPA) for a target device will occupy less space. Note that you can also manually remove files that are not required. However, please keep in mind that the IPA still contains all architectures - unless you explicitly excluded them when archiving: the actual size on the device will occupy much less space and Apple will ensure that only the required architectures are included. Thus, also the actual download size from the App Store will be much smaller. See also this Xcode guide. Therefore, we recommend to keep the HERE SDK framework as it is when the app is deployed through the App Store - since all size optimization happens automatically as part of the deployment process.

Note

An Apple Developer Account is needed when you plan to distribute your app on a real device or share with others, for example, to publish it on Apple's App Store. Without an Apple Developer Account you can run it on an iOS simulator. Alternatively, create Xcode 'Free Provisioning' (Personal Team) profile, which can be used to try your application on a real device. Note that this profile is valid only for limited time.

Privacy manifest files

As of May 2024, every release of an iOS application to the App Store requires the declaration of a privacy manifest in form of a PrivacyInfo.xcprivacy file. By default, this file is included in the heresdk.xcframework (XCFW) at the following paths:

  • heresdk.xcframework/ios-arm64/heresdk.framework/PrivacyInfo.xcprivacy
  • heresdk.xcframework/ios-arm64_x86_64-simulator/heresdk.framework/PrivacyInfo.xcprivacy

Publishing an update (Explore)

If you need to publish an update for older versions prior to HERE SDK 4.18.2.0 please insert the privacy manifest file manually into the XCFW and use the below content:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>NSPrivacyTracking</key>
    <false />
    <key>NSPrivacyTrackingDomains</key>
    <array />
    <key>NSPrivacyAccessedAPITypes</key>
    <array>
      <dict>
        <key>NSPrivacyAccessedAPIType</key>
        <string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
        <key>NSPrivacyAccessedAPITypeReasons</key>
        <array>
          <string>C617.1</string>
          <string>0A2A.1</string>
        </array>
      </dict>
      <dict>
        <key>NSPrivacyAccessedAPIType</key>
        <string>NSPrivacyAccessedAPICategorySystemBootTime</string>
        <key>NSPrivacyAccessedAPITypeReasons</key>
        <array>
          <string>35F9.1</string>
        </array>
      </dict>
      <dict>
        <key>NSPrivacyAccessedAPIType</key>
        <string>NSPrivacyAccessedAPICategoryDiskSpace</string>
        <key>NSPrivacyAccessedAPITypeReasons</key>
        <array>
          <string>E174.1</string>
        </array>
      </dict>
    </array>
  </dict>
</plist>

Publishing an update (Navigate)

If you need to publish an update for older versions prior to HERE SDK 4.18.2.0 please insert the privacy manifest file manually into the XCFW and use the below content:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>NSPrivacyTracking</key>
    <false />
    <key>NSPrivacyTrackingDomains</key>
    <array />
    <key>NSPrivacyAccessedAPITypes</key>
    <array>
      <dict>
        <key>NSPrivacyAccessedAPIType</key>
        <string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
        <key>NSPrivacyAccessedAPITypeReasons</key>
        <array>
          <string>C617.1</string>
          <string>0A2A.1</string>
        </array>
      </dict>
      <dict>
        <key>NSPrivacyAccessedAPIType</key>
        <string>NSPrivacyAccessedAPICategorySystemBootTime</string>
        <key>NSPrivacyAccessedAPITypeReasons</key>
        <array>
          <string>35F9.1</string>
        </array>
      </dict>
      <dict>
        <key>NSPrivacyAccessedAPIType</key>
        <string>NSPrivacyAccessedAPICategoryDiskSpace</string>
        <key>NSPrivacyAccessedAPITypeReasons</key>
        <array>
          <string>E174.1</string>
        </array>
      </dict>
    </array>
    <key>NSPrivacyCollectedDataTypes</key>
    <array>
      <dict>
        <key>NSPrivacyCollectedDataType</key>
        <string>NSPrivacyCollectedDataTypeCoarseLocation</string>
        <key>NSPrivacyCollectedDataTypeLinked</key>
        <false />
        <key>NSPrivacyCollectedDataTypeTracking</key>
        <false />
        <key>NSPrivacyCollectedDataTypePurposes</key>
        <array>
          <string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
        </array>
      </dict>
      <dict>
        <key>NSPrivacyCollectedDataType</key>
        <string>NSPrivacyCollectedDataTypePreciseLocation</string>
        <key>NSPrivacyCollectedDataTypeLinked</key>
        <false />
        <key>NSPrivacyCollectedDataTypeTracking</key>
        <false />
        <key>NSPrivacyCollectedDataTypePurposes</key>
        <array>
          <string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
        </array>
      </dict>
    </array>
  </dict>
</plist>