完整代码示例
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
}
}
关键功能说明
-
权限请求
- 通过独立按钮触发,避免应用启动时强制请求
- 处理授权结果并通过 Alert 反馈
-
通知类型
- 立即通知:5 秒后触发的单次通知
- 每日通知:每天上午 10 点触发(需真机测试)
- 自定义声音:使用项目中的
chime.mp3
文件(需添加到 Xcode)
-
用户交互
- 点击通知时解析
userInfo
中的深度链接 - 应用在前台时仍显示通知横幅
- 点击通知时解析
-
管理通知
- 通过唯一
identifier
支持后续取消 - 一键取消所有计划中的通知
- 通过唯一
使用说明
-
添加自定义声音
- 将音频文件(如
chime.mp3
)拖入 Xcode 项目 - 确保 Target Membership 勾选正确
- 将音频文件(如
-
测试场景
- 模拟器测试:5 秒通知可直接测试
- 真机测试:修改系统时间测试每日通知
-
查看通知计划
UNUserNotificationCenter.current().getPendingNotificationRequests { requests in print("已计划的通知数量: \(requests.count)") }
注意事项
-
权限处理
- 首次触发通知前必须请求用户授权
- 用户拒绝后引导前往系统设置开启:
if let url = URL(string: UIApplication.openSettingsURLString) { UIApplication.shared.open(url) }
-
角标管理
- 示例中在应用启动时自动重置角标为 0
- 可通过
UIApplication.shared.applicationIconBadgeNumber
动态修改
-
自定义声音限制
- 文件格式需为
.aiff
,.wav
,.caf
- 文件需直接放在项目根目录,不要放在子文件夹中
- 文件格式需为
此示例可直接运行,覆盖了本地通知的核心场景,适合作为功能模板集成到实际项目中。