はじめてのライブアクティビティ

この記事は[ZOZO Advent Calendar 2022](https://qiita.com/advent-calendar/2022/zozo)Vol.1の20日目の記事です。


# はじめに
本記事はiOS 16で追加されたライブアクティビティについての概要と実装についてのまとめです。


# ライブアクティビティとは？
ライブアクティビティはiOS 16で追加されたアプリの最新データをDynamicIslandとロック画面に表示する機能です。
DynamicIslandはiPhone14 Pro、iPhone14 Pro Maxでのみ使用できます。(2022年12月時点)

|iPhone14 Pro、iPhone14 Pro Max|それ以外|
|:----|:----|
| ロック画面、<br>DynamicIsland|ロック画面|

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308861/c3ee24f5-bb0d-deac-c238-58cfa9319ab4.png" width=600>

参照https://developer.apple.com/documentation/activitykit

# ライブアクティビティを表示するには？

ライブアクティビティは[ActivityKit](https://developer.apple.com/documentation/activitykit)と[WidgetKit](https://developer.apple.com/documentation/WidgetKit)、[SwiftUI](https://developer.apple.com/documentation/SwiftUI)を使用して表示します。
まずはライブアクティビティの構成について紹介します。

## ライブアクティビティの構成

ライブアクティビティを表示するのに必要になるのは3つです。
- ライブアクティビティに表示するデータ
- ロック画面に表示するView
- DynamicIslandの表示要素

ライブアクティビティに表示するためのデータは静的なデータと動的なデータの2種類あります。
動的なデータを更新することで、ライブアクティビティの状態を更新することができます。
ロック画面、DynamicIslandのデータの表示にはActivityViewContextを使用します。

![スクリーンショット 2022-12-19 3.02.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308861/74f4558a-66f1-1343-1b73-d3cba00e6443.png)


## ライブアクティビティとウィジェットの違い
ライブアクティビティはActivityKitだけではなくWidgetKitも使用しているため、構成はウイジェットと似ていますが、データを更新する方法が違います。
以下ウイジェットの構成です。ウイジェットはProviderというのを使用してデータの更新を行います。

![スクリーンショット 2022-12-18 14.20.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308861/54db58c0-1b51-9412-8da8-d2b7fdbef9c6.png)

参照 https://developer.apple.com/documentation/WidgetKit

# ライブアクティビティの実装
本記事では、ライブアクティビティを表示するところをピックアップして書きますので、詳細なライブアクティビティの作り方やライブアクティビティの更新、終了については触れません。


今回は以下のような静的なデータ1つと動的なデータ1つを表示するライブアクティビティを作成します。

![スクリーンショット 2022-12-20 5.36.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308861/5159b9e9-9c66-5b0e-65b7-e2abf44ebb0d.png)


実装の流れは次の通りです。
1. ライブアクティビティに表示するデータの作成
2. ライブアクティビティ用のWidgetを作成
3. ロック画面に表示するViewの作成
4. DynamicIslandの表示要素の作成
5. ライブアクティビティの起動


## 1. ライブアクティビティに表示するデータの作成

ライブアクティビティに表示するデータはActivityAttributesに準拠します。

```swift
struct SampleAttributes: ActivityAttributes {
    // 静的なーデータ
    var name: String

    // 動的なデータ
    public struct ContentState: Codable, Hashable {
        var time: Date
    }
}
```

## 2. ライブアクティビティ用のWidgetを作成

ライブアクティビティのウイジェットのConfigurationにはActivityConfigurationを使用します。

```swift
@main
struct SampleActivityWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: SampleAttributes.self) { context in
            // ロック画面のView
        } dynamicIsland: { context in
            // DynamicIslandの要素
        }
    }
}
```

## 3. ロック画面に表示するViewの作成
次にロック画面用のViewを作成し、ライブアクティビティのConfigurationに設定します。

ロック画面に表示するViewは160ptの高さ以内にするのをおすすめします。高さ160ptを超える場合、ロック画面でライブアクティビティが切り詰められることがあるようです。


``` swift
struct LockScreenLiveActivityView: View {
    let context: ActivityViewContext<SampleAttributes>
    
    var body: some View {
        VStack {
            Text("Hello \(context.attributes.name)")
            Text(context.state.time, style: .time)
        }
        .activityBackgroundTint(Color.cyan)
        .activitySystemActionForegroundColor(Color.black)
    }
}
```

```swift
@main
struct SampleActivityWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: SampleAttributes.self) { context in
            LockScreenLiveActivityView(context: context)
        } dynamicIsland: { context in
            // DynamicIslandの要素
        }
    }
}
```



## 4. DynamicIslandの表示要素の作成
DynamicIslandでのライブアクティビティの表示は、ロック画面と異なり複数の表示形式があります。

|表示方法|説明|画像|
|:---|:---|:---|
|compact| ライブアクティビティを１つだけ起動している場合に表示されます。 | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308861/464f83b6-f82e-0822-4a8f-73c8a00abe51.png" width=300> |
|minimal| 複数のライブアクティビティを起動している場合、minimalの表示になります。minimal表示は2パターンあり、システムがどのように表示するか決めます。<br>※minimalに表示する内容思いつきませんでした🙏 | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308861/eab45236-aa50-d357-799f-4bc9d36b5bac.png" width=300> <br> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308861/fa69edd4-f5e6-1792-aed7-b325451b749f.png" width=300>|
|expand| compactまたはminimalのをタップするとexpandが表示されます。|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308861/c7638142-1e08-be7d-5beb-cfad1b495c60.png" width=300>|

複数の表示方法がありますが、システムが状況にあった表示を行ってくれるので、DynamicIslandに表示するレイアウトを実装すれば良いです。
以下がDynamicIslandの実装です。

```swift
struct SampleLiveActivity: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: SampleAttributes.self) { context in
            LockScreenLiveActivityView(context: context)
        } dynamicIsland: { context in
            DynamicIsland {
                DynamicIslandExpandedRegion(.center) {
                    Text("Hello \(context.attributes.name)")
                }
                DynamicIslandExpandedRegion(.bottom) {
                    Text(context.state.time, style: .time)
                }
            } compactLeading: {
                // compactLeadingとcompactTrailingは分けて実装するが、まとまってcomactとしてDynamicIslandに表示される
                Text("Hello \(context.attributes.name)")
            } compactTrailing: {
                Text(context.state.time, style: .time)
            } minimal: {
                 Text("Min")
            }
        }
    }
}
```
これで、ライブアクティビティを表示する準備が出来ました。あとはライブアクティビティを起動するだけです。

## 5. ライブアクティビティの起動
ライブアクティビティの起動は `request(attributes:content:pushType:)`を呼びます。
ライブアクティビティはiPhoneのみでで使用できるかつ、ライブアクティビティを無効に設定することができるため、使用できるかのチェックを`areActivitiesEnabled`で行います。
また、ライブアクティビティは複数起動できますが、起動できる制限に達成した場合ライブアクティビティの起動に失敗する場合があるのでエラー処理をおこないます。


```swift
if ActivityAuthorizationInfo().areActivitiesEnabled {
    do {
        activity = try Activity<SampleAttributes>.request(attributes: .init(name: "edm"), content: .init(state: .init(time: .now), staleDate: Calendar.current.date(byAdding: .minute, value: 30, to: Date())!))
    } catch (let error) {
        // エラー処理
    }
}
```
以上でライブアクティビティを表示することができます。



# おわりに
ライブアクティビティの構成や表示するまでの流れについてでした。
次は今回触れなかったライブアクティビティの更新、終了についてまとめたいと思います。