在 Day 21,我們學會了如何 發送本地通知(Local Notification),並設定前景通知顯示。
今天要進一步延伸介紹:
透過這篇文章,讓通知系統將不只是提醒,更能互動與導向 App 內特定頁面。
當使用者點擊通知時,系統會將事件回傳給 App,我們可以在 UNUserNotificationCenterDelegate
的方法中處理:
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void)
response.notification.request.identifier
得知是哪一個通知import SwiftUI
import UserNotifications
class NotificationManager: NSObject, ObservableObject, UNUserNotificationCenterDelegate {
@Published var navigateToLearningPage = false
override init() {
super.init()
UNUserNotificationCenter.current().delegate = self
requestAuthorization()
}
private func requestAuthorization() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
print(granted ? "使用者允許通知" : "使用者拒絕通知")
}
}
func sendLocalNotification() {
let content = UNMutableNotificationContent()
content.title = "學習提醒"
content.body = "該回來繼續學 Swift 鐵人賽囉!"
content.sound = .default
let request = UNNotificationRequest(
identifier: "open_learning",
content: content,
trigger: UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
)
UNUserNotificationCenter.current().add(request)
}
// 前景顯示通知
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.banner, .sound])
}
// 點擊通知事件
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let id = response.notification.request.identifier
print("使用者點擊通知:\(id)")
if id == "open_learning" {
DispatchQueue.main.async {
self.navigateToLearningPage = true
}
}
completionHandler()
}
}
struct ContentView: View {
@StateObject private var notificationManager = NotificationManager()
var body: some View {
NavigationStack {
VStack(spacing: 20) {
Text("Swift 鐵人賽 Day 21")
.font(.title2)
Button("發送學習提醒通知") {
notificationManager.sendLocalNotification()
}
.buttonStyle(.borderedProminent)
}
.navigationDestination(isPresented: $notificationManager.navigateToLearningPage) {
LearningView()
}
}
}
}
struct LearningView: View {
var body: some View {
VStack(spacing: 16) {
Text("📖 學習進度頁面")
.font(.title)
Text("歡迎回來,繼續挑戰 Swift 鐵人賽吧!")
}
.padding()
}
}
通知分類(UNNotificationCategory
):
UNNotificationAction
)互動按鈕(UNNotificationAction
):
這樣可以讓 App 的通知系統更有彈性與互動性。
import SwiftUI
import UserNotifications
class NotificationManager: NSObject, ObservableObject, UNUserNotificationCenterDelegate {
@Published var navigateToLearningPage = false
override init() {
super.init()
UNUserNotificationCenter.current().delegate = self
requestAuthorization()
setupNotificationActions()
}
private func requestAuthorization() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
print(granted ? "使用者允許通知" : "使用者拒絕通知")
}
}
func sendLocalNotification() {
let content = UNMutableNotificationContent()
content.title = "學習提醒"
content.body = "該回來繼續學 Swift 鐵人賽囉!"
content.sound = .default
let request = UNNotificationRequest(
identifier: "open_learning",
content: content,
trigger: UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
)
UNUserNotificationCenter.current().add(request)
}
func setupNotificationActions() {
let markDoneAction = UNNotificationAction(
identifier: "MARK_DONE",
title: "完成",
options: [.authenticationRequired]
)
let remindLaterAction = UNNotificationAction(
identifier: "REMIND_LATER",
title: "稍後提醒",
options: []
)
let taskCategory = UNNotificationCategory(
identifier: "TASK_CATEGORY",
actions: [markDoneAction, remindLaterAction],
intentIdentifiers: [],
options: []
)
UNUserNotificationCenter.current().setNotificationCategories([taskCategory])
}
func sendInteractiveNotification() {
let content = UNMutableNotificationContent()
content.title = "待辦任務"
content.body = "今天的 Swift 鐵人賽任務還沒完成!"
content.sound = .default
content.categoryIdentifier = "TASK_CATEGORY" // 綁定分類
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
let request = UNNotificationRequest(identifier: "TASK_NOTIFICATION", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)
}
// 前景顯示通知
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.banner, .sound])
}
// 點擊通知事件
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let actionId = response.actionIdentifier
let notificationId = response.notification.request.identifier
print("使用者點擊通知:\(notificationId), 按鈕:\(actionId)")
switch actionId {
case "MARK_DONE":
print("任務已完成")
case "REMIND_LATER":
print("稍後提醒")
case UNNotificationDefaultActionIdentifier:
print("使用者點擊通知本身")
default:
break
}
completionHandler()
}
}
struct ContentView: View {
@StateObject private var notificationManager = NotificationManager()
var body: some View {
NavigationStack {
VStack(spacing: 20) {
Text("Swift 鐵人賽 Day 21")
.font(.title2)
Button("發送學習提醒通知") {
notificationManager.sendLocalNotification()
}
.buttonStyle(.borderedProminent)
Button("發送分類通知") {
notificationManager.sendInteractiveNotification()
}
.buttonStyle(.borderedProminent)
}
.navigationDestination(isPresented: $notificationManager.navigateToLearningPage) {
LearningView()
}
}
}
}
struct LearningView: View {
var body: some View {
VStack(spacing: 16) {
Text("學習進度頁面")
.font(.title)
Text("歡迎回來,繼續挑戰 Swift 鐵人賽吧!")
}
.padding()
}
}
今天我們學到: