ガイド変更履歴HERE SDK API references
ガイド

ベスト プラクティス

以下のセクションでは、HERE SDK for iOSを最大限に活用できるように、最も一般的なユースケースについて説明し、ヒントとわかりやすいガイドラインを示します。

最適化の計画

HERE SDKのパフォーマンスを向上させるためのその他のオプションを確認できます。

  • マップキャッシュを最適化する:マップ キャッシュ ドキュメントのガイドラインに従って、キャッシュ メカニズムを調整します。
  • **条件付きのHERE SDKの初期化:**リソースを節約するには、必要な場合にのみ HERE SDK を初期化します。
  • フレームレートを調整する:MapViewのフレームレート設定を変更して、パフォーマンスと表示の滑らかさのバランスをとります。詳細なガイダンスについては、以下のフレームレートの調整に関するセクションを参照してください。
  • **カスタムマップスタイルでビジュアルを簡素化する:**レンダリングする要素の数が少ないカスタムマップスタイルを使用することで、レンダリングの効率が向上し、データ処理の負荷が軽減されます。
  • マップデータをプリロードする:(Navigateでのみ使用可能) オフラインマップを使用して事前にマップデータをダウンロードして保存することで、アクセスがスムーズになり、ライブデータ接続への依存が軽減されます。

さらに、HERE SDKでは、さまざまなエンジンで多様な設定オプションを提供しています。たとえばSearchEngineを利用すると、SearchOptionsを定義して、返される検索結果の範囲を絞り込んで制限できます。

低メモリ状態を処理する

アプリケーションがメモリに制約のあるデバイス上で実行される場合や、負荷が高い場合は、クラッシュを回避するためにメモリを効率的に管理することが重要です。HERE SDKには、このようなシナリオでメモリ使用量を最適化するメカニズムが用意されています。

低メモリの警告を検出して対応する

オペレーティングシステムからの (UIApplication.didReceiveMemoryWarningNotificationなどを通じた) メモリ不足の圧力に対応するために、メモリ警告をリッスンし、それに応じてアクションを実行するハンドラーを実装します。以下は、考えられる実装の1つです。

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

handleLowMemory()の実装例を以下に示します。

低メモリモードを有効にしてキャッシュを消去する

メモリ消費を事前に削減するには、SDKOptionslowMemoryModeを有効にし、必要に応じてキャッシュを消去します。

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
  }
}

lowMemoryMode = trueを設定すると、SDKの内部キャッシュがより少ないメモリを使用するように構成されます。これは、RAMが制限されているデバイスや、メモリの圧力が高い場合に役立ちます。

lowMemoryModeをオンにすると、マップビューのレンダリングなど、HERE SDKのいくつかの機能のパフォーマンスに影響します。このため、オペレーティングシステムから低メモリ警告を受け取った後にのみ有効にするのが合理的な場合もあります。次のセクションでは、これを行う方法について説明します。

システムからメモリ圧力信号が送信された場合は、次のメソッドを使用してHERE SDKの内部キャッシュをクリアします。

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

これにより、すべてのメモリ内のキャッシュに保持されているメモリが解放され、OOM (Out of Memory) クラッシュを防止できます。

現在サポートされているのはPurgeMemoryStrategy.fullのみであり、これにより、どのデータも保持せずに、キャッシュに保存されたすべてのコンテンツがクリアされます。

無料のリソース

アプリの有効期間が終了したら、SDKNativeEngine.sharedInstance = nil を呼び出してリソースを開放できます。また、SDKNativeEngine へのすべての参照を nil に設定する必要があります (存在する場合)。この呼び出し後は、HERE SDK を再度初期化しない限り、関連する HERE SDK の機能は使用できなくなります。デフォルトのコンストラクタを使用して SearchEngine() または RoutingEngine() などのエンジンを作成した場合は、これらのインスタンスも再作成します。基本的に、nil に設定したら、同じ SDKNativeEngine のインスタンスを使用していたすべてのエンジンを再作成する必要があります。

未使用の音声ファイルとフォント ファイルを削除してパッケージ サイズを小さくする

voice_package_ar-SAなどの未使用の音声ファイルを削除することで、HERESDKのパッケージサイズを小さくできます。これらの音声パッケージファイルは、音声合成エンジンを使用したターン・バイ・ターンナビを使用する場合にのみ必要です。音声ファイルはNavigateの一部としてのみ含まれます。

さらに、HERE SDKには、サポートされているすべての言語でマップラベルをレンダリングするために、いくつかのフォントファイルが含まれています。アプリ全体のサイズを最適化するために、選択したフォントを削除できます。

たとえば、DroidSansFallbackフォントでは、共有のUnicodeコードポイントに簡体字中国語の表意文字が使用されています。

未使用のファイルを削除するには、以下の手順を実行します。

  • heresdk.framework 内の ios-arm64/voice_assets フォルダーを開き、不要な音声パッケージを削除します。

こちらでは、サポートされているすべての音声言語のリストと、HERE SDK フレームワーク内に保存されている関連する音声スキンの名前を確認できます。

さらに、HERE SDKには、サポートされているすべての言語でマップラベルをレンダリングするために、いくつかのフォントファイルが含まれています。アプリ全体のサイズを最適化するために、選択したフォントを削除できます。

たとえば、DroidSansFallbackフォントでは、共有のUnicodeコードポイントに簡体字中国語の表意文字が使用されています。

このフォントを削除するには、heresdk.framework ファイルを開き、次のファイルを削除します。

  • assets/geoviz/fonts/DroidSansFallback.ttf

別の例として、日本語文字の完全なセットをレンダリングする必要がない場合は、以下を削除できます。

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

なお、これらのフォントは日本と中国で使用されるだけでなく、世界中の他の地域でもユーザーがデバイスの言語を日本語または中国語に切り替える際に使用される可能性があります。

フォントを削除する場合は、削除したフォント以外の言語に地図言語を切り替えることをお勧めします。マップラベルを表示するには、少なくとも1つのフォントを残す必要があります。削除されたフォントの言語でラベルを表示する必要があるたびに、欠落している各文字に対してエラーメッセージが記録されます。

フォントファイルのサイズは、数百キロバイトから数メガバイトの幅があります。選択したフォントを削除すると、アプリケーションがデバイスにインストールされたときに消費される領域が少なくなります。この手順で節約できる最大容量は、約11MBです。

未使用のトラック制限アイコンを削除してパッケージサイズを削減する

HERE SDKバイナリ内の以下のパスから、未使用のトラック制限アイコンを手動で削除できます。

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

これらのアイコンは、IconProvider.createVehicleRestrictionIcon()およびIconProviderAssetType.ui設定を使用して以下の車両制限アイコンを作成する場合にのみ使用されます。これらは、対応するIconProviderAssetType.mapと比べて、UIレンダリングに適しています。これらのファイルが削除されると、HERE SDKは自動的にmapアセットを使用するようにフォールバックします。

これらのSVGアイコンはHERE SDKバイナリに含まれており、圧縮時に約264 KBを占有します。

IconProviderを使用する予定がなく、MapFeatures.vehicleRestrictionレイヤーを表示する必要もない場合は、assets/geoviz/assets/oslo/truck_restrictionフォルダー全体を削除することで、さらに大幅に容量を節約できます。

これらのファイルを削除するには、上記のセクションで説明されている手順に従ってください。

フレームレートを調整する

デフォルトでは、MapViewは60フレーム/秒 (FPS) でレンダリングされます。mapView.frameRate を使用して最大フレーム レートを調整し、たとえば、ローエンド デバイスでの CPU/GPU の使用量を削減できます。FPSを0に設定することで、自動レンダリングサイクルを非アクティブ化することもできます。負の値を設定しても効果はありません。この値は、アプリに複数のMapViewが含まれている場合は、MapViewインスタンスごとに設定できます。

もう 1 つのオプションは、レンダリングする要素が少ないカスタム マップ スタイルを使用することです。

資格情報を保護する

資格情報は使用を意図していない第三者による不正使用を防止するために保護します。

資格情報の安全を確保するための1つの選択肢は、資格情報をセキュアなサーバーに保存し、SSL接続とキーチェーンデータ保護などのデータ保護メカニズムを使用してリクエストによって取得することです。

ベストプラクティスとして、以下の点を考慮してください。

  • 秘密データをプレーンテキストで保持しないようにする。
  • 安全な通信チャネルを使用して資格情報を転送する。
  • デバイス セキュリティや強力な暗号化機能などの暗号化を使用して資格情報を保存します。
  • 改ざん防止とアンチデバッグのコードを追加して、潜在的な攻撃者が動的なランタイム実行中にデータを傍受できないようにする。
  • アプリケーションの使用状況を追跡して異常を検出する。

完了ハンドラーとデリゲート

  • HERE SDK は、検索結果などの単一イベント通知の完了ハンドラーを使用します。
  • ジェスチャー イベントなどの繰り返し発生するイベント通知には、デリゲートが使用されます。複数のデリゲートを設定できる場合は、メソッド パターン add_x()remove_x() は命名規則として使用されます。一度に設定できるデリゲートが 1 つだけの場合は、nil を設定してリッスンを停止できるプロパティが使用されます。
  • ほとんどのデリゲート プロパティは弱参照として宣言されています。そのため、これらのデリゲートを手動で nil に設定して、準拠オブジェクトと各デリゲート変数間の保持サイクルを停止する必要がなくなりました。
  • 開発者は、完了ハンドラーまたはデリゲートのスコープ内のエラーを適切に処理する責任があります。ガイドラインとして、例外をスローする可能性のあるコードを処理してください。

TaskHandlesを使用して非同期操作をキャンセルする

ほとんどの非同期メソッドは、即時の戻り値としてTaskHandleを提供しますが、操作の最終結果は遅れて完了ハンドラーで返されます。TaskHandleはステータス情報を提供し、進行中の操作を中止できます。

Objective-C のサポート

現在、HERE SDK では Objective-C はサポートされておらず、ブリッジング ヘッダーは使用できません。コードベースが Objective-C で記述されている場合は、Swift への移行を検討してください。

ただし、既存の Objective-C プロジェクトに統合されている Swift ファイルから HERE SDK に直接アクセスできます。Swift と Objective-C を 1 つのプロジェクトに混在させることはできますが、現時点では Objective-C から HERE SDK に直接アクセスすることはできません。

外部REST APIで使用するアクセストークンを取得する

HERE SDKが初期化されるたびに、新しいアクセストークンが内部的に生成されます。複数のSDKNativeEngineインスタンスがある場合、各インスタンスは独自のトークンを保持します。トークンは Authentication.authenticate(SDKNativeEngine.sharedInstance, callback) を介して更新および取得することもできます。このコールバックでは authenticationData.token を介してトークンが提供され、このトークンを使用すると外部の HERE REST API にアクセスできます。

HERE SDK自体を使用するためにトークンを把握する必要はありません。トークンは内部的にのみ必要とされ、HERE SDKはトークン生成を自動的に処理します。

バックエンドを不正使用から保護するため、生成されたトークンが多すぎる場合、または短期間に非常に多くのサービスにアクセスがあった場合は、レート制限が適用される場合があります。このような場合、エンジンはrequestLimitReachedまたは類似するエラーを返します。アプリの使用率が非常に高くなることが予想される場合は、HERE担当者に事前に連絡し、必要に応じて制限を調整してください。

配布用のアプリを準備する

HERE SDK フレームワーク (heresdk.xcframework) はファット バイナリではありませんが、デバイス (arm64) とシミュレーター (x86_64) 用に構築された、複数のアーキテクチャ用のフレームワーク一式が含まれています。したがって、複数のアーキテクチャが含まれています。構成内容は次のとおりです。

  • デバイスのアーキテクチャを備えたフレームワーク。このフレームワークはエンド ユーザーのデバイスで使用されます。
  • シミュレーター用の 2 つのアーキテクチャを持つフレームワーク。通常、このフレームワークは開発中にのみ必要です。
  • デバイスのデバッグ シンボル。Apple のクラッシュ アナリティクス サーバーに自動的にアップロードされます。

これにより、シミュレーターおよび実際のデバイスに簡単にデプロイできます。デバッグ シンボル (dSYM) は Xcode でクラッシュをシンボル化するために使用されます。dSYM ファイルを使用してクラッシュをシンボル化する方法の詳細については、こちらをご覧ください。

ファイル サイズはデプロイ時に Xcode によって最適化され、不要なフレームワークは削除されます。dSYM デバッグ シンボルなどのファイルを手動で削除することはできますが、お勧めはしません。これらのファイルは比較的サイズが大きく、XCFW の約 80% を占有しています。ただし、デバッグ シンボルは開発中に必ずしも必要ではありませんが、リリース ビルドでシンボル化されたクラッシュ スタックを取得するためには欠かせません。シンボル化されたクラッシュ スタックは、高い優先順位で処理されるためご注意ください。

HERE SDK フレームワークは Apple の XCFramework バンドル タイプ (XCFW) に準拠しているため、アプリのディストリビューションに使用できます。Xcode で IPA (iOS App Store Package) ファイルを構築するには、次の手順に従います。

  1. IPA を構築する前に、アプリに適切なディストリビューション証明書とプロビジョニング プロフィールがあることを確認してください。これらは Apple Developer ポータルから入手できます。ディストリビューション証明書をキーチェーンにインストールし、プロビジョニング プロフィールをコンピューターにダウンロードします。
  2. Xcode でプロジェクトを開きます。アプリの .xcodeproj ファイルまたは .xcworkspace ファイルを Xcode で開きます。
  3. Xcode ウィンドウの左上隅で、アクティブなスキームのドロップダウン メニュー (再生ボタンと停止ボタンの横) を選択します。IPA を構築するターゲット デバイスまたはシミュレーターを選択します。
  4. プロジェクト ナビゲーター (左側のサイドバー) でプロジェクトを選択し、[Signing & Capabilities](署名と機能) タブに移動します。[Signing](署名) セクションで開発チームを選択し、[Automatically manage signing](自動的に署名を管理) チェックボックスがオンになっていることを確認します。Xcode で適切なプロビジョニング プロフィールが選択されます。
  5. プロジェクト ナビゲーターでプロジェクトをクリックし、上部メニューの [Edit Scheme](スキームを編集) をクリックします。スキーム エディターで、[Run](実行) アクションを選択し、[Build Configuration](設定を構築) ドロップダウンを [Release](リリース) に設定します。[Close](閉じる) をクリックして変更内容を保存します。
  6. IPA を作成するには、プロジェクトをアーカイブする必要があります。Xcode の上部メニューで [Product](製品) をクリックし、[Clean Build Folder](構築したフォルダーを消去) を選択してプロジェクトを消去します。次に、[Product](製品) をもう一度クリックし、[Archive](アーカイブ) を選択してプロジェクトのアーカイブを作成します。
  7. アーカイブ処理が完了すると、[Organizer](オーガナイザー) ウィンドウが表示されます。左側の列でアーカイブを選択し、右側の [Distribute App](アプリの配布) ボタンをクリックします。配布方法として [App Store Connect](アプリ ストア接続) を選択し、[Next](次へ) をクリックします。IPA を直接[App Store Connect](アプリ ストア接続) にアップロードするか、コンピューターに保存するかに応じて、[Upload](アップロード) または [Export](エクスポート) を選択します。

Xcode では、アプリに署名する開発チームを選択し、[Generic iOS Device](一般的な iOS デバイス) を選択して、[Product](製品) -> [Archive](アーカイブ) を選択する必要があります。

ターゲット デバイスにエクスポートされたアプリ (結果として得られる IPA) のバイナリ サイズでは、使用する領域は少なくなります。不要なファイルは手動で削除することもできます。ただし、アーカイブ時に明示的に除外しない限り、IPA にはすべてのアーキテクチャが含まれることに注意してください。デバイスの実際のサイズでは、使用する領域は非常に少なく、Apple は必要なアーキテクチャのみが含まれるようにします。したがって、App Store から実際にダウンロードするときのサイズも非常に小さくなります。こちらの Xcode のガイドも参照してください。そのため、App Store を介してアプリをデプロイするときは、HERE SDK フレームワークをそのままにすることをお勧めします。サイズの最適化は、すべてデプロイ処理の一部として自動で行われるためです。

アプリを実際のデバイスにディストリビューションしたり、Apple の App Store で公開するなどして他の人と共有したりする場合は、Apple Developer アカウントが必要です。Apple Developer アカウントがない場合は、iOS シミュレーターで実行できます。または、Xcode の「無料プロビジョニング」 (Personal Team) プロフィールを作成します。このプロフィールを使用すると、実際のデバイスでアプリケーションを試用できます。このプロフィールは期間限定で有効です。

プライバシー マニフェスト ファイル

2024 年 5 月現在、iOS アプリケーションを App Store にリリースする際には常に、PrivacyInfo.xcprivacy ファイル形式でプライバシー マニフェストの宣言が必要です。デフォルトでは、このファイルは次のパスにある heresdk.xcframework (XCFW) に配置されています。

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

更新を公開する (Explore)

HERE SDK 4.18.2.0以前の古いバージョンの更新を公開する必要がある場合は、プライバシーマニフェストファイルを手動でXCFWに挿入し、以下のコンテンツを使用してください。

<?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>

更新を公開する (Navigate)

HERE SDK 4.18.2.0以前の古いバージョンの更新を公開する必要がある場合は、プライバシーマニフェストファイルを手動でXCFWに挿入し、以下のコンテンツを使用してください。

<?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>

EN 日本語

HERE documentation

Find answers to your product and technical questions

Documentation

What's new

Videos

EN 日本語

HERE ドキュメント

製品や技術に関する質問の答えを見つけましょう。より多くの内容と最新の情報については、英語版をご覧ください。

ドキュメント

ダイナミックマップ

動的コンテンツ関連のAPIをアプリやサービスに活用して、ドライバーが安全・快適かつ予定どおりに目的地へ到着できるよう支援します。

地図とデータ

世界中を走行する多数のマッピング車両から得られる最新の位置情報データを活用し、精度の高い地図やカスタムレイヤーを構築できます。

最新情報

動画

(function () { const input = document.querySelector('input[data-typeahead]'); if (!input) return; // Prevent the form from submitting/navigating input.closest('form')?.addEventListener('submit', e => e.preventDefault()); input.addEventListener('input', function () { const q = this.value.trim().toLowerCase(); document.querySelectorAll('.nav-group-name').forEach(group => { let anyVisible = false; group.querySelectorAll('.nav-group-task').forEach(task => { const text = task.textContent.trim().toLowerCase(); const show = !q || text.includes(q); task.style.display = show ? '' : 'none'; if (show) anyVisible = true; }); // Hide the whole group header if nothing matches group.style.display = anyVisible || !q ? '' : 'none'; }); }); })(); (function () { function onTokenClick(event) { var link = event.target.closest('.sdk-for-ios .item .token'); if (!link) return; event.preventDefault(); console.log('token clicked', link.textContent.trim()); var item = link.closest('.item'); if (!item) return; var content = item.querySelector('.height-container'); if (!content) { console.log('no .height-container found for item', item); return; } var isHidden = window.getComputedStyle(content).display === 'none'; content.style.display = isHidden ? 'block' : 'none'; link.classList.toggle('token-open', isHidden); var href = link.getAttribute('href'); if (href) { if (history.pushState) history.pushState({}, '', href); else location.hash = href; } } function openHashTarget() { var hash = window.location.hash.slice(1); if (!hash) return; var anchor = document.querySelector('.sdk-for-ios a[name="' + hash + '"]'); if (!anchor) return; var item = anchor.closest('.item'); if (!item) return; var link = item.querySelector('.token'); var content = item.querySelector('.height-container'); if (!link || !content) return; content.style.display = 'block'; link.classList.add('token-open'); } function init() { console.log('HERE SDK accordion init'); openHashTarget(); } document.removeEventListener('click', onTokenClick); document.addEventListener('click', onTokenClick); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } window.addEventListener('hashchange', openHashTarget); window.addEventListener('pageLoad', init); })();