見出し画像

ActivityKitを使用したライブアクティビティの実装方法

ActivityKitを使用してライブアクティビティを実装する方法についてまとめる。


ライブアクティビティとは

ライブアクティビティは、Appの最新データをiPhoneのロック画面とDynamic Islandに表示して、大切なタスクやイベントの状況を常に把握できるようにする機能です

ライブアクティビティとDynamic Islandの紹介

ライブアクティビティは、iOS 16.1から使用できるようになった機能。ロック画面やダイナミックアイランド上に、リアルタイムでアプリ情報を表示することができる。ダイナミックアイランドは、iPhone 14 Pro、Pro Max以降のProシリーズに搭載されている、ディスプレイ上部に搭載されているフロントカメラの、パンチホール部分に設けられた表示スペースのことを指す。

実装方法

Info.plistファイルの編集

ライブアクティビティを表示するためには、

  • Supports Live Activities (NSSupportsLiveActivities)

キーと対応するValueをYESにしてInfo.plistファイルに追加する。

上記対応を行わないと、ライブアクティビティは表示されないので注意すること。

Widget Extensionの追加

ライブアクティビティを表示するためには、Widget Extensionを追加する必要がある。

「Include Live Activity」にチェックを入れて、初期設定が完了すると、Widget Extensionが追加される。

ライブアクティビティに表示する情報の定義

ライブアクティビティに表示する情報は、ActivityAttributes プロトコルに準拠した構造体で定義する。
ライブアクティビティに表示する情報の種類は、以下の2種類に分類される。

  • 静的な情報

  • 動的な情報 

静的な情報は、ActivityAttributes プロトコルに準拠する構造体のプロパティで定義する。動的な情報は、構造体内で定義したCodable , Hashable プロトコルに準拠するContentState 構造体のプロパティで定義する。

struct CounterAttributes: ActivityAttributes {
    struct ContentState: Codable & Hashable {
        var age = 0
    }
    
    var name = ""
}

上記は年齢(age)と名前(name)を表示するライブアクティビティを想定した、ActivityAttributes プロトコルに準拠した構造体のサンプルコード。表示する年齢は動的に変更することが可能だが、名前を動的に変更することはできない。

ライブアクティビティViewの定義

ライブアクティビティViewは、Widget プロトコルに準拠した構造体のbody プロパティに定義する。Widget Extension追加時に生成されたSwiftファイル内に記述していく(上記の例だと「SampleLiveActivity.swift」)。

ActivityConfiguration

struct SampleLiveActivity: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: CounterAttributes.self) { context in
            VStack {
                Text("Lock Screen")
                HStack {
                    Text(context.attributes.name)
                    Text(context.state.age.description)
                }
            }
        } dynamicIsland: { context in
            DynamicIsland {
                DynamicIslandExpandedRegion(.leading) {
                    Text("EL")
                }
                DynamicIslandExpandedRegion(.trailing) {
                    Text("ET")
                }
                DynamicIslandExpandedRegion(.center) {
                    Text("EC")
                }
                DynamicIslandExpandedRegion(.bottom) {
                    VStack {
                        Text("EB")
                        HStack {
                            Text(context.attributes.name)
                            Text(context.state.age.description)
                        }
                    }
                }
            } compactLeading: {
                Text("L")
            } compactTrailing: {
                Text("T")
            } minimal: {
                Text("M")
            }
        }
    }
}

Widget プロトコルに準拠した構造体のbody プロパティに、ActivityConfiguration インスタンスを返すクロージャを記述する。
for 引数には、前項で作成したActivityAttributes に準拠した構造体名を指定する。 

第2引数には、ActivityViewContext<Attribute> インスタンスを引数とする、ロック画面に表示するライブアクティビティViewを返すクロージャを指定する。ActivityViewContext<Attribute> インスタンスには、ライブアクティビティに表示するための情報が格納されており、クロージャ内でライブアクティビティViewを生成・更新する時に使用する。
ここで指定したライブアクティビティViewは、ロック画面下部やダイナミックアイランドをサポートしていないデバイスで、 ライブアクティビティの更新についてユーザーに対して通知する時に表示される。

最後のdynamicIsland 引数には、ActivityViewContext<Attribute> を引数とするDynamicIsland 型のインスタンスを返すクロージャを指定する。DynamicIsland イニシャライザの各引数には、ダイナミックアイランドに表示するライブアクティビティViewを返すクロージャを指定する。
ここで指定したライブアクティビティViewは、ダイナミックアイランド各所で表示される。

ライブアクティビティの開始

let attributes = CounterAttributes(name: name)
let contentState = CounterAttributes.ContentState(age: age)
do {
    activity = try Activity<CounterAttributes>.request(
        attributes: attributes,
        content: ActivityContent<Activity<CounterAttributes>.ContentState>.init(
            state: contentState,
            staleDate: nil),
        pushType: nil)
} catch (let error) {
    print(error.localizedDescription)
}

request(attributes:content:pushType:) でライブアクティビティを開始する。
attributes にはActivityAttributes プロトコルに準拠したインスタンスを指定する。このインスタンスには、ライブアクティビティに表示する静的な情報が格納されている。
content には、ActivityContent インスタンスを指定する。このインスタンスには、ライブアクティビティに表示する動的な情報が格納されている。
pushTypeには、ライブアクティビティがプッシュ通知で動的な情報を受け取るかどうかを示す値を指定する。プッシュ通知で動的な情報を受け取る時は、.tokenを指定する。

ライブアクティビティの更新

Task {
    guard let activity else { return }
    let updatedState = CounterAttributes.ContentState(age: age)
    await activity.update(
        ActivityContent<Activity<CounterAttributes>.ContentState>.init(
          state: updatedState,
          staleDate: nil))
}

update(_:) または、update(_:alertConfiguration:)でライブアクティビティを更新する。第一引数には、ActivityContent インスタンスを指定する。このインスタンスには、ライブアクティビティに表示する動的な情報が格納されている。

Task {
    guard let activity else { return }
    let updatedState = CounterAttributes.ContentState(age: age)
    await activity.update(
        ActivityContent<Activity<CounterAttributes>.ContentState>.init(
          state: updatedState,
          staleDate: nil),
        alertConfiguration: AlertConfiguration(
            title: "Update Title",
            body: "Update Contents",
            sound: .default))
}

update(_:alertConfiguration:) でライブアクティビティの更新をユーザーに通知することができる。title 引数 とbody 引数 に指定した内容は、Apple Watch側にのみ表示されることに注意。
また、sound 引数にAlertConfiguration.AlertSound インスタンスを指定することで、iPhone及びiPad側で通知音が発生させることができ、.defaultを指定することで、システムサウンドを発生させることができる。

ライブアクティビティの終了

Task {
    guard let activity else { return }
    let updatetState =  CounterAttributes.ContentState(age: age)
    
    await activity.end(
        .init(
            state: updatetState,
            staleDate: nil),
        dismissalPolicy: .immediate)
}

end(_:dismissalPolicy:) でライブアクティビティを終了する。dismissalPolicy 引数には、ライブアクティビティを終了させるタイミングを指定するための、ActivityUIDismissalPolicy インスタンスを指定する。

ここから先は

4,470字 / 2画像

¥ 200

最後まで記事をお読みいただきありがとうございます! 記事が参考になればフォロー・♥いただけると凄く励みになります🥳