You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

用Live Activity替代推送通知实现计时交互的技术问题

用Live Activity替代本地通知实现倒计时交互方案

核心思路

Live Activity本身没有内置的调度触发API,所以需要保留本地通知的调度逻辑,让原通知触发时间点:

  1. 播放提示音
  2. 自动启动Live Activity展示1分钟倒计时
  3. 处理Live Activity的点击(1分钟内点击→Success)和超时(1分钟未点击→Failed)逻辑

一、修正基础代码错误

1. 修复Activity Attributes语法错误

原代码中typealias语法错误,修正后:

struct TimeActivityAttributes: ActivityAttributes {
    // 定义动态状态类型
    public struct TimerStatus: Codable, Hashable {
        var endTime: Date
    }

    // 指定ContentState关联类型为TimerStatus
    public typealias ContentState = TimerStatus

    // 静态属性
    var name: String
}

2. 修复Live Activity启动方法的类型错误

原代码中类型不匹配(TimerActivityAttributes应为TimeActivityAttributes),且本地场景无需pushType: .token(该参数用于远程推送更新Live Activity):

var activity: Activity<TimeActivityAttributes>? = nil

func startActivity(withEndTime endTime: Date) {
    let attributes = TimeActivityAttributes(name: "Timer")
    let state = TimeActivityAttributes.TimerStatus(endTime: endTime)
    
    // 本地启动无需token,pushType设为nil即可
    activity = try? Activity<TimeActivityAttributes>.request(attributes: attributes, contentState: state, pushType: nil)
}

二、调整本地通知配置(触发Live Activity+仅提示音)

1. 修正重复添加通知的错误

原调度代码中重复调用center.add(request),删除冗余逻辑:

let numberOfNotifications = 24
let intervalBetweenNotifications = timeRange / TimeInterval(numberOfNotifications)

for i in 1...numberOfNotifications {
    let randomTimeInterval = TimeInterval.random(in: 0...intervalBetweenNotifications)
    let notificationDate = startDate.addingTimeInterval(TimeInterval(i) * intervalBetweenNotifications + randomTimeInterval)

    let dateComponents = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: notificationDate)
    let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)

    // 配置通知:仅播放提示音,不显示弹窗
    let content = UNMutableNotificationContent()
    content.sound = UNNotificationSound.default // 提示音
    content.userInfo = ["notificationFiredAt": notificationDate] // 保留触发时间用于后续校验

    let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)

    center.add(request) { error in
        if let error = error {
            debugPrint(error)
        }
    }
}

2. 通知触发时启动Live Activity

实现UNUserNotificationCenterDelegate的willPresent方法,在通知触发时启动Live Activity并仅播放提示音:

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    // 仅播放提示音,不显示通知弹窗
    completionHandler([.sound])
    
    // 启动1分钟倒计时的Live Activity
    let endTime = Date().addingTimeInterval(60)
    startActivity(withEndTime: endTime)
}

三、处理Live Activity的交互与超时逻辑

1. 点击Live Activity触发Success

在Live Activity的UI视图中添加点击手势,点击后结束Activity并打印结果:

// 在Live Activity的View结构体中添加
struct TimeActivityView: View {
    let attributes: TimeActivityAttributes
    @ActivityState var state: TimeActivityAttributes.TimerStatus
    
    var body: some View {
        let timeRemaining = max(state.endTime.timeIntervalSinceNow, 0)
        Text("剩余时间: \(Int(timeRemaining))秒")
            .onTapGesture {
                // 结束Activity并打印Success
                Task {
                    await Activity<TimeActivityAttributes>.activities.first?.end(dismissalPolicy: .immediate)
                    print("Success")
                }
            }
    }
}

2. 超时自动触发Failed

在Live Activity的更新逻辑中,监听endTime,当时间到达时自动结束Activity并打印结果:

// 在Live Activity的View结构体中
struct TimeActivityView: View {
    let attributes: TimeActivityAttributes
    @ActivityState var state: TimeActivityAttributes.TimerStatus
    
    var body: some View {
        let timeRemaining = max(state.endTime.timeIntervalSinceNow, 0)
        Text("剩余时间: \(Int(timeRemaining))秒")
            .onChange(of: timeRemaining) { newValue in
                if newValue <= 0 {
                    // 超时,结束Activity并打印Failed
                    Task {
                        await Activity<TimeActivityAttributes>.activities.first?.end(dismissalPolicy: .immediate)
                        print("Failed")
                    }
                }
            }
    }
}

关键说明

  • pushType: .token仅用于远程推送更新Live Activity,本地场景无需配置,设为nil即可正常启动
  • 若需确保Live Activity在后台也能正常显示,需在Info.plist中添加NSSupportsLiveActivities并设为YES
  • 重复触发通知时,可先检查是否已有活跃的Live Activity,避免重复启动(可在startActivity方法中添加判断)

内容的提问来源于stack exchange,提问作者Mel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 01:48:27