【SwiftUI】LiveActivityを実装する part 3(iOS16.1)

SwiftUI

LiveActivityを実装する part 3です。
part3はLiveActivityのアプリ側で実装する処理についてです。

LiveActivityのアプリ側のコード

LiveActivityはWidget Extensionに実装しますが、
Widgetとは異なりアプリ側でリクエストして表示する必要があります。

アプリ側からリクエストや更新などの処理を行う方法について紹介します。

LiveActivityのViewについて

まず今回のLiveActivityはテキストと数値を表示するだけの簡単なものです。

Viewのコードはデフォルトで生成されるコードにテキストと数値の表示箇所を追加しただけです。
以下のコードになります。

struct LiveActivityForBlogWidgetLiveActivity: Widget {
    
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: LiveActivityForBlogWidgetAttributes.self) { context in
            VStack {                
                Text(context.attributes.name)
                Text("\(context.state.value)")
            }
            .activityBackgroundTint(Color.cyan)
            .activitySystemActionForegroundColor(Color.black)
            
        } dynamicIsland: { context in
            DynamicIsland {
                DynamicIslandExpandedRegion(.leading) {
                    Text(context.attributes.name)
                }
                DynamicIslandExpandedRegion(.trailing) {
                    Text("\(context.state.value)")
                }
                DynamicIslandExpandedRegion(.center) {
                    Text("Center")
                }
                DynamicIslandExpandedRegion(.bottom) {
                    Text("Bottom")
                }
            } compactLeading: {
                Text(context.attributes.name)
            } compactTrailing: {
                Text("\(context.state.value)")
            } minimal: {
                Text("Min")
            }
            .widgetURL(URL(string: "liveActivityForBlog://deeplink?param=DynamicIsland"))
            .keylineTint(Color.red)
        }
    }
}

LiveActivityをリクエストする

part1でも扱いましたが、LiveActivityをリクエストする方法です。

ActivityAttributesとActivityAttributes.ContentStateにデータをセットしてLiveActivityをリクエストします。
ActivityAttributesはデフォルトではWidget側に生成されるので、
アプリ側で使用出来る様にTarget Membershipを確認して下さい。

以下はデフォルトで生成されるActivityAttributesのコードです。

struct LiveActivityForBlogWidgetAttributes: ActivityAttributes {
    public struct ContentState: Codable, Hashable {
        var value: Int
    }

    var name: String
}

ActivityAttributes自体のメンバにはLiveActivityの表示中に変更しない値を持たせます。
内部のContentStateは後から変更出来るので、表示を更新したい値を持たせます。

Activity.requestに先のデータを渡してリクエストすると表示されます。
LiveActivityをリクエストする際はエラーハンドリングが必要なのでdo-catchを使用しましょう。

なお今回は分かりやすさの為にLiveActivityを変数で1つだけ持っています。
実際は必ずしも変数で持つ必要はありませんし、
変数で持つなら複数表示できなくするか、複数持つ形にした方が良いでしょう。

import SwiftUI
import ActivityKit

struct ContentView: View {

    @State var liveActivity:Activity<LiveActivityForBlogWidgetAttributes>?
    
    var body: some View {
        VStack {
            Button("Request LiveActivity"){
                let attributes = LiveActivityForBlogWidgetAttributes(name: "Name")
                let contentState = LiveActivityForBlogWidgetAttributes.ContentState(value: 1)
                
                do {
                    liveActivity = try Activity.request(attributes: attributes, contentState: contentState)
                } catch (let error) {
                    print("Error requesting Live Activity \(error.localizedDescription).")
                }
            }
        }
        .padding()
    }
}

この時、以下のエラーが出てLiveActivityが表示されない場合があります。

「com.apple.ActivityKit.ActivityInput error 1.」

原因は3つの可能性があります。

  • Info.plistの設定を忘れた
  • LiveActivityの表示を許可しなかった
  • LiveActivityの上限数を超えた

LiveActivityの使用にはInfo.plistへの設定が必要です。

また初回表示時にロックスクリーンのLiveActivityの表示の可否についてのボタンが表示されます。
ここで拒否すると設定アプリからの変更が必要です。
要は通知の可否と同様ですがタイミングやUIが異なるので注意して下さい。

LiveActivityには上限数があるようで、1つのアプリから5つ出すとエラーが出ました。

Info.plistと許可については詳しくはpart1をご確認下さい。

LiveActivityを更新する

LiveActivityはアプリ側から更新を行う事ができます。
更新できるのはContentStateの部分です。

struct LiveActivityForBlogWidgetAttributes: ActivityAttributes {

    //ここが更新できる
    public struct ContentState: Codable, Hashable {
        var value: Int
    }

    //ここは更新できない
    var name: String
}

更新時にはContentStateと共にAlertConfigurationを含める事ができます。

AlertConfigurationにはTitleとBody、Soundの3つが設定できます。
なおSoundはiOSでも鳴りますが、TitleとBodyはApple Watch向けの様です。

また、updateメソッドは非同期で実行する為、
Viewから使用するにはTaskで囲ってawaitをつける必要があります。

struct ContentView: View {
    
    @State var liveActivity:Activity<LiveActivityForBlogWidgetAttributes>?
    
    var body: some View {
        VStack {
            Button("Request LiveActivity"){
                //略
            }
            
            Button("Update LiveActivity"){
                let contentState = LiveActivityForBlogWidgetAttributes.ContentState(value: 2)
                let alert = AlertConfiguration(title: "Title(Watch)", body: "Body(Watch)", sound: .default)
                
                Task{
                    await liveActivity?.update(using: contentState ,alertConfiguration: alert)
                }
            }
        }
        .padding()
    }
}

LiveActivityを終了する

LiveActivityを終了する方法です。

LiveActivityは必ずしも終了後すぐに消えるものではなく、
終了時にContentStateを更新して残しておく事もできます。

終了時に使うendメソッドにはContentStateとdismissalPolicyを設定します。
dismissalPolicyは終了後に残しておくかどうかの設定になり、以下の3つがあります。

  • .default
  • .immediate
  • .after(date: Date)

defaultは4時間となっています。immediateは文字通り即時非表示になります。
afterは日時指定が可能ですが、長くなる様に設定しても4時間以上は指定しても残りません。

また、このendメソッドも非同期で実行する為、Taskとawaitが必要です。

struct ContentView: View {
    
    @State var liveActivity:Activity<LiveActivityForBlogWidgetAttributes>?
    
    var body: some View {
        VStack {
            Button("Request LiveActivity"){
                //略
            }
            
            Button("Update LiveActivity"){
                //略
            }
            
            Button("End LiveActivity"){
                let contentState = LiveActivityForBlogWidgetAttributes.ContentState(value: 3)
                
                Task{
                    await liveActivity?.end(using: contentState, dismissalPolicy:.default)
                }
            }
        }
        .padding()
    }
}

リクエスト済みのLiveActivityを取得する

LiveActivityは簡単に取得する事ができます。
取得したLiveActivityはattributesやcontentStateなどで識別する事もできますし、
String型でIDを持っているのでそちらで識別する事も可能です。

この為、変数でLiveActivity自体は保持し続ける必要はあまりありません。

//配列(get only)
Activity<LiveActivityForBlogWidgetAttributes>.activities

//数
Activity<LiveActivityForBlogWidgetAttributes>.activities.count

//ForEach
ForEach(Activity<LiveActivityForBlogWidgetAttributes>.activities){ activity in
    //色々取得できる
    activity.attributes
    activity.contentState
    activity.id
    activity.activityState
}

最後に

LiveActivityを実装する part 3でした。

これでLiveActivityの使い方はざっくり把握できたかと思います。
便利な機能なのでLiveActivityが実装されたアプリが増えるといいなと思います。

part1,2は以下のリンクから読む事ができます。

コメント

  1. […] 【SwiftUI】LiveActivityを実装する part 3(iOS16.1)LiveActivityを実装する part 3です…SwiftUIActivityKitiOS16LiveActivitySwiftUIWidgetシェアする Twitter Facebook はてブ Pocket […]

  2. […] 1は以下のリンクから見ることができます。…thwork.net2022.10.23 【SwiftUI】LiveActivityを実装する part 3(iOS16.1)LiveActivityを実装する part 3です…未分類ActivityKitiOS16LiveActivitySwiftUIWidgetシェアする Twitter Facebook はてブ Pocket […]

タイトルとURLをコピーしました