我报名参加金石计划1期应战——瓜分10万奖池,这是我的第1篇文章,点击检查活动详情

本文参阅、翻译并完成 Apple‘s documentation activitykit displaying live data with live activities 及 Updating and ending your Live Activity with remote push notifications 内容,文章触及的项目代码能够从这儿获取。

实时活动(Live Activity) - 在锁定屏幕和灵动岛上显示应用程序的实时数据

概述

实时活动(Live Activity) 在 iPhone 确认屏幕和灵动岛中显现 App 的实时数据,能协助用户盯梢 App 的内容。

要供给 Live Activity,开发者需求将代码增加到新的或现有的小组件中。Live Activity 运用了 WidgetKit 的功用,并运用 SwiftUI 编写界面。而 ActivityKit 的作用是处理 Live Activity 的生命周期:开发者运用它的 API 来进行恳求、更新和完毕实时活动。

实时活动功用和 ActivityKit 将包含在今年晚些时候推出的 iOS 16.1 中。

实时活动要求或约束

  • 时刻

时活动还会一直保留在确认屏幕上,直到用户主动将其移除,或交由体系在四小时后将其移除。即实时活动会灵动上岛最多保留八小时,在确认屏幕上最多保留十二小时

  • 更新

每个实时活动运转在自己的沙盒中,与小组件不同的是,它无法访问网络或接收方位更新。开发者若要更新实时活动的动态数据,请在 App 中运用 ActivityKit 结构或答应实时活动接纳长途推送告知。但需求留意,ActivityKit 更新和长途推送告知更新的更新动态数据巨细不能超越 4KB

  • 款式

实时活动针对确认屏幕和灵动岛供给了不同的视图。确认屏幕能够呈现在一切支撑 iOS 16 的设备上。而灵动岛在支撑设备上,运用以下视图显现实时活动:紧凑前视图、紧凑尾视图、最小视图和扩展视图。

当用户接触灵动岛,且灵动岛中有紧凑或最小视图,一起实时活动更新时,会呈现扩展视图。在不支撑灵动岛的设备上,扩展视图显现为实时活动更新的横幅。

为保证体系能够在每个方位显现 App 的实时活动,开发者有必要支撑一切视图

为 App 增加对实时活动的支撑

描绘实时活动界面的代码是 App 的小组件的一部分。假如开发者现已在 App 中供给小组件,则能够将实时活动的界面代码增加到现有的小组件中,而且能够在小组件和实时活动之间重用部分代码。但虽然实时活动利用了 WidgetKit 的功用,但它们并不是小组件。与更新小组件界面的 timeline 机制比较,开发者只能运用 ActivityKit 或长途推送告知来更新实时活动。

开发者也能够创立一个小组件来完成实时活动,而无需供给小部件。但请尽或许考虑一起供给小组件和实时活动,以满意用户的需求。

本文将参阅 Apple 的开发文档,完成实时活动。

创立项目并为 App 增加对实时活动的支撑

创立项目 LiveActivities 并为项目增加新 Target,挑选 Widget Extension:

实时活动(Live Activity) - 在锁定屏幕和灵动岛上显示应用程序的实时数据
实时活动(Live Activity) - 在锁定屏幕和灵动岛上显示应用程序的实时数据
实时活动(Live Activity) - 在锁定屏幕和灵动岛上显示应用程序的实时数据

能够将其命名 LiveActivitiesWidget,暂时不需求勾选 Include Configuration Intent,单击 Finsih 并同意 Activate scheme 对话框:

实时活动(Live Activity) - 在锁定屏幕和灵动岛上显示应用程序的实时数据
实时活动(Live Activity) - 在锁定屏幕和灵动岛上显示应用程序的实时数据

info.plist 增加 NSSupportsLiveActivities key,并设置为YES

实时活动(Live Activity) - 在锁定屏幕和灵动岛上显示应用程序的实时数据

界说实时活动的静态和动态数据

在为 App 的实时活动创立装备目标之前,首先经过完成 ActivityAttributes (描绘实时活动的内容的协议)来描绘实时活动将展现的数据。它的声明如下:

public protocol ActivityAttributes : Decodable, Encodable {
    /// The associated type that describes the dynamic content of a Live Activity.
    ///
    /// The dynamic data of a Live Activity that's encoded by `ContentState` can't exceed 4KB.
    associatedtype ContentState : Decodable, Encodable, Hashable
}

除了包含实时活动中呈现的静态数据,开发者还能够运用 ActivityAttributes 来声明所需的自界说 Activity.ContentState 类型,该类型描绘 App 的实时活动的动态数据。

咱们将以一个披萨外卖行程为例,新建一个 PizzaDeliveryAttributes.swift 文件并增加以下内容:

import Foundation
import ActivityKit
struct PizzaDeliveryAttributes: ActivityAttributes {
    public typealias PizzaDeliveryStatus = ContentState
    public struct ContentState: Codable, Hashable {
        var driverName: String
        var deliveryTimer: ClosedRange<Date>
    }
    var numberOfPizzas: Int
    var totalAmount: String
    var orderNumber: String
}

在上面的示例中,PizzaDeliveryAttributes 描绘了以下静态数据:订货的比萨饼数量、客户需求付出的金额以及订单号。

留意代码是怎么界说 Activity.ContentState 来封装动态数据的:送披萨的司机的名字和估计送达时刻。

此外,该示例界说了类型别号 PizzaDeliveryStatus 以使代码更具描绘性和易于阅读。

创立实时活动装备

接着,咱们需求增加代码,在小组件的完成中回来 ActivityConfiguration。以下运用上一个示例中的 PizzaDeliveryAttributes 结构来装备咱们的现实活动:

import SwiftUI
import WidgetKit
@main
struct LiveActivitiesWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: PizzaDeliveryAttributes.self) { context in
            // Create the view that appears on the Lock Screen and as a
            // banner on the Home Screen of devices that don't support the
            // Dynamic Island.
            // ...
        } dynamicIsland: { context in
            // Create the views that appear in the Dynamic Island.
            // ...
        }
    }
}

假如咱们的 App 现已供给了小组件,请将实时活动增加到 WidgetBundle 里。 假如没有 WidgetBundle,例如 App 当时只供给一个小组件,请按照创立 Widget Extension 中的描绘创立一个 WidgetBundle,然后将实时活动增加到其间,例如:

@main
struct LiveActivitiesWidgets: WidgetBundle {
    var body: some Widget {
        FavoritePizzaWidget()
        if #available(iOS 16.1, *) {
            PizzaDeliveryLiveActivity()
        }
    }
}

创立确认屏幕视图

要创立实时活动的界面,咱们能够在之前创立的 Widget Extension 中运用 SwiftUI。与小组件类似,咱们无需为实时活动供给界面的巨细,而是让体系确认恰当的尺度

新建文件 LockScreenLiveActivityView.swift,并增加以下代码,运用 SwiftUI 视图描绘PizzaDeliveryAttributes 的信息:

import WidgetKit
import SwiftUI
struct LockScreenLiveActivityView: View {
    let context: ActivityViewContext<PizzaDeliveryAttributes>
    var body: some View {
        VStack {
            Spacer()
            Text("\(context.state.driverName) is on their way with your pizza!")
            Spacer()
            HStack {
                Spacer()
                Label {
                    Text("\(context.attributes.numberOfPizzas) Pizzas")
                } icon: {
                    Image(systemName: "bag")
                        .foregroundColor(.indigo)
                }
                .font(.title2)
                Spacer()
                Label {
                    Text(timerInterval: context.state.deliveryTimer, countsDown: true)
                        .multilineTextAlignment(.center)
                        .frame(width: 50)
                        .monospacedDigit()
                } icon: {
                    Image(systemName: "timer")
                        .foregroundColor(.indigo)
                }
                .font(.title2)
                Spacer()
            }
            Spacer()
        }
        .activitySystemActionForegroundColor(.indigo)
        .activityBackgroundTint(.cyan)
    }
}

这儿需求留意,假如其高度超越 160,体系或许会截断确认屏幕上的实时活动。

创立紧凑和最小的视图

在支撑实时活动的设备的灵动岛上,当 App 开端一个实时活动而且它是唯一一个活泼的实时活动时,紧凑前视图和尾视图一起呈现,在灵动岛中形成一个有凝聚力的视图。当多个实时活动处于活动状况时(无论是来自咱们的 App 还是来自多个 App),体系会挑选哪些实时活动可见,并显现两个最小视图:一个最小视图显现附加到灵动岛,而另一个显现为分离的款式。

默许情况下,灵动岛中的紧凑视图和最小视图运用黑色布景色彩和白色文本。 运用 keylineTint(_:) 修改器将可选的色彩运用到灵动岛,例如青色,稍后咱们会看到。

以下示例展现了披萨外卖运用程序怎么运用 SwiftUI 视图供给所需的紧凑和最小视图:

@main
struct LiveActivitiesWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: PizzaDeliveryAttributes.self) { context in
            // Create the view that appears on the Lock Screen and as a
            // banner on the Home Screen of devices that don't support the
            // Dynamic Island.
            // ...
        } dynamicIsland: { context in
            // Create the views that appear in the Dynamic Island.
            DynamicIsland {
                // Create the expanded view.
                // ...
            } compactLeading: {
                Label {
                    Text("\(context.attributes.numberOfPizzas) Pizzas")
                } icon: {
                    Image(systemName: "bag")
                        .foregroundColor(.indigo)
                }
                .font(.caption2)
            } compactTrailing: {
                Text(timerInterval: context.state.deliveryTimer, countsDown: true)
                    .multilineTextAlignment(.center)
                    .frame(width: 40)
                    .font(.caption2)
            } minimal: {
                VStack(alignment: .center) {
                    Image(systemName: "timer")
                    Text(timerInterval: context.state.deliveryTimer, countsDown: true)
                        .multilineTextAlignment(.center)
                        .monospacedDigit()
                        .font(.caption2)
                }
            }
            .keylineTint(.cyan)
        }
    }
}

创立扩展视图

除了紧凑和最小视图之外,咱们还有必要支撑扩展视图。当用户接触并持有一个紧凑或最小的视图时,扩展视图会呈现,而且也会短显现实时活动更新。当咱们更新实时活动时,没有灵动岛的设备也会将扩展视图显现为横幅。 运用 DynamicIslandExpandedRegionPosition 指定咱们希望的灵动岛扩展区域方位。以下示例显现了披萨外卖运用程序怎么创立其扩展视图:

@main
struct PizzaDeliveryWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: PizzaDeliveryAttributes.self) { context in
            // Create the view that appears on the Lock Screen and as a
            // banner on the Home Screen of devices that don't support the
            // Dynamic Island.
            LockScreenLiveActivityView(context: context)
        } dynamicIsland: { context in
            // Create the views that appear in the Dynamic Island.
            DynamicIsland {
                // Create the expanded view.
                DynamicIslandExpandedRegion(.leading) {
                    Label("\(context.attributes.numberOfPizzas) Pizzas", systemImage: "bag")
                        .foregroundColor(.indigo)
                        .font(.title2)
                }
                DynamicIslandExpandedRegion(.trailing) {
                    Label {
                        Text(timerInterval: context.state.deliveryTimer, countsDown: true)
                            .multilineTextAlignment(.trailing)
                            .frame(width: 50)
                            .monospacedDigit()
                    } icon: {
                        Image(systemName: "timer")
                            .foregroundColor(.indigo)
                    }
                    .font(.title2)
                }
                DynamicIslandExpandedRegion(.center) {
                    Text("\(context.state.driverName) is on their way!")
                        .lineLimit(1)
                        .font(.caption)
                }
                DynamicIslandExpandedRegion(.bottom) {
                    Button {
                        // Deep link into your app.
                    } label: {
                        Label("Call driver", systemImage: "phone")
                    }
                    .foregroundColor(.indigo)
                }
            } compactLeading: {
                // Create the compact leading view.
                // ...
            } compactTrailing: {
                // Create the compact trailing view.
                // ...
            } minimal: {
                // Create the minimal view.
                // ...
            }
            .keylineTint(.yellow)
        }
    }
}

这儿也需求留意,假如灵动岛的高度超越 160,体系或许会截断灵动岛中的实时活动。

为了呈现打开的实时活动中呈现的视图,体系将打开的视图划分为不同的区域。请留意上述示例怎么回来一个指定多个 DynamicIslandExpandedRegion 目标的灵动岛。传递以下 DynamicIslandExpandedRegionPosition 值以在打开视图中的指定方位布置内容:

  • center 将内容置于 TrueDepth 摄像头下方。

  • leading 将内容沿打开的实时活动的前沿放置在 TrueDepth 摄像头周围,并在其下方包裹其他内容。

  • trailing 将内容沿打开的实时活动的后缘放置在 TrueDepth 摄像头周围,并在其下方包裹其他内容。

  • bottom 将内容置于 leading、trailing 和 center 之下。

实时活动(Live Activity) - 在锁定屏幕和灵动岛上显示应用程序的实时数据

为了呈现打开的实时活动中呈现的内容,体系首先确认 center 内容的宽度,一起考虑 leading 和 trailing 内容的最小宽度。 然后体系依据其笔直方位放置 leading 和 trailing 内容并确认其巨细。默许情况下, leading 和 trailing 接持平水平空间。

实时活动(Live Activity) - 在锁定屏幕和灵动岛上显示应用程序的实时数据

咱们能够经过将优先级传递给 init(_:priority:content:) 初始化程序来告知体系优先考虑 DynamicIslandExpandedRegion 视图之一。 体系以灵动岛的全宽呈现具有最高优先级的视图。

假如内容太宽而无法呈现在 TrueDepth 相机周围的 leading 方位,请运用 belowIfTooWide 修饰符来烘托 TrueDepth 相机下方的 leading 内容。

运用自界说色彩

默许情况下,体系运用默许的文本色和最适合用户确认屏幕的实时活动的布景色彩。假如要设置自界说色彩色彩,请运用 activityBackgroundTint(_:) 视图修饰符。此外,运用 activitySystemActionForegroundColor(_:) 视图修饰符来界说体系在确认屏幕上实时活动周围显现的辅佐操作按钮的文本色彩。

要设置自界说的半透明布景色,请运用 opacity(_:) 视图修饰符或指定一个不透明布景色彩。

在 Always-On Retina 显现屏的设备上,体系会调暗屏幕以延伸电池寿数,并在确认屏幕上呈现实时活动,就像在暗形式下一样。 运用 SwiftUI 的 isLuminanceReduced 环境值来检测 Always On 并运用在 Always On 中看起来很棒的图画。

保证实时活动可用

实时活动仅在 iPhone 上可用。假如咱们的 App 可在多个平台上运用并供给小组件,请保证实时活动在运转时可用。此外,用户能够在“设置”运用中挑选停用运用的实时活动。

要检查实时活动是否可用以及用户是否答应咱们的 App 运用实时活动:

  • 运用 areActivitiesEnabled 同步的确认 App 是否能够发动实时活动;

  • 经过运用 activityEnablementUpdates 异步序列调查运用是否能够发动实时活动。

一个运用能够发动多个实时活动,而一个设备能够运转多个 App 的实时活动。除了保证实时活动可用之外,在开端、更新或完毕实时活动时请留意处理或许呈现的过错。例如,发动实时活动或许会失败,由于用户的设备或许已达到其运转实时活动的约束。

开端实时活动

当运用程序在前台时,咱们能够在运用程序代码中运用 request(attributes:contentState:pushType:) 函数发动实时活动。它将开发者创立的 attributescontentState 作为参数来供给显现在实时活动中的初始值,并告知体系哪些数据是动态的。假如咱们运用长途推送告知来更新实时活动,还需求供给 pushType 参数。

更新 LiveActivities 中的 ContentView.swift 以下代码示例早年面的示例中为披萨外卖发动了一个新的实时活动:

import SwiftUI
import ActivityKit
struct ContentView: View {
    let minutes = 12
    @State var deliveryActivity: Activity<PizzaDeliveryAttributes>? = nil
    var body: some View {
        VStack {
            Text("Hello, world!")
        }
        .onAppear {
            if #available(iOS 16.1, *) {
                let future = Calendar.current.date(byAdding: .minute, value: (minutes), to: Date())!
                let date = Date.now...future
                let initialContentState = PizzaDeliveryAttributes.ContentState(driverName: "Layer", deliveryTimer:date)
                let activityAttributes = PizzaDeliveryAttributes(numberOfPizzas: 3, totalAmount: "$66.66", orderNumber: "12345")
                do {
                    deliveryActivity = try Activity.request(attributes: activityAttributes, contentState: initialContentState)
                } catch (let error) {
                }
            }
        }
    }
}

请留意上面的代码片段不传递 pushType 参数,在不运用长途推送告知的情况下更新其内容。它还将回来的deliveryActivity 存储,可用于更新和完毕实时活动。有关运用长途推送告知更新您的实时活动的更多信息,请参阅 Updating and ending your Live Activity with remote push notifications。

咱们只能在运用程序处于前台时从 App 发动实时活动。 可是咱们能够在 App 在后台运转时更新或完毕实时活动,例如经过运用后台使命。

发动多个实时活动:

更新实时活动

当咱们从 App 发动实时活动时,运用发动实时活动时收到的 Activity 目标的 update(using:) 函数更新显现在实时活动中的数据。要检索 App 当时活动的实时活动,请运用 activities。

例如,披萨配送能够更新显现配送状况的实时活动,其间包含新的配送时刻和新的司机。它还能够运用 update(using:alertConfiguration:) 函数在 iPhone 和 Apple Watch 上显现提示,告知用户新的实时活动内容,在 Button("Order pizza!"){} 后增加以下代码:

Button("Update!") {
    if #available(iOS 16.1, *) {
        let future = Calendar.current.date(byAdding: .minute, value: (Int(minutes / 2)), to: Date())!
        let date = Date.now...future
        let updatedDeliveryStatus = PizzaDeliveryAttributes.PizzaDeliveryStatus(driverName: "Layer's brother", deliveryTimer: date)
        let alertConfiguration = AlertConfiguration(title: "Delivery Update", body: "Your pizza order will immediate delivery.", sound: .default)
        Task {
            try? await Task.sleep(nanoseconds: 5_000_000_000)
            await deliveryActivity?.update(using: updatedDeliveryStatus, alertConfiguration: alertConfiguration)
        }
    }
}

在点击“Update!”的 5 秒后,配送员和时刻都发生了改变,一起有录屏无法很好表现的提示作用:

在 Apple Watch 上,体系运用提示的标题和正文。在 iPhone 上,体系不会显现惯例提示,而是显现灵动岛中打开的实时活动。 在不支撑灵动岛的设备上,体系会在主屏幕上显现一个横幅,该横幅运用 App 的实时活动的扩展视图。

带有动画的内容更新

当咱们界说实时活动的界面时,体系会疏忽任何动画修饰符——例如,withAnimation(_:_:)animation(_:value:)——并改用体系的动画时刻。但当实时活动的动态内容发生改变时,体系会履行一些动画。

  • 文本视图经过含糊的内容过渡动画展现内容改变,而且体系为图画和 SF Symbols 做动画的内容过渡。

  • 假如开发者依据内容或状况更改用户界面,进行增加或删去视图,视图会淡入淡出。

  • 能够运用以下视图转化来装备这些内置转化:opacitymove(edge:)slidepush(from:)

  • 运用 numericText(countsDown:) 完成计时作用的文本。

在配备 Always On Retina 显现屏的设备上,为例坚持 Always On 时的电量,体系不会履行动画。能够在动画内容更改之前运用 SwiftUI 的 isLuminanceReduced 环境值来检测是否敞开 Always On。

在 App 中完毕实时活动

始终在相关的使命或实时事件完毕后,完毕实时活动的展现。已完毕的实时活动将保留在确认屏幕上,直到用户将其删去或体系主动将其删去。主动删去取决于开发者供给给 end(using:dismissalPolicy:) 函数的免除战略。

此外,始终包含更新的 Activity.ContentState 以保证实时活动在完毕后显现最新和终究的内容。这很重要,由于实时活动或许还会在确认屏幕上坚持一段时刻的可见。

继续在 Button("Update!") {} 后增加代码:

Button("I do not want it!!") {
    if #available(iOS 16.1, *) {
        let finalDeliveryStatus = PizzaDeliveryAttributes.PizzaDeliveryStatus(driverName: "Anne Johnson", deliveryTimer: Date.now...Date())
        Task {
            try? await Task.sleep(nanoseconds: 5_000_000_000)
            await deliveryActivity?.end(using:finalDeliveryStatus, dismissalPolicy: .default)
        }
    }
}

上面的示例运用默许免除战略。因此,实时活动完毕后会在确认屏幕上显现一段时刻,以便用户浏览手机以检查最新信息。用户能够随时挑选移除实时活动,或许体系在活动完毕四小时后主动移除。要立即删去确认屏幕的完毕实时活动,请运用 .immediate。或许,运用 after(_:) 指定四小时内的日期。

用户能够随时从确认屏幕中删去 App 的实时活动。这只会完毕 App 的实时活动的展现,但不会完毕或撤销用户发动实时活动的操作。例如用户能够从确认屏幕中删去他们的披萨外卖的实时活动,但这不会撤销披萨订单。

当用户或体系移除实时活动时,ActivityState 更改为 ActivityState.dismissed

运用长途推送告知更新或完毕实时活动

除了运用 ActivityKit 从 App 更新和完毕实时活动之外,咱们还能够运用从服务器发送到 Apple 推送告知服务 (APN) 的长途推送告知更新或完毕实时活动。 要了解有关运用长途推送告知更新实时活动的更多信息,请参阅 Updating and ending your Live Activity with remote push notifications。

盯梢更新

像上面的例子,当咱们发动实时活动时,ActivityKit 回来一个 Activity 目标。 除了唯一标识每个实时活动的 id 之外,还供给序列来调查内容状况、活动状况和 Push token 的更新。运用相应的序列在 App 中接纳更新,使 App 和 实时活动坚持同步:

  • 要观实时活动的状况,例如确认它是处于活动状况还是现已完毕,运用 activityStateUpdates

  • 要调查实时活动内容的改变,运用 contentState

  • 要调查实时活动的 Push token 的改变,运用 pushTokenUpdates

获取实时活动列表

一个 App 能够发动多个实时活动。例如,体育运用程序或许答运用户为他们感兴趣的每个现场体育比赛发动一个 实时活动。假如 App 发动多个实时活动,请运用 activityUpdates 函数获取有关 App 正在进行的活动的告知。盯梢正在进行的实时活动以保证 App 的数据与 ActivityKit 盯梢的正在运转的实时活动同步。

以下代码段显现了披萨外卖怎么检索正在进行的活动列表:

// Fetch all ongoing pizza delivery Live Activities.
for await activity in Activity<PizzaDeliveryAttributes>.activityUpdates {
    print("Pizza delivery details: \(activity.attributes)")
}

获取一切活泼的实时活动列表的另一个计划是开发者手动维护正在进行的实时活动数据,并保证开发者不会让任何活动运转超越需求的时刻。例如体系或许会中止咱们的 App,或许咱们的 App 或许会在实时活动处于活泼状况时崩溃。当运用下次发动时,检查是否有任何实时活动仍然处于活泼状况,更新存储的实时活动列表数据,并完毕任何不再相关的实时活动。

运用长途推送告知更新实时活动

ActivityKit 向咱们供给了从 App 发动、更新和完毕实时活动的功用。 此外,它还供给接纳 Push token 的功用,开发者能够运用长途推送告知来更新 App 的实时活动,这些长途推送告知从开发者的服务器发送到 Apple 推送告知服务 (APNs)。

检查 User Notification 文档

接纳长途推送告知更新实时活动,类似于咱们运用惯例的告知推送方法。咱们运用 User Notifications 来恳求运用告知权限,而且有必要设置长途告知服务器。关于实时活动,此使命与在用户设备上显现一般告知的长途推送告知相同。

假如咱们不熟悉长途推送告知,请检查 User Notifications 结构的文档。 保证阅读了Registering Your App with APNs 和 Asking Permission to Use Notifications。

要运用长途推送告知来更新 App 的实时活动,咱们需求运用 Push token 经过 APNs 进行身份验证,如 Establishing a Token-Based Connection to APNs 中所述。

了解推送的更多信息,欢迎检查笔者的另一篇文章 朴素 Push 普识——了解 Push Notifications 全貌。

更新 App 代码并创立 Push Notification Server

在咱们的 Xcode 项目中,首先将推送告知功用增加到 App 中,如运用 APNs 注册咱们的运用程序中所述,但不要运用 registerForRemoteNotifications() 为长途推送告知注册实时活动。请运用 ActivityKit 获取 Push token 。

要运用推送告知更新或完毕实时活动:

  1. 假如咱们尚未完成长途推送告知服务器,请创立一个运用 APNs 发送长途推送告知的服务器运用程序。
  2. 在咱们的 App 中发动实时活动,并保证将 pushType 参数传递给 request(attributes:contentState:pushType:) 函数。
  3. 成功发动实时活动后,咱们会收到一个带有 pushToken 的 Activity 目标。将其发送到咱们的推送告知服务器,并运用它来发送更新或完毕实时活动的长途推送告知。
  4. 运用你存储在服务器上的 pushToken 向实时活动发送推送告知。咱们有必要设置 content-state key 以匹配自界说的 Activity.ContentState 类型,以保证体系能够解码 JSON 有用负载并更新实时活动。
  5. 将咱们发送给 APNs 的恳求的 apns-push-type 标头字段的值设置为 liveactivity
  6. 运用以下格局设置咱们发送到 APNs 的恳求的 apns-topic 标头字段:<your bundleID>.push-type.liveactivity
  7. 要更新实时活动,请将有用负载的 event key 的值设置为 update。要完毕实时活动,请将其设置为 end。假如咱们完毕实时活动,请包含终究 content-sate,以保证实时活动在完毕后显现最新数据。
  8. 运用 pushTokenUpdates 调查 Push token 的更改,将任何新推送令牌发送到咱们的服务器,并使服务器上的旧令牌无效。
  9. 当咱们的实时活动完毕时,使服务器上的 Push token 无效。

要在模拟器中测验实时活动的长途推送告知,请运用配备 Apple T2 安全芯片的 Mac 或配备运转 macOS 13 或更高版别的 Apple 芯片的 Mac。

以下有用负载会更新比萨配送的司机名字和配送时刻。 content-state 的内容有必要与咱们在 ActivityAttributes 完成中声明的自界说 Activity.ContentState 类型的特点相匹配。

在以下示例中,content-state 与运用实时活动显现实时数据中的示例中的自界说 PizzaDeliveryStatus 类型的特点相匹配。 此外,示例有用负载包含一个 alert,让用户知道更新的实时活动内容:

{
    "aps": {
        "timestamp": 1168364460,
        "event": "update",
        "content-state": {
            "driverName": "Anne Johnson",
            "estimatedDeliveryTime": 1659416400
        },
        "alert": {
            "title": "Delivery Update",
            "body": "Your pizza order will arrive soon.",
            "sound": "example.aiff" 
        }
    }
}

请留意, sound 不是有必要供给的。上面的示例为坚持简单,没有运用本地化字的符串作为 alert title 和 body。在咱们 App 的警报完成中,请考虑本地化这两个字符串。有关显现长途告知警报的更多信息,请参阅 Generating a remote notification。

需求留意的是,用户的设备或许不会收到长途推送告知,例如用户没有网络连接。相同,假如推送告知在实时活动完毕后抵达,体系也会疏忽它。这两种情况都或许导致实时活动显现过期的信息。为了协助减少显现过期信息的时机,除了运用长途推送告知之外,还能够从 App 更新咱们的实时活动。

该体系答应每小时有一定的告知预算,以答应频繁更新——例如现场体育比赛。可是,假如咱们超出预算,体系或许会约束推送告知。为避免超出每小时告知预算,咱们能够发送不计入预算的低优先级推送告知。

一个语音通话的 Demo

假如你对以下 Demo 感兴趣,相同能够参阅这儿。

实时活动(Live Activity) - 在锁定屏幕和灵动岛上显示应用程序的实时数据
语音通话 App 灵动岛 多灵动岛 锁屏