Skip to content

Swift のライブアクティビティ

Swift Braze SDK用にライブアクティビティを実装する方法について説明します。ライブアクティビティは、ロック画面に直接表示される永続的でインタラクティブな通知で、ユーザーはデバイスのロックを解除することなく、ダイナミックなリアルタイム更新を得ることができます。

iPhoneのロック画面に表示された配達トラッカーのライブアクティビティ。車のアイコンが付いたステータスバーがほぼ半分まで進んでいる。テキストには「2 min until pickup」と表示されている

仕組み

ライブアクティビティは、静的な情報と更新可能なダイナミックな情報を組み合わせて表示します。例えば、配達のステータストラッカーを提供するライブアクティビティを作成できます。このライブアクティビティには、静的な情報として会社名が含まれるほか、配達ドライバーが目的地に近づくにつれて更新されるダイナミックな「配達までの時間」も含まれます。

開発者は、Brazeを使用してライブアクティビティのライフサイクルを管理し、Braze REST APIを呼び出してライブアクティビティの更新を行い、購読しているすべてのデバイスにできるだけ早く更新を配信できます。また、Brazeを通じてライブアクティビティを管理しているため、プッシュ通知、アプリ内メッセージ、Content Cardsなど、他のメッセージングチャネルと組み合わせて活用を促進できます。

シーケンス図

図を表示
---
config:
  theme: mc
---
sequenceDiagram
  participant Server as Client Server
  participant Device as User Device
  participant App as iOS App / Braze SDK
  participant BrazeAPI as Braze API
  participant APNS as Apple Push Notification Service
  Note over Server, APNS: Launch Option 1<br/>Locally Start Activities
  App ->> App: Register a Live Activity using <br>`launchActivity(pushTokenTag:activity:)`
  App ->> App: Get push token from iOS
  App ->> BrazeAPI: Activity ID & Push token<br>automatically sent to Braze
  Note over Server, APNS: Launch Option 2<br/>Remotely Start Activities
  Device ->> App: Call `registerPushToStart`<br>to collect push tokens early
  App ->> BrazeAPI: Push-to-start tokens sent to Braze
  Server ->> BrazeAPI: POST /messages/live_activity/start
  Note right of BrazeAPI: Payload includes:<br>- push_token<br>- activity_id<br>- external_id<br>- event_name<br>- content_state (optional)
  BrazeAPI ->> APNS: Live activity start request
  APNS ->> Device: APNS sends activity to device
  App ->> App: Get push token from iOS
  App ->> BrazeAPI: Activity ID & Push token<br>automatically sent to Braze
  Note over Server, APNS: Resuming activities upon app launch
  App ->> App: Call `resumeActivities(ofType:)` on each app launch
  Note over Server, APNS: Updating a Live Activity
  loop update a live activity
  Server ->> BrazeAPI: POST /messages/live_activity/update
  Note right of BrazeAPI: Payload includes changes<br>to ContentState (dynamic variables)
  BrazeAPI ->> APNS: Update sent to APNS
  APNS ->> Device: APNS sends update to device
  end
  Note over Server, APNS: Ending a Live Activity
  Server ->> BrazeAPI: POST /messages/live_activity/update
  Note right of BrazeAPI: Activity can be ended via:<br> - User manually dismisses<br>- Times out after 12 hours<br>- Setting `end_activity: true` on `/messages/live_activity/update`
  APNS ->> Device: Live activity is dismissed

ライブアクティビティの実装

前提条件

この機能を使う前に、Swift Braze SDKを統合する必要がある。 さらに、以下を完了する必要があります。

  • プロジェクトがiOS 16.1以降をターゲットにしていることを確認します。
  • XcodeプロジェクトのSigning & CapabilitiesPush Notificationエンタイトルメントを追加します。
  • 通知の送信に.p8キーを使用していることを確認します。.p12.pemなどの古いファイルはサポートされていません。
  • Braze Swift SDKのバージョン8.2.0以降では、ライブアクティビティをリモートで登録できます。この機能を使用するには、iOS 17.2以降が必要です。

ステップ1:アクティビティの作成

まず、iOSアプリケーションでライブアクティビティを設定するために、AppleのドキュメントのDisplaying live data with Live Activitiesに従っていることを確認してください。このタスクの一環として、Info.plistNSSupportsLiveActivitiesYESに設定することを忘れないでください。

ライブアクティビティの正確な性質はビジネスケースに固有であるため、Activityオブジェクトをセットアップして初期化してください。重要な定義は以下のとおりです。

  • ActivityAttributes: このプロトコルは、ライブアクティビティに表示される静的(変更不可)なコンテンツとダイナミックな(変更可能な)コンテンツを定義します。
  • ActivityAttributes.ContentState: この型は、アクティビティの進行中に更新されるダイナミックなデータを定義します。

また、SwiftUIを使用してロック画面およびサポートされているデバイスのDynamic IslandのUIプレゼンテーションを作成します。

これらの制約はBrazeとは独立しているため、ライブアクティビティに関するAppleの前提条件と制限事項を確認しておいてください。

Superb Owlショーのユーザーへの最新情報を提供するライブアクティビティを作成したいとしましょう。このショーでは、2つの競合する野生動物保護団体が、保護しているフクロウの数に応じてポイントを獲得します。この例では、SportsActivityAttributesという構造体を作成しましたが、ActivityAttributesの独自の実装を使用することもできます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#if canImport(ActivityKit)
  import ActivityKit
#endif

@available(iOS 16.1, *)
struct SportsActivityAttributes: ActivityAttributes {
  public struct ContentState: Codable, Hashable {
    var teamOneScore: Int
    var teamTwoScore: Int
  }

  var gameName: String
  var gameNumber: String
}

ステップ2:アクティビティの開始

まず、アクティビティの登録方法を選択します。

  • リモート: ユーザーライフサイクルの早い段階で、プッシュ・トゥ・スタートトークンが必要になる前にregisterPushToStartメソッドを使用し、/messages/live_activity/startエンドポイントを使用してアクティビティを開始します。
  • ローカル: ライブアクティビティのインスタンスを作成し、launchActivityメソッドを使用してBrazeが管理するプッシュトークンを作成します。

ステップ2.1: ウィジェットエクステンションにBrazeKitを追加する

Xcodeプロジェクトでアプリ名を選択し、Generalを選択します。Frameworks and LibrariesBrazeKitがリストされていることを確認します。

サンプルXcodeプロジェクトのFrameworks and Libraries配下のBrazeKitフレームワーク。

ステップ2.2: BrazeLiveActivityAttributesプロトコルの追加

ActivityAttributesの実装に、BrazeLiveActivityAttributesプロトコルへの準拠を追加し、属性モデルにbrazeActivityIdプロパティを追加します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import BrazeKit

#if canImport(ActivityKit)
  import ActivityKit
#endif

@available(iOS 16.1, *)
// 1. Add the `BrazeLiveActivityAttributes` conformance to your `ActivityAttributes` struct.
struct SportsActivityAttributes: ActivityAttributes, BrazeLiveActivityAttributes {
  public struct ContentState: Codable, Hashable {
    var teamOneScore: Int
    var teamTwoScore: Int
  }

  var gameName: String
  var gameNumber: String

  // 2. Add the `String?` property to represent the activity ID.
  var brazeActivityId: String?
}

ステップ2.3: プッシュ・トゥ・スタートの登録

次に、ライブアクティビティの型を登録し、Brazeがこの型に関連付けられたすべてのプッシュ・トゥ・スタートトークンとライブアクティビティインスタンスを追跡できるようにします。

以下の例では、LiveActivityManagerクラスがライブアクティビティオブジェクトを処理します。次に、registerPushToStartメソッドがSportsActivityAttributesを登録します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import BrazeKit

#if canImport(ActivityKit)
  import ActivityKit
#endif

class LiveActivityManager {

  @available(iOS 17.2, *)
  func registerActivityType() {
    // This method returns a Swift background task.
    // You may keep a reference to this task if you need to cancel it wherever appropriate, or ignore the return value if you wish.
    let pushToStartObserver: Task = Self.braze?.liveActivities.registerPushToStart(
      forType: Activity<SportsActivityAttributes>.self,
      name: SportsActivityAttributes.name
    )
  }

}

ステップ2.4: プッシュ・トゥ・スタート通知の送信

/messages/live_activity/startエンドポイントを使用してリモートプッシュ・トゥ・スタート通知を送信します。

AppleのActivityKitフレームワークを使用してプッシュトークンを取得できます。Braze SDKがこれを管理します。これにより、BrazeがバックエンドでApple Push Notification service(APNs)にプッシュトークンを送信するため、Braze APIを通じてライブアクティビティを更新できます。

  1. AppleのActivityKit APIを使用してライブアクティビティ実装のインスタンスを作成します。
  2. pushTypeパラメーターを.tokenに設定します。
  3. 定義したライブアクティビティのActivitiesAttributesContentStateを渡します。
  4. launchActivity(pushTokenTag:activity:)にBrazeインスタンスを渡してアクティビティを登録します。pushTokenTagパラメーターは、定義するカスタム文字列です。作成する各ライブアクティビティに対して一意である必要があります。

ライブアクティビティを登録すると、Braze SDKはプッシュトークンの変更を抽出して監視します。

この例では、ライブアクティビティオブジェクトのインターフェイスとしてLiveActivityManagerというクラスを作成します。次に、pushTokenTag"sports-game-2024-03-15"に設定します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import BrazeKit

#if canImport(ActivityKit)
  import ActivityKit
#endif

class LiveActivityManager {

  @available(iOS 16.2, *)
  func createActivity() {
    let activityAttributes = SportsActivityAttributes(gameName: "Superb Owl", gameNumber: "Game 1")
    let contentState = SportsActivityAttributes.ContentState(teamOneScore: "0", teamTwoScore: "0")
    let activityContent = ActivityContent(state: contentState, staleDate: nil)
    if let activity = try? Activity.request(attributes: activityAttributes,
                                            content: activityContent,
      // Setting your pushType as .token allows the Activity to generate push tokens for the server to watch.
                                            pushType: .token) {
      // Register your Live Activity with Braze using the pushTokenTag.
      // This method returns a Swift background task.
      // You may keep a reference to this task if you need to cancel it wherever appropriate, or ignore the return value if you wish.
      let liveActivityObserver: Task = AppDelegate.braze?.liveActivities.launchActivity(pushTokenTag: "sports-game-2024-03-15",
                                                                                        activity: activity)
    }
  }

}

ライブアクティビティウィジェットは、この初期コンテンツをユーザーに表示します。

iPhoneのロック画面に表示されているライブアクティビティ。2つのチームのスコアが表示されています。Wild Bird FundとOwl Rehabの両チームともスコアは0です。

ステップ3:アクティビティトラッキングの再開

Brazeがアプリ起動時にライブアクティビティを追跡するようにするには、以下を実行します。

  1. AppDelegateファイルを開きます。
  2. ActivityKitモジュールが利用可能な場合はインポートします。
  3. アプリケーションに登録したすべてのActivityAttributes型に対して、application(_:didFinishLaunchingWithOptions:)resumeActivities(ofType:)を呼び出します。

これにより、Brazeはすべてのアクティブなライブアクティビティのプッシュトークン更新を追跡するタスクを再開できます。ユーザーがデバイスでライブアクティビティを明示的に閉じた場合、それは削除されたとみなされ、Brazeはそれ以上追跡しません。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import UIKit
import BrazeKit

#if canImport(ActivityKit)
  import ActivityKit
#endif

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

  static var braze: Braze? = nil

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {

    if #available(iOS 16.1, *) {
      Self.braze?.liveActivities.resumeActivities(
        ofType: Activity<SportsActivityAttributes>.self
      )
    }

    return true
  }
}

ステップ4:アクティビティの更新

iPhoneのロック画面に表示されているライブアクティビティ。2つのチームのスコアが表示されています。Wild Bird Fundは2ポイント、Owl Rehabは4ポイントです。

/messages/live_activity/updateエンドポイントを使用すると、Braze REST APIを通じて渡されるプッシュ通知でライブアクティビティを更新できます。このエンドポイントを使用してライブアクティビティのContentStateを更新します。

ContentStateを更新すると、ライブアクティビティウィジェットに新しい情報が表示されます。以下は、前半終了時のSuperb Owlショーの様子です。

詳細については、/messages/live_activity/updateエンドポイントの記事を参照してください。

ステップ5:アクティビティの終了

ライブアクティビティがアクティブな場合、ユーザーのロック画面とDynamic Islandの両方に表示されます。Brazeを通じて終了するには、/messages/live_activity/updateエンドポイントでend_activitytrueに設定します。

ライブアクティビティを終了する際の信頼性を向上させるために、以下のオプションのステップを実行してください。

  1. オプションで、同じupdateリクエストにdismissal_dateを含め、iOSがライブアクティビティUIを削除するタイミングを提案します。
  2. メッセージアクティビティログで配信結果を確認します。

自動解除の設定

自動解除を設定するには、ライブアクティビティを開始した後にupdateエンドポイントへのフォローアップリクエストをスケジュールします。

  1. 追跡可能なactivity_idを含む/messages/live_activity/startリクエストを送信します。
  2. そのactivity_idとターゲット終了時間をバックエンドスケジューラーに保存します。
  3. ターゲット終了時間に、end_activitytrueに設定した/messages/live_activity/updateリクエストを送信します。
  4. 同じupdateリクエストで解除日を設定します。詳細については、/messages/live_activity/updateエンドポイントを参照してください。

解除のタイミングはiOSによって制御されます。有効な終了リクエストを送信した後でも、ロック画面やDynamic Islandからの削除はOS レベルの条件に基づいて遅延したり、異なる動作をしたりすることがあります。

ライブアクティビティはBraze外で終了することもあります。

  • ユーザーによる解除: ユーザーはライブアクティビティを手動で閉じることができます。
  • タイムアウト: デフォルトの8時間経過後、iOSはユーザーのDynamic Islandからライブアクティビティを削除します。デフォルトの12時間経過後、iOSはユーザーのロック画面からライブアクティビティを削除します。

詳細については、/messages/live_activity/updateエンドポイントの記事を参照してください。

ライブアクティビティのトラッキング

ライブアクティビティのイベントは、Currents、Snowflakeデータ共有、およびクエリビルダーで利用できます。以下のイベントは、ライブアクティビティのライフサイクルの把握と監視、トークンの可用性のトラッキング、および問題の独立した診断や配信ステータスの確認に役立ちます。

ライブアクティビティの送信を確認する

ワークスペースが iOS ライブアクティビティを送信しているかどうかを確認する必要がある場合、以下の方法を使用できます。

メッセージアクティビティログ

設定 > メッセージアクティビティログに移動し、ライブアクティビティのエラーでフィルタリングして、想定される期間中のライブアクティビティ関連の配信結果を確認します。詳細については、メッセージアクティビティログを参照してください。

クエリビルダー、Currents、またはSnowflakeデータ共有

以下のライブアクティビティイベントを確認して、ライブアクティビティのライフサイクルと配信を検証します。

  • Live Activity Send: Brazeがライブアクティビティを開始、更新、または終了するたびに記録されます
  • Live Activity Outcome: 送信された各ライブアクティビティのAPNsへの最終的な配信ステータスです

オプションとして、トークンの利用可否シグナルも確認できます。

  • Live Activity Push To Start Token Change
  • Live Activity Update Token Change

API使用状況ダッシュボード

設定 > APIと識別子 > ダッシュボードに移動し、フィルターを選択してエンドポイントでフィルタリングし、APIレスポンスを確認します。たとえば、/messages/live_activity/update(または /messages/live_activity/start)を選択して、過去30日間のリクエストボリュームを表示します。APIレスポンスは、APIが呼び出されていること、およびこのワークスペースでiOSライブアクティビティ通知が使用されていることを示します。詳細については、API使用状況ダッシュボードを参照してください。

ライブアクティビティイベントの監視(オプション)

Braze SDKは、braze.liveActivitiesに2つのサブスクリプションメソッドを提供し、ライブアクティビティの完全なライフサイクルを監視できます。詳細なステップバイステップのウォークスルーについては、ライブアクティビティチュートリアルを参照してください。

  • subscribeToStateUpdates(_:): push-to-startトークン登録と実行中のアクティビティインスタンスの両方のライフサイクルイベントを配信します。
  • subscribeToErrors(_:): ライブアクティビティのトラッキング中に発生したSDKおよびサーバー側のエラーを配信します。

サブスクリプションの設定

application(_:didFinishLaunchingWithOptions:)でサブスクリプションを一度設定し、アプリのライフタイム全体にわたって保持します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class AppDelegate: UIResponder, UIApplicationDelegate {
  static var braze: Braze?

  var stateSubscription: Braze.Cancellable?
  var errorSubscription: Braze.Cancellable?

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let braze = Braze(configuration: config)
    Self.braze = braze

    if #available(iOS 16.1, *) {
      stateSubscription = Self.braze?.liveActivities.subscribeToStateUpdates { event in
        self.handleStateUpdate(event)
      }
      errorSubscription = Self.braze?.liveActivities.subscribeToErrors { error in
        self.handleLiveActivityError(error)
      }
    }

    return true
  }
}

subscribeToStateUpdates

subscribeToStateUpdates(_:)は、ライブアクティビティの完全なライフサイクルをカバーするUpdateEvent値を配信します。イベントは2つのスコープに分かれています。

  • .activityType(ActivityType): push-to-startトークン登録のためのタイプレベルイベント(iOS 17.2以降)。アクティビティインスタンスはまだ存在しません。
  • .activityInstance(ActivityInstance): 特定の実行中のアクティビティのインスタンスレベルイベント。

複数のサブスクライバーがサポートされており、各アクティブなサブスクリプションはすべてのイベントを独立して受信します。

タイプスコープのイベント

イベント 発火タイミング
.pushToStartTokenRead(activityType:) push-to-startトークンがOSから読み取られました。Brazeはこのタイプの新しいアクティビティをリモートで開始できるようになりました。
.pushToStartTokenFlushed(activityType:) トークンがBrazeサーバーに送信されました。Brazeはこのタイプのpush-to-start通知を送信できます。
.pushToStartOptedOut(activityType:) ユーザーがoptOutPushToStart(type:)を通じてこのアクティビティタイプのpush-to-startをオプトアウトしました。
.pushToStartOptOutFlushed(activityType:) オプトアウトがBrazeサーバーに送信されました。

インスタンススコープのイベント

イベント 発火タイミング
.started(activityId:activityType:pushTokenTag:launchSource:) SDKがlaunchActivity(pushTokenTag:activity:)を通じてこのアクティビティの追跡を開始しました。launchSourceの値は、アプリが開始したアクティビティの場合は.local、リモートで開始されたアクティビティの場合は.pushToStartです。
.resumed(activityId:activityType:pushTokenTag:) SDKがresumeActivities(ofType:)を通じてこのアクティビティの追跡を再開しました。
.pushTokenFlushed(activityId:activityType:pushTokenTag:) アクティビティのプッシュトークンがBrazeサーバーに受け入れられました。アクティビティはリモート更新を受信できるようになりました。
.active(activityId:activityType:) アクティビティは現在アクティブで、ユーザーに表示されています。
.stale(activityId:activityType:staleDate:) アクティビティのコンテンツが古くなりました。iOS 16.2以降でのみ発行されます。
.dismissed(activityId:activityType:) ユーザーがアクティビティを手動で削除しました。
.ended(activityId:activityType:) アクティビティが終了しました。
.contentUpdated(activityId:activityType:) アクティビティのコンテンツ状態が更新されました(iOS 16.2以降)。カスタムロジックを使用して、Activity.activitiesからIDでActivity<T>を検索し、activity.content.stateを通じて型付き状態にアクセスします。
.pushTokenUpdated(activityId:activityType:) ActivityKitがアクティビティのプッシュトークンをローテーションしました。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
func handleStateUpdate(_ event: Braze.LiveActivities.UpdateEvent) {
  switch event {

  // Type-scoped: push-to-start token lifecycle (iOS 17.2+)
  case .activityType(.pushToStartTokenRead(let activityType)):
    print("[\(activityType)] Push-to-start token read by SDK")

  // ...

  // Instance-scoped: SDK tracking
  case .activityInstance(.started(let id, let type, let tag, let source)):
    print("[\(type)] Activity \(id) started via \(source), tag: \(tag)")

  // ...

  // Instance-scoped: ActivityKit lifecycle
  case .activityInstance(.active(let id, let type)):
    print("[\(type)] Activity \(id) is active")

  // ...

  case .activityInstance(.ended(let id, let type)):
    print("[\(type)] Activity \(id) ended")

  // Instance-scoped: content updates (iOS 16.2+)
  case .activityInstance(.contentUpdated(let id, let type)):
    // For more advanced use cases of `contentUpdated`, see the section below
    print("[\(type)] Content updated for activity \(id)")

  case .activityInstance(.pushTokenUpdated(let id, let type)):
    print("[\(type)] Activity \(id) push token rotated")
  }
}

subscribeToErrors

subscribeToErrors(_:)は、UpdateEventと同じ2つのスコープを使用してErrorEvent値を配信します。

  • .activityType(ActivityType): push-to-start登録失敗のタイプレベルエラー。
  • .activityInstance(ActivityInstance): 実行中のアクティビティのインスタンスレベルエラー。

リトライが適切かどうかを判断するには、isTransientフラグを使用します。SDKは一時的な失敗を自動的にリトライします。

タイプスコープのエラー

エラー 発火タイミング
.pushToStartRegistrationFailed(activityType:isTransient:reason:) push-to-startトークンがBrazeサーバーに到達できませんでした。

インスタンススコープのエラー

エラー 発火タイミング
.registrationFailed(activityId:activityType:pushTokenTag:isTransient:reason:) アクティビティのプッシュトークンがBrazeへの登録に失敗しました。
.activityNotFound(activityId:activityType:) resumeActivities(ofType:)が、もう実行されていないアクティビティの保存済みマッピングを検出しました。アプリが強制終了されている間にアクティビティが終了した可能性があります。
.invalidPushTokenTag(activityId:activityType:tag:) launchActivity(pushTokenTag:activity:)が無効なタグで呼び出されました。タグは空でなく、256バイト未満である必要があります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func handleLiveActivityError(_ error: Braze.LiveActivities.ErrorEvent) {
  switch error {

  // Type-scoped errors
  case .activityType(.pushToStartRegistrationFailed(let type, let isTransient, let reason)):
    if isTransient {
      print("[\(type)] Push-to-start registration failed (transient, will retry): \(reason)")
    } else {
      print("[\(type)] Push-to-start registration failed (permanent): \(reason)")
    }

  // Instance-scoped errors
  case .activityInstance(.registrationFailed(let id, let type, _, let isTransient, let reason)):
    if isTransient {
      print("[\(type)] Activity \(id) registration failed (transient, retrying): \(reason)")
    } else {
      print("[\(type)] Activity \(id) registration failed (permanent): \(reason)")
    }

  case .activityInstance(.activityNotFound(let id, let type)):
    print("[\(type)] Stored activity \(id) not found on resume")

  case .activityInstance(.invalidPushTokenTag(let id, let type, let tag)):
    print("[\(type)] Activity \(id) has invalid push token tag '\(tag)'")
  }
}

コンテンツ状態の更新を処理する(オプション)

実際のライブアクティビティインスタンスのコンテンツ状態を使用する場合は、このセクションに従ってください。

.contentUpdatedイベントが発火したら、カスタムロジックを使用してActivity.activitiesからIDで実行中のActivity<T>を検索し、activity.content.stateを通じて型付きContentStateにアクセスします。

単一の属性タイプ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
case .activityInstance(.contentUpdated(let id, let type)):
  #if canImport(ActivityKit)
    // Add custom logic look up the Activity<T> by ID and access your app's typed ContentState.
    // In this example, `SportsActivityAttributes` is the app's custom type.
    if #available(iOS 16.2, *),
      let activity = findActivityInstance(id: id, as: SportsActivityAttributes.self)
    {
      // `activityContent` is now strongly typed as a `SportsActivityAttributes`
      let activityContent = activity.content.state
      print("[\(type)] Game \(id) — score: \(activityContent.teamOneScore)\(activityContent.teamTwoScore)")
      return
    }
  #endif
  print("[\(type)] Content updated for activity \(id)")

// ...

// - MARK: Helper methods

@available(iOS 16.2, *)
func findActivityInstance<Attributes: ActivityAttributes>(
  id: String,
  as type: Attributes.Type
) -> Activity<Attributes>? {
  // Use Apple's API to find the matching Live Activity instance:
  // - https://developer.apple.com/documentation/activitykit/activity/activities
  Activity<Attributes>.activities.first(where: { $0.id == id })
}

複数の属性タイプ

アプリが複数のActivityAttributesタイプを使用している場合は、type文字列を確認して適切なActivity<T>を検索します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
case .activityInstance(.contentUpdated(let id, let type)):
  #if canImport(ActivityKit)
    if #available(iOS 16.2, *) {
      if type == SportsActivityAttributes.name,
        let activity = findActivityInstance(id: id, as: SportsActivityAttributes.self)
      {
        let activityContent = activity.content.state
        print("[\(type)] Game \(id) — score: \(activityContent.teamOneScore)\(activityContent.teamTwoScore)")
        return

      } else if type == OrderActivityAttributes.name,
        let activity = findActivityInstance(id: id, as: OrderActivityAttributes.self)
      {
        let activityContent = activity.content.state
        print("[\(type)] Order \(id) — status: \(activityContent.status), ETA: \(activityContent.eta)")
        return
      }
    }
  #endif
  print("[\(type)] Content updated for activity \(id)")

// ...

// - MARK: Helper methods

@available(iOS 16.2, *)
func findActivityInstance<Attributes: ActivityAttributes>(
  id: String,
  as type: Attributes.Type
) -> Activity<Attributes>? {
  // Use Apple's API to find the matching Live Activity instance:
  // - https://developer.apple.com/documentation/activitykit/activity/activities
  Activity<Attributes>.activities.first(where: { $0.id == id })
}

よくある質問(FAQ)

機能とサポート

ライブアクティビティをサポートしているプラットフォームは?

現在、ライブアクティビティはiOSとiPadOSに固有の機能です。デフォルトでは、iPhoneやiPadで起動したアクティビティは、ペアリングされたwatchOS 11以降またはmacOS 26以降のデバイスにも表示されます。

Brazeは現在、Androidでのネイティブなライブアクティビティサポートを提供していません。Androidでは、Brazeプッシュ通知とカスタム通知レンダリングを使用してライブ更新エクスペリエンスを構築できます。

macOSのメニューバーにライブアクティビティがアラートとして表示されているスクリーンショット

ライブアクティビティの記事では、Braze Swift SDKを使用してライブアクティビティを管理するための前提条件について説明しています。

React Nativeアプリはライブアクティビティをサポートしていますか?

はい、React Native SDK 3.0.0以降は、Braze Swift SDKを介してライブアクティビティをサポートしています。つまり、Braze Swift SDKの上に直接React Native iOSのコードを記述する必要があります。

Appleが提供するライブアクティビティ機能は、JavaScriptでは変換できない言語機能(Swift Concurrency、generics、SwiftUIなど)を使用しているため、ライブアクティビティ用のReact Native固有のJavaScriptコンビニエンスAPIは存在しません。

Brazeはキャンペーンやキャンバスステップとしてのライブアクティビティをサポートしていますか?

いいえ、現在サポートされていません。

プッシュ通知とライブアクティビティ

ライブアクティビティがアクティブな状態でプッシュ通知が送信された場合はどうなりますか?

ブルズ対ベアーズのスポーツ中継のライブアクティビティが画面中央に、プッシュ通知のlorem ipsumテキストが画面下部に表示された携帯電話の画面

ライブアクティビティとプッシュ通知は異なる画面領域を占有するため、ユーザーの画面上で競合することはありません。

ライブアクティビティがプッシュメッセージ機能を活用する場合、ライブアクティビティを受信するためにプッシュ通知を有効にする必要がありますか?

ライブアクティビティは更新にプッシュ通知を利用しますが、異なるユーザー設定によって制御されています。ユーザーはライブアクティビティにオプトインしつつプッシュ通知はオプトアウトでき、その逆も可能です。

ライブアクティビティ更新トークンは8時間後に期限切れになります。

ライブアクティビティにはプッシュプライマーが必要ですか?

プッシュプライマーは、ユーザーにアプリからのプッシュ通知をオプトインするよう促すベストプラクティスです。しかし、ライブアクティビティにオプトインするためのシステムプロンプトはありません。デフォルトでは、ユーザーがiOS 16.1以降でアプリをインストールすると、そのアプリのライブアクティビティにオプトインされます。この権限は、アプリごとにデバイス設定で無効化または再有効化できます。

技術的なトピックとトラブルシューティング

ライブアクティビティにエラーがあるかどうかを確認するには?

ライブアクティビティのエラーは、Brazeダッシュボードのメッセージアクティビティログに記録されます。ここで「LiveActivity Errors」でフィルタリングできます。

push-to-start通知を送信した後、ライブアクティビティを受信できないのはなぜですか?

まず、messages/live_activity/startエンドポイントで説明されているすべての必須フィールドがペイロードに含まれていることを確認します。activity_attributesおよびcontent_stateフィールドは、プロジェクトのコードで定義されているプロパティと一致する必要があります。ペイロードが正しいことが確かな場合は、APNsによってレート制限されている可能性があります。この制限はBrazeではなくAppleによって課されています。

push-to-start通知がデバイスに正常に届いたがレート制限のために表示されなかったことを確認するには、Macのコンソールアプリを使用してプロジェクトをデバッグします。目的のデバイスの記録プロセスをアタッチし、検索バーでprocess:liveactivitiesdを使用してログをフィルタリングします。

push-to-startでライブアクティビティを開始した後、新しい更新を受信しないのはなぜですか?

ステップ2.2: BrazeLiveActivityAttributesプロトコルの追加で説明されている手順が正しく実装されていることを確認してください。ActivityAttributesには、BrazeLiveActivityAttributesプロトコルへの準拠とbrazeActivityIdプロパティの両方が含まれている必要があります。

ライブアクティビティのpush-to-start通知を受信したら、Braze URLの/push_token_tagエンドポイントへの送信ネットワークリクエストが表示され、"tag"フィールドの下に正しいアクティビティIDが含まれていることを再確認してください。

最後に、更新ペイロード内のライブアクティビティ属性タイプが、SDKメソッド呼び出しのregisterPushToStartで使用した文字列とクラスに完全に一致していることを確認してください。定数を使用してタイプミスを防いでください。

live_activity/updateエンドポイントを使用しようとすると、アクセス拒否の応答が返されます。なぜですか?

使用するAPIキーには、さまざまなBraze APIエンドポイントにアクセスするための適切な権限を付与する必要があります。以前に作成したAPIキーを使用している場合、権限の更新を忘れている可能性があります。APIキーセキュリティの概要を確認してください。

messages/sendエンドポイントはmessages/live_activity/updateエンドポイントとレート制限を共有していますか?

デフォルトでは、messages/live_activity/updateエンドポイントのレート制限は、ワークスペースごとに、複数のエンドポイントにわたって、1時間あたり250,000リクエストです。詳細については、APIレート制限を参照してください。

New Stuff!