# \[iOS]プッシュ通知を受信する

{% hint style="warning" %}
**画像などを添付したプッシュ通知に対応する場合**

画像や動画などを添付したリッチプッシュ通知に対応するにはExtensionの作成が必要です。\
詳しくは [リッチプッシュ通知に対応する](https://app.developers.karte.io/ios-sdk-appendix/appendix-rich-notification-ios-sdk) をご覧ください。
{% endhint %}

プッシュ通知を受信したい場合は、リモート通知機能を利用することで実現可能です。

リモート通知機能は、KARTE SDKの導入及び`KarteRemoteNotification` モジュールを導入することで利用可能です。

* 参考：[SDKを導入する](https://app.developers.karte.io/ios-sdk/setup-ios-sdk)

なお KARTE ではプッシュ通知の送信に、Firebase Cloud Messaging（以下FCM）を利用しています。\
そのためFCM経由でプッシュ通知を送信するために各種設定を行う必要があります。

また受信側アプリケーションでも FCM SDK の導入が必要となります。

## 導入手順

### 1. FCM SDK を導入する

1. SDKを導入する\
   導入に関しては、下記ドキュメントをご覧ください。\
   [Set up a Firebase Cloud Messaging client app on iOS](https://firebase.google.com/docs/cloud-messaging/ios/client)
2. Notification Composer で通知のテストを行う\
   Firebase の Notification Composer から通知メッセージを送信し、メッセージが受信できるか確認してください。\
   [Send a notification message](https://firebase.google.com/docs/cloud-messaging/ios/first-message#send_a_notification_message)

### 2. サービスアカウントの設定を行う

KARTEからFCMに対して通知の送信リクエストを行うために、KARTE側にサービスアカウントの設定を行う必要があります。\
[サービスアカウントを設定する](https://app.developers.karte.io/app-send-notification/app-setup-service-account)

### 3. KARTE SDK を導入する

1. Podfile の編集\
   プロジェクトディレクトリにある `Podfile` を任意のエディタで開き、`KarteRemoteNotification` の Pod を追加します。

{% code title="Podfile" overflow="wrap" %}

```ruby
pod 'KarteRemoteNotification'
```

{% endcode %}

2. Pod のインストール\
   プロジェクトディレクトで下記コマンドを実行し、Pod をインストールします。

{% code overflow="wrap" %}

```bash
pod install
```

{% endcode %}

## 実装手順

### 1. FCMトークンを送信する

KARTE からプッシュ通知を送信するためには、FCMトークンが必要となります。\
そのためアプリケーションからKARTEにFCMトークンを送信する処理を実装します。

1. SDKのインポート宣言を追加\
   `messaging(_:didReceiveRegistrationToken:)` を実装しているファイルの先頭にインポート宣言を追加します。

{% tabs %}
{% tab title="Swift" %}
{% code overflow="wrap" %}

```swift
import KarteRemoteNotification
```

{% endcode %}
{% endtab %}

{% tab title="Objective-C" %}
{% code overflow="wrap" %}

```objc
@import KarteRemoteNotification;
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. FCMトークンの送信処理を追加\
   `messaging(_:didReceiveRegistrationToken:)` メソッド内に送信処理を追加します。

{% tabs %}
{% tab title="Swift" %}
{% code overflow="wrap" %}

```swift
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
  // FCMトークンが生成/更新されたタイミングで、KARTE側にFCMトークンを登録
  KarteApp.registerFCMToken(fcmToken)
}
```

{% endcode %}
{% endtab %}

{% tab title="Objective-C" %}
{% code overflow="wrap" %}

```objc
- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken
{
  // FCMの登録トークンが生成/更新されたタイミングで、KARTE側に登録トークンを送信
  [KRTApp registerFCMToken:fcmToken];
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

[\[ 参考\]FCM登録トークンや、アプリPUSH通知許可設定がKARTEに送信されるタイミング](https://support.karte.io/post/38slBGumICBZdaURAESXvU#1-0)

### 2. 通知開封時の処理を実装する

受信したプッシュ通知をタップ（開封）した際、通知メッセージに含まれているリンクを開くためには下記の実装が必要となります。\
※プッシュ通知のタップ時に、任意の画面に遷移させる等の処理をさせたい場合には必須の実装です。

なおクラスやメソッドの詳細は随時[リファレンス](https://plaidev.github.io/karte-sdk-docs/ios/latest/KarteRemoteNotification/Classes/RemoteNotification.html)を参照ください。\
また通知タップ時には計測用のイベントが自動で送信されます、送信されない場合は[FAQ: 通知の開封を検知できません](https://app.developers.karte.io/app-faq/app-faq-unable-to-detect-the-opening-of-the-notification)を参照下さい。\
※Messageからの送信の場合は`mass_push_click` 、それ以外での送信の場合は`message_click`が発生します。

**Case1. UserNotifications.frameworkを利用するアプリケーションの場合**

{% tabs %}
{% tab title="Swift" %}
{% code overflow="wrap" %}

```swift
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
  let userInfo = response.notification.request.content.userInfo

  // KARTE経由のプッシュ通知であるか判定
  if let notification = RemoteNotification(userInfo: userInfo) {
    // KARTE経由のプッシュ通知のリンクを開く
    notification.handle()
  } else {
    // KARTE以外のシステムから送信されたプッシュ通知
  }
  completionHandler()
}
```

{% endcode %}
{% endtab %}

{% tab title="Objective-C" %}
{% code overflow="wrap" %}

```objc
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
  NSDictionary *userInfo = response.notification.request.content.userInfo;

  // KARTE経由のプッシュ通知であるか判定
  KRTRemoteNotification *notification = [[KRTRemoteNotification alloc] initWithUserInfo:userInfo];
  if (notification) {
    // KARTE経由のプッシュ通知のリンクを開く
    [notification handle];
  } else {
    // KARTE以外のシステムから送信されたプッシュ通知
  }
  completionHandler();
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

**Case2. UserNotifications.frameworkを利用しないまたはiOS10未満をサポートするアプリケーションの場合**

{% tabs %}
{% tab title="Swift" %}
{% code overflow="wrap" %}

```swift
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  switch application.applicationState {
    case .active, .inactive:
      // KARTE経由のプッシュ通知であるか判定
      if let notification = RemoteNotification(userInfo: userInfo) {
        // KARTE経由のプッシュ通知のリンクを開く
        notification.handle()
      } else {
        // KARTE以外のシステムから送信されたプッシュ通知
      }
    case .background:
      break
  }
  completionHandler(UIBackgroundFetchResult.newData)
}
```

{% endcode %}
{% endtab %}

{% tab title="Objective-C" %}
{% code overflow="wrap" %}

```objc
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler
{
  switch (application.applicationState) {
    case UIApplicationStateActive:
    case UIApplicationStateInactive:
    {
      // KARTE経由のプッシュ通知であるか判定
      KRTRemoteNotification *notification = [[KRTRemoteNotification alloc] initWithUserInfo:[NSDictionary dictionary]];
      if (notification) {
        // KARTE経由のプッシュ通知のリンクを開く
        [notification handle];
      } else {
        // KARTE以外のシステムから送信されたプッシュ通知
      }
      break;
    }
    case UIApplicationStateBackground:
      break;
  }
  completionHandler(UIBackgroundFetchResultNewData);
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

なお通知メッセージに含まれるURLを独自に処理したい場合は、`RemoteNotification` クラスの `url` プロパティを参照することで、URLのみを取り出すことが可能です。\
その他の変数の取得については[デフォルト変数とペイロード値の対応関係について](https://app.developers.karte.io/app-send-notification/app-customize-notification-payload#デフォルト変数とペイロード値の対応関係について)をご確認ください。

{% tabs %}
{% tab title="Swift" %}
{% code overflow="wrap" %}

```swift
if let url = RemoteNotification(userInfo: userInfo)?.url {
  // KARTE経由のプッシュ通知かつURLが設定されている場合
  // URLに応じて独自にリンクを処理する
}
```

{% endcode %}
{% endtab %}

{% tab title="Objective-C" %}
{% code overflow="wrap" %}

```objc
NSURL *url = [[[KRTRemoteNotification alloc] initWithUserInfo:userInfo] url];
if (url) {
  // KARTE経由のプッシュ通知かつURLが設定されている場合
  // URLに応じて独自にリンクを処理する
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Universal Links を利用してアプリ内に遷移させるには？**

`handle` メソッドが内部で利用している、 `UIApplication` クラスの `openURL` メソッドは、Universal Links を適切に処理することができないため、そのままでは `Safari` が起動してしまいます。\
そのため Universal Links をプッシュ通知に指定する場合は、アプリケーション側で通知クリック後の処理を実装する必要があります。\
参考：[Universal Links に対応する](https://app.developers.karte.io/ios-sdk-appendix/appendix-universal-links-ios-sdk)
{% endhint %}

## 動作確認

最後に正しく導入が行われているか確認を行うためにテストメッセージを送信して確認を行います。

詳細については、下記ドキュメントをご覧ください。\
[テストメッセージを送信する](https://app.developers.karte.io/app-send-notification/app-test-notification)

## トラブルシューティング

プッシュ通知に問題がある場合は、[KARTE for Appプッシュ通知で問題が発生した時のチェックリスト](https://support.karte.io/post/5IUmM2QOEjJjyCE5Lq9aEf)を参考にしてください。

**What’s Next**

画像付きのプッシュ通知に対応する場合は、以下のドキュメントをご覧ください。

* [リッチプッシュ通知に対応する](https://app.developers.karte.io/ios-sdk-appendix/appendix-rich-notification-ios-sdk)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://app.developers.karte.io/ios-sdk/notification-ios-sdk.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
