Push处理有以下场景
- App在前台接收到通知
- App在后台接收到通知
- 用户通过点击通知调起App
设置
- 由于纯SwiftUI App 的入口是一个struct,而我们需要使用的
UserNotifications是一个OC框架,所以我们需要创建一个类去遵循UIApplicationDelegate
import UIKit
import UserNotifications
class MyAppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
) -> Bool {
// 注册通知代理处理即将到来的通知
UNUserNotificationCenter.current().delegate = self
// 请求通知权限
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current()
.requestAuthorization(
options: authOptions,
completionHandler: { success, error in
// 处理用户授权的状态
if success {
print("用户已经授权")
} else {
// 这里需要根据错误重新提示用户授权或做一些其他处理。目前只是打印错误
print(error?.localizedDescription as Any)
}
}
)
return true
}
}
// MARK: - UNUserNotificationCenterDelegate
extension MyAppDelegate: UNUserNotificationCenterDelegate {
// 如果App在前台接收到通知
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
return [.list, .banner, .sound]
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse) async {
// 处理接收到的通知
}
}
- 需要将代理放置到我们的SwiftUI中,这可以通过使用
@UIApplicationDelegateAdaptor完成
// MARK: - AppDelegate
/// 添加代理
@UIApplicationDelegateAdaptor var appDelegate: MyAppDelegate
// MARK: - Body
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(sharedModelContainer)
}
给App添加Push能力
在Target的 "Signing & Capabilities" 中点击 "+ Capability" 然后输入push就可以
通知处理
在SwiftUI中已经有onOpenURL(perform:)和 .onContinueUserActivity(_:perform:)这类的ViewModifier,我们需要尽量适配这些ViewModifier,可以自定义一个ViewModifier,它的签名是onNotification(perform closure: @escaping (UNNotificationResponse) -> Void
为了处理通知,我们需要处理以下事情
接收到来的通知
通过设计一个类来专门处理通知,利用Combine框架完成订阅与通知逻辑
import Combine
import UserNotifications
public class NotificationHandler: ObservableObject {
// MARK: - Shared Instance
public static let shared = NotificationHandler()
private init() {}
// MARK: - Properties
/// 最新的通知
@Published private(set) var latestNotification: UNNotificationResponse? = .none
// MARK: - Methods
/// 处理通知并转发到App
/// - Parameter notification: 需要处理的通知响应
public func handle(notification: UNNotificationResponse) {
// 持有响应并通过Combine发布出去
self.latestNotification = notification
}
}
之后我们就可以在之前的通知代理里面使用该类处理通知
// MARK: - UNUserNotificationCenterDelegate
extension MyAppDelegate: UNUserNotificationCenterDelegate {
// 如果App在前台接收到通知
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
return [.list, .banner, .sound]
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse) async {
// 处理接收到的通知
NotificationHandler.shared.handle(notification: response)
}
}
通知到SwiftUI的View
SwiftUI中大多数的事件和UI控制都是通过ViewModifier来处理的,我们要创建一个ViewModifier来贴合SwiftUI的风格
import SwiftUI
typealias NotificationResponseAction = (UNNotificationResponse) -> Void
struct NotificationViewModifier: ViewModifier {
// MARK: - Private Properties
private let onNotification: NotificationResponseAction
// MARK: - Initializers
// 提供一个闭包,可以让调用者处理自己的通知逻辑
init(onNotification: @escaping NotificationResponseAction) {
self.onNotification = onNotification
}
// MARK: - Body
func body(content: Content) -> some View {
content
// 订阅通知更新
.onReceive(NotificationHandler.shared.$latestNotification) { notification in
guard let notification else { return }
onNotification(notification)
}
}
}
extension View {
func onNotification(perform action: @escaping NotificationResponseAction) -> some View {
modifier(NotificationViewModifier(onNotification: action))
}
}
我们创建了一个ViewModifier并提供了一个便利的使用方法
之后我们就可以在View上使用这个ViewModifier了
// MARK: - Body
var body: some Scene {
WindowGroup {
ContentView()
.onNotification { notification in
print(notification)
}
}
.modelContainer(sharedModelContainer)
}
保证我们在App前台、后台、以及通过通知启动App时都能调用到
- 由于
NotificationHandler良好的设计,在用户通过通知启动App,handler就已经处理通知,并保存了latestNotification。 - 此时SwiftUI视图还未创建,等View创建时通过onNotification订阅通知时就能接收到latestNotification这个值了