4.4 在 SwiftUI 中触发通知的完整代码示例

完整代码示例

import SwiftUI
import UserNotifications

// MARK: - App 入口与通知代理
@main
struct NotificationDemoApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onAppear {
                    // 应用启动时重置角标
                    UIApplication.shared.applicationIconBadgeNumber = 0
                }
        }
    }
}

// 处理通知代理方法(前台显示、用户交互)
class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        UNUserNotificationCenter.current().delegate = self
        return true
    }
    
    // 应用在前台时显示通知
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
        return [.banner, .sound, .badge]
    }
    
    // 处理用户点击通知
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {
        let userInfo = response.notification.request.content.userInfo
        if let deepLink = userInfo["deepLink"] as? String {
            print("用户点击通知,深度链接: \(deepLink)")
        }
    }
}

// MARK: - SwiftUI 视图
struct ContentView: View {
    @State private var showAlert = false
    @State private var alertMessage = ""
    
    var body: some View {
        VStack(spacing: 20) {
            // 请求权限按钮
            Button("请求通知权限") {
                requestNotificationPermission()
            }
            
            // 触发 5 秒后通知
            Button("触发 5 秒后通知") {
                scheduleNotification(
                    title: "倒计时结束",
                    body: "您设置的提醒已触发!",
                    timeInterval: 5,
                    repeats: false
                )
            }
            
            // 触发每日 10 点通知
            Button("触发每日 10 点通知") {
                scheduleDailyNotification()
            }
            
            // 取消所有通知
            Button("取消所有通知") {
                UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
                showAlert(message: "已取消所有计划的通知")
            }
        }
        .buttonStyle(.borderedProminent)
        .alert("提示", isPresented: $showAlert) {
            Button("确定") { }
        } message: {
            Text(alertMessage)
        }
    }
    
    // MARK: - 核心方法
    // 请求通知权限
    private func requestNotificationPermission() {
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
            DispatchQueue.main.async {
                if granted {
                    showAlert(message: "通知权限已授权 🎉")
                } else {
                    showAlert(message: error?.localizedDescription ?? "用户拒绝了通知权限")
                }
            }
        }
    }
    
    // 调度基于时间的通知
    private func scheduleNotification(title: String, body: String, timeInterval: TimeInterval, repeats: Bool) {
        let content = UNMutableNotificationContent()
        content.title = title
        content.body = body
        content.sound = .default
        content.badge = 1
        content.userInfo = ["deepLink": "myapp://reminder/\(Date())"] // 自定义数据
        
        let trigger = UNTimeIntervalNotificationTrigger(
            timeInterval: timeInterval,
            repeats: repeats
        )
        
        let request = UNNotificationRequest(
            identifier: UUID().uuidString, // 唯一标识符
            content: content,
            trigger: trigger
        )
        
        UNUserNotificationCenter.current().add(request) { error in
            DispatchQueue.main.async {
                if let error = error {
                    showAlert(message: "通知添加失败: \(error.localizedDescription)")
                } else {
                    showAlert(message: "通知已计划 ✅")
                }
            }
        }
    }
    
    // 调度每日通知
    private func scheduleDailyNotification() {
        let content = UNMutableNotificationContent()
        content.title = "每日提醒"
        content.body = "现在是上午 10 点!"
        content.sound = UNNotificationSound(named: UNNotificationSoundName("chime.mp3")) // 自定义声音
        content.badge = 1
        
        var components = DateComponents()
        components.hour = 10
        components.minute = 0
        
        let trigger = UNCalendarNotificationTrigger(
            dateMatching: components,
            repeats: true
        )
        
        let request = UNNotificationRequest(
            identifier: "daily_10am_reminder",
            content: content,
            trigger: trigger
        )
        
        UNUserNotificationCenter.current().add(request) { error in
            DispatchQueue.main.async {
                if let error = error {
                    showAlert(message: "每日通知添加失败: \(error.localizedDescription)")
                } else {
                    showAlert(message: "每日通知已生效 ⏰")
                }
            }
        }
    }
    
    // 显示提示
    private func showAlert(message: String) {
        alertMessage = message
        showAlert = true
    }
}

关键功能说明

  1. 权限请求

    • 通过独立按钮触发,避免应用启动时强制请求
    • 处理授权结果并通过 Alert 反馈
  2. 通知类型

    • 立即通知:5 秒后触发的单次通知
    • 每日通知:每天上午 10 点触发(需真机测试)
    • 自定义声音:使用项目中的 chime.mp3 文件(需添加到 Xcode)
  3. 用户交互

    • 点击通知时解析 userInfo 中的深度链接
    • 应用在前台时仍显示通知横幅
  4. 管理通知

    • 通过唯一 identifier 支持后续取消
    • 一键取消所有计划中的通知

使用说明

  1. 添加自定义声音

    • 将音频文件(如 chime.mp3)拖入 Xcode 项目
    • 确保 Target Membership 勾选正确
  2. 测试场景

    • 模拟器测试:5 秒通知可直接测试
    • 真机测试:修改系统时间测试每日通知
  3. 查看通知计划

    UNUserNotificationCenter.current().getPendingNotificationRequests { requests in
        print("已计划的通知数量: \(requests.count)")
    }
    

注意事项

  1. 权限处理

    • 首次触发通知前必须请求用户授权
    • 用户拒绝后引导前往系统设置开启:
      if let url = URL(string: UIApplication.openSettingsURLString) {
          UIApplication.shared.open(url)
      }
      
  2. 角标管理

    • 示例中在应用启动时自动重置角标为 0
    • 可通过 UIApplication.shared.applicationIconBadgeNumber 动态修改
  3. 自定义声音限制

    • 文件格式需为 .aiff, .wav, .caf
    • 文件需直接放在项目根目录,不要放在子文件夹中

此示例可直接运行,覆盖了本地通知的核心场景,适合作为功能模板集成到实际项目中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值