Live Activities 让您的 iOS 应用直接在锁屏和动态岛上显示实时更新。非常适合配送跟踪、体育比分或时效性交易更新,让用户无需打开应用即可保持了解。
如果使用原生 iOS(Swift/Objective-C),请遵循 iOS SDK 设置 。
OneSignal iOS SDK 版本 5.2.0+ 用于 push-to-start 支持(查看发布说明 )。
OneSignal iOS SDK 版本 5.2.15+ 用于 点击跟踪 和 确认投递
iOS 16.1+ 和 iPadOS 17+
使用 .p8 APNs 密钥 。Apple 不支持在 Live Activities 中使用 p12 证书。
Xcode 14 或更高版本
这些步骤引导您快速设置 Live Activities。有关更多详情和设计自定义,请参阅 Apple 的 Live Activities 开发者文档 。
在 Xcode 中,转到 File > New > Target… > Widget Extension 。
选择并按 Next 。
通过提供名称(示例:OneSignalWidget)配置 Widget Extension,并确保选中 Include Live Activity 。然后点击 Finish 。
Click Don’t Activate if prompted to activate the scheme.
2. 更新 Info.plist
在您的主目标的 Info.plist 中,添加键 Supports Live Activities 作为 Boolean ,并将其设置为 YES。
如果您以编程方式设置它,应该如下所示:
< key > NSSupportsLiveActivities </ key >
< true />
更新 Live Activities 时,您可以选择设置”优先级”,Apple 使用它来确定更新的紧急程度。Apple 有内部阈值,会对过于频繁使用高优先级标志的请求进行限制。 如果您的 Live Activities 用例依赖于更频繁的高优先级更新,您可以按照 Apple 开发者文档 的指示,将键 NSSupportsLiveActivitiesFrequentUpdates 作为布尔类型设置为 YES 添加到您的 Info.plist 中。当 Live Activity 超过其推送预算时,用户将看到一个对话框,如果他们允许 Live Activity 继续,预算将自动增加以提供无缝的用户体验。
3. 添加 SDK
在您的 Widget Extension 目标中,在General > Frameworks, Libraries and Embedded Content 下添加 OneSignalFramework: 在您项目的 Targets 列表中找到您的 widget extension 目标的名称。示例名称是 OneSignalWidgetExtension。 打开您的 Podfile 并添加以下代码。将 OneSignalWidgetExtension 替换为您的 widget extension 目标的名称。 Podfile
Full Podfile example
target 'OneSignalWidgetExtension' do
use_frameworks!
pod 'OneSignal/OneSignal' , '>= 5.0.0' , '< 6.0'
end
关闭 Xcode 并运行 pod repo update && pod install 来安装 OneSignalLiveActivities pod。 如果在执行此操作后仍然看到”No such module ‘OneSignalLiveActivities‘“错误,您可以通过转到您的 “Widget Extension Target” > General > Frameworks and Libraries > + icon 手动添加依赖项。选择 OneSignalLiveActivities 框架:
4. 定义小组件属性和 UI
打开 your-nameLiveActivity.swift 文件(示例:OneSignalWidgetLiveActivity.swift)来定义结构的属性并对 widget UI 进行更改。
your-nameAttributes 描述您 Live Activity 的静态内容。
ContentState 描述您 Live Activity 的动态内容。
如果您遵循示例,请将下面的代码复制粘贴到您的 OneSignalWidgetLiveActivity.swift 文件中。
your-nameLiveActivity.swift
import ActivityKit
import WidgetKit
import SwiftUI
// 导入 OneSignalLiveActivities 模块
// 如果您收到关于找不到模块的错误,请返回步骤 3 并确保您已正确添加了 OneSignalLiveActivities pod。
import OneSignalLiveActivities
// 更新以继承 OneSignalLiveActivityAttributes
// 这将在您的 API 请求中用于启动 Live Activity
struct OneSignalWidgetAttributes : OneSignalLiveActivityAttributes {
// 更新以继承 OneSignalLiveActivityContentState
public struct ContentState : OneSignalLiveActivityContentState {
// 关于您活动的动态状态属性在这里!
var emoji: String
// 添加对 OneSignalLiveActivityContentStateData 的引用?
var onesignal: OneSignalLiveActivityContentStateData ?
}
// 关于您活动的固定不变属性在这里!
var name: String
// 添加对 OneSignalLiveActivityAttributeData 的引用
var onesignal: OneSignalLiveActivityAttributeData
}
struct OneSignalWidgetLiveActivity : Widget {
var body: some WidgetConfiguration {
// 更新以使用 `for: the-name-of-your-attributes-struct`
// 这将在您的 API 请求中用于启动 Live Activity
ActivityConfiguration ( for : OneSignalWidgetAttributes. self ) { context in
// 锁屏/横幅 UI 在这里
// 更新以显示通过负载发送的属性
VStack {
Text ( "Hello \( context. attributes . name ) \( context. state . emoji ) " )
}
. activityBackgroundTint (Color. cyan )
. activitySystemActionForegroundColor (Color. black )
} dynamicIsland : { context in
DynamicIsland {
// 展开的 UI 在这里。通过各种区域组成展开的 UI,
// 如 leading/trailing/center/bottom
DynamicIslandExpandedRegion (. leading ) {
Text ( "Leading" )
}
DynamicIslandExpandedRegion (. trailing ) {
Text ( "Trailing" )
}
DynamicIslandExpandedRegion (. bottom ) {
Text ( "Bottom \( context. state . emoji ) " )
// 更多内容
}
} compactLeading : {
Text ( "L" )
} compactTrailing : {
Text ( "T \( context. state . emoji ) " )
} minimal : {
Text (context. state . emoji )
}
. widgetURL ( URL ( string : "http://www.apple.com" ))
. keylineTint (Color. red )
}
}
}
5. 允许主目标成员资格
将您的主应用目标添加到 your-nameLiveActivity.swift 文件中的 Target Membership 列表。
在 Xcode 中,打开屏幕右侧的 Inspector 面板。在 Target Membership 中,点击 + 按钮并选择包含 ContentView 和您的 OneSignal 初始化代码的主应用目标。
6. 将设置方法添加到您的 AppDelegate
在 OneSignal SDK 初始化后,在您的 AppDelegate 中调用 OneSignal.LiveActivities.setup。
将 OneSignalWidgetAttributes 替换为您的 Live Activity 属性结构的名称。
// 导入 OneSignalLiveActivities 模块
import OneSignalLiveActivities
// 这应该在初始化 OneSignal SDK 后添加
if #available ( iOS 16.1 , * ) {
OneSignal. LiveActivities . setup (OneSignalWidgetAttributes. self )
// 如果您有多个 Live Activities,您可以在这里使用 setup 方法添加它们
// OneSignal.LiveActivities.setup(LiveActivityWidgetAttributes-2.self)
}
这使用 ActivityKit 异步序列管理和报告更新。
如果您在应用中也直接使用以下任何序列,可能会干扰 OneSignal Live Activity 行为:
activityStateUpdates
pushTokenUpdates
pushToStartTokenUpdates
activityUpdates
启动 Live Activity
在设备上启动 Live Activity 有 2 个选项:
发送推送启动 API 请求 。确保所有名称和 ID 与您的 widget 配置完全匹配(参数区分大小写)。如果有任何遗漏或错误添加,您在尝试启动 widget 时可能会遇到问题。 这是一个适用于上述示例的示例请求。 替换:
YOUR_APP_ID 为您的 OneSignal 应用 ID。
YOUR_APP_API_KEY 为您的 OneSignal API 密钥。
OneSignalWidgetAttributes 为您的 Widget Attributes 结构的名称。
curl --location 'https://api.onesignal.com/apps/YOUR_APP_ID/activities/activity/OneSignalWidgetAttributes' \
--header 'Authorization: key YOUR_APP_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"event": "start",
"activity_id": "push-to-start",
"included_segments": [
"Subscribed Users"
],
"event_attributes": {
"name": "World"
},
"event_updates": {
"emoji":"🤩"
},
"name": "Live Activities Test",
"contents": {
"en": "A push started this Live Activity"
},
"headings": {
"en": "Live Activity Started"
}
}'
如果您遵循提供的示例代码,您应该在设备的锁屏上看到 Live Activity。 您已成功使用 push-to-start 启动了 Live Activity! 用户需要选择”允许”以继续获取更新。
您可以让用户在与您的应用交互时触发 Live Activity。 例如,当用户有活动事件进行中(他们下了订单、游戏正在进行、事件即将开始等)并打开您的应用时,您可以自动显示 Live Activity。 在此示例中,我们将使用按钮手动启动 Live Activity。 import SwiftUI
import ActivityKit
import OneSignalFramework
import OneSignalLiveActivities
struct ContentView : View {
@StateObject private var viewModel = LiveActivityViewModel ()
var body: some View {
VStack {
Button ( "Start Live Activity" ) {
viewModel. startLiveActivity ()
}
}
}
}
class LiveActivityViewModel : ObservableObject {
func startLiveActivity () {
let osAttributes = OneSignalLiveActivityAttributeData. create ( activityId : "click-to-start" )
let attributes = OneSignalWidgetAttributes ( name : "OneSignal" , onesignal : osAttributes)
let contentState = OneSignalWidgetAttributes. ContentState ( emoji : "🤩" , onesignal : nil )
do {
let activity = try Activity < OneSignalWidgetAttributes > . request (
attributes : attributes,
contentState : contentState,
pushType : . token )
} catch {
print (error. localizedDescription )
}
}
}
点击按钮后它将启动 Live Activity。
跟踪 Live Activity 点击
通过实施 OneSignal 的点击跟踪来跟踪用户何时点击您的 Live Activities 和动态岛。这使您能够测量参与度并可选择将用户深度链接到应用中的特定内容。
将 .onesignalWidgetURL() 修饰符添加到您 Live Activity widget 中希望跟踪点击的任何 UI 组件:
import OneSignalLiveActivities
struct ExampleAppFirstWidget : Widget {
var body: some WidgetConfiguration {
ActivityConfiguration ( for : ExampleAppFirstWidgetAttributes. self ) { context in
VStack {
Spacer ()
Text ( "Sample Text " + context. attributes . title ). font (. headline )
// 其他 UI 代码
}
. onesignalWidgetURL ( URL ( string : "myapp://settings" ), context : context)
// 如果您不需要深度链接,请传递 nil:.onesignalWidgetURL(nil, context: context)
} dynamicIsland : { context in
DynamicIsland {
DynamicIslandExpandedRegion (. leading ) {
Text ( "Leading" )
}
// 其他动态岛区域
} compactLeading : {
Text ( "L" )
} compactTrailing : {
Text ( "T" )
} minimal : {
Text ( "Min" )
}
. onesignalWidgetURL ( URL ( string : "myapp://settings" ), context : context)
// 可选:单独跟踪动态岛点击
}
}
}
重要注意事项:
您可以传递用于深度链接的 URL,或者如果您只想跟踪点击而无需导航,则传递 nil
如果您使用 .onesignalWidgetURL(),视图层次结构不能包含 Apple 的 .widgetURL() 修饰符
如果您想跟踪两者的点击,请将修饰符应用于主 Live Activity 视图和动态岛
步骤 2:在您的应用中处理 URL
在您的应用中添加 URL 处理以跟踪点击并适当路由用户:
import OneSignalLiveActivities
// AppDelegate 示例:
func application ( _ app : UIApplication, open url : URL, options : [UIApplication.OpenURLOptionsKey : Any ] = [ : ]) -> Bool {
let originalURL = OneSignal. LiveActivities . trackClickAndReturnOriginal (url)
// 处理原始 URL 并导航用户
if let url = originalURL {
// 您的自定义 URL 路由逻辑
return handleDeepLink (url)
}
return false
}
// SceneDelegate 示例:
func scene ( _ scene : UIScene, openURLContexts URLContexts : Set ) {
guard let url = URLContexts. first ? . url else { return }
let originalURL = OneSignal. LiveActivities . trackClickAndReturnOriginal (url)
if let url = originalURL {
// 您的自定义 URL 路由逻辑
handleDeepLink (url)
}
}
// SwiftUI 示例:
. onOpenURL { url in
let originalURL = OneSignal. LiveActivities . trackClickAndReturnOriginal (url)
if let url = originalURL {
// 您的自定义 URL 路由逻辑
handleDeepLink (url)
}
}
trackClickAndReturnOriginal() 方法自动使用 OneSignal 跟踪点击并返回您在 widget 中指定的原始 URL 供您的应用处理。
更新 Live Activity
使用更新 Live Activity API 来更新活动的 widgets。
匹配启动活动时使用的 activity_id。
此示例请求将更新 push-to-start widget,因为它具有我们定义的标题为 push-to-start 的 activity_id。
要更新 click-to-start widget,请更新请求路径以使用 click-to-start 而不是 push-to-start。
curl --location 'https://api.onesignal.com/apps/YOUR_APP_ID/live_activities/push-to-start/notifications' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: key YOUR_API_KEY' \
--data '{
"event": "update",
"event_updates": {
"emoji": "😎"
},
"contents": {
"en": "A push updated this Live Activity"
},
"name": "Live Activity Updated"
}'
结束 Live Activity
使用相同的更新 Live Activity API ,我们可以通过设置 "event": "end" 来结束 Live Activity。
curl --location 'https://api.onesignal.com/apps/YOUR_APP_ID/live_activities/my_activity_id/notifications' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: key YOUR_API_KEY' \
--data '{
"event": "end",
"event_updates": {
"emoji": "👋"
},
"contents": {
"en": "A push ended this Live Activity"
},
"name": "Live Activity Ended"
}'
Live Activity 的其他结束方式:
使用我们的 SDK exit() 方法 。
用户手动滑动 Live Activity 使其消失。
用户在 iOS 设置中撤销 Live Activities 权限。
您已成功结束 Live Activity 并完成了示例!
最佳实践和建议
设计考虑
遵循 Apple 的 Live Activities 人机界面指南 。
优先考虑重要信息,使其易于一目了然地理解。
不要在您的应用中添加吸引对动态岛注意的元素。
使用边距并保持元素之间的空间。
为背景使用醒目的颜色。为浅色和深色模式进行设计。
功能性
设置回退消息
在某些情况下,用户无法接收到 live activity 的更新(在已启动一个后),打开应用应该刷新它们以继续
为了解决这个问题,您可以将stale date 设置为未来的日期和时间,在您知道会向用户发送第一个更新后,那些没有收到更新的用户将显示回退消息。
您可以在 widget UI 中监听这种”stale”状态以显示回退消息:
struct ptsLiveActivity : Widget {
var body: some WidgetConfiguration {
ActivityConfiguration ( for : ptsAttributes. self ) { context in
//这将在 stale date 后翻转为 true
let isStale = context. isStale
if ! isStale{
VStack {
Text ( " \( context. attributes . name ) \( context. state . emoji ) " )
. activityBackgroundTint (Color. cyan )
. activitySystemActionForegroundColor (Color. black )
}
} else {
//如果消息是陈旧的,我们要求用户点击 widget 来打开应用
VStack {
Text ( "出了问题,请点击刷新" )
}
}
//... widget UI 的其余部分
}
}
}
常见问题
高优先级更新的预算是多少?
Apple 不为高优先级(priority: 10)更新提供固定限制,但他们确实执行动态系统级预算。在短时间内发送过多高优先级更新可能导致限制,更新被延迟或丢弃。
要降低限制风险:
使用混合优先级:Apple 建议同时使用 priority: 5(标准)和 priority: 10(高)以取得平衡。
仅为时效性或关键更新保留 priority: 10(如订单状态变化、游戏比分)。
如果您的用例需要频繁更新:
将键 NSSupportsLiveActivitiesFrequentUpdates 添加到您应用的 Info.plist 文件中,设置为布尔值 YES。
当超过此预算时,iOS 可能会提示用户允许额外更新。如果用户同意,Apple 将自动扩展允许的更新限制以保持无缝体验。
有关更多详情,请参阅 Apple 开发者文档 。
我可以从主应用读取 Live Activity 更新吗?
是的。您可以观察更新以进行调试或 UI 同步:
Task {
for await content in activity.contentUpdates {
print ( "LA activity id: \( activity. id ) , content update: \( content. state ) " )
}
}
// Example output:
// LA activity id: EFE6D54D-F7E1-45EF-9514-4C2598F8DED2, content update: ContentState(message: "My message from payload")
跟踪生命周期变化:
Task {
for await state in activity.activityStateUpdates {
print ( "LA state update: \( state ) " )
//If you wanted to do something based on the state, you would use this:
if state != ActivityState.active {
//Do something here
}
}
}
// Example output 1 - LA ended, but still visible
// LA state update: active
/* State can be equal to 4 possible values on the ActivityState enum
active, dismissed, ended, and stale
*/
API 返回 400 错误,提示我超过了订阅者限制。我该怎么办?
如果您的推送订阅者数量超过了您计划的推送订阅者限制,请将您的账户升级到下一个计划,或联系 support@onesignal.com。有关最新计划详情,请点击这里查看 。
如何避免同时发送推送和 Live Activities?
您的应用可能已经发送了一系列推送通知,而您设计的 Live Activity 替代了这些推送通知的需要。例如,如果您通过推送发送比分更新,您可以通过 Live Activity 来替代。
为了确保您的用户不会收到太多消息,我们建议当您的用户选择加入 Live Activity 时,添加一个数据标签。通过添加此数据标签,您可以从可能包含相同或类似内容的推送消息中排除具有此数据标签的用户。阅读更多关于数据标签 和细分 的信息。
故障排除
无接收者
为了在尝试启动或更新 Live Activity 时找到您的用户,您必须确保活动类型、widget 和 cURL 请求都具有匹配的值。
检查请求中的路径参数,确保您向服务器发送的是格式正确的请求。应用 ID 必须与您在 OneSignal.Initialize 方法中使用的应用 ID 匹配,活动类型必须与您在 Live Activity 文件中定义的类型匹配。
在推送启动 API 请求 的主体中,您应该具有以下参数:
event: "start"
event_updates: 您在活动类型下的结构中定义的并在您的 widget 中使用的动态数据。确保请求、类型和 widget 之间的字母大小写和变量都匹配。
event_attributes: 静态数据遵循与事件更新相同的逻辑,必须包含使用中的所有变量,并且必须在 live activity 和请求的所有部分中匹配
activity_id: 这将为 widget 分配一个 ID,是在用户设备上启动活动后用于更新活动的 ID。
name: Live Activity 名称。
contents: 发送推送所需的消息内容。
headings: 发送推送所需的消息标题。
定向参数,如 included_segments。可用选项 。
活动已发送但未接收
确保请求格式正确。如果省略了 Widget 中使用的任何字段,活动可能无法按预期启动或更新。
在您的 API 请求中,确定您设置的 priority 级别。如果您将其设置为 10(最高优先级),请尝试将其降低到 5 并重新测试。Apple 会根据他们自己的内部速率限制对发送过于频繁的请求进行限制。
如果您的用例依赖于更频繁的更新,请按照 Apple 开发者文档 的指示,将键 NSSupportsLiveActivitiesFrequentUpdates 作为布尔类型设置为 YES 添加到您的 Info.plist 中。当 Live Activity 超过其推送预算时,用户将看到一个对话框,如果他们允许 Live Activity 继续,预算将自动增加以提供无缝的用户体验。
需要帮助? 与我们的支持团队聊天或发送邮件至 support@onesignal.com 请包含以下信息:
您遇到的问题详情以及复现步骤(如有)
您的 OneSignal 应用 ID
外部 ID 或订阅 ID(如适用)
您在 OneSignal 控制台中测试的消息 URL(如适用)
任何相关的日志或错误信息
我们很乐意为您提供帮助!