RxSwift扩展UNUserNotificationCenter遇类型转换崩溃,求前台本地通知方案
这个问题我之前踩过一模一样的坑!本质是Objective-C Block和Swift闭包之间的桥接类型不匹配导致的——你看到的NSStackBlock转(__C.UNNotificationPresentationOptions) -> ()失败,就是因为代理方法里的completion handler在ObjC里是block类型,直接强制转换时签名没对齐。
要实现前台显示本地通知的需求,我们需要正确处理userNotificationCenter(_:willPresent:withCompletionHandler:)这个代理方法的响应式包装,下面是完整的解决方案:
1. 正确扩展UNUserNotificationCenter的Rx响应式接口
首先我们给UNUserNotificationCenter添加Rx扩展,通过methodInvoked监听代理方法,并且正确转换completion handler的类型:
import RxSwift import RxCocoa import UserNotifications extension Reactive where Base: UNUserNotificationCenter { /// 监听前台收到通知的事件,返回通知对象和completion闭包 var willPresentNotification: Observable<(UNNotification, (UNNotificationPresentationOptions) -> Void)> { return methodInvoked(#selector(UNUserNotificationCenterDelegate.userNotificationCenter(_:willPresent:withCompletionHandler:))) .map { parameters in // 确保参数类型转换完全匹配 let notification = parameters[1] as! UNNotification // 这里的类型转换必须严格对应ObjC block的签名 let completionHandler = parameters[2] as! (UNNotificationPresentationOptions) -> Void return (notification, completionHandler) } } }
2. 订阅事件并处理前台通知展示
接下来在你的业务代码里订阅这个Observable,调用completion闭包并传入需要的展示选项(比如.alert、.sound,这样通知就能在App前台显示了):
// 假设你有一个全局的disposeBag let disposeBag = DisposeBag() // 订阅前台通知事件 UNUserNotificationCenter.current().rx.willPresentNotification .subscribe(onNext: { notification, completion in // 这里可以自定义处理通知内容,比如打印日志、更新UI print("前台收到通知:\(notification.request.content.title)") // 传入展示选项,让通知在前台显示 completion([.alert, .sound, .badge]) }) .disposed(by: disposeBag)
3. 别忘了设置代理
RxSwift的响应式扩展依赖于代理的转发,所以需要确保UNUserNotificationCenter的delegate被正确设置。你可以直接使用RxCocoa提供的RxUserNotificationCenterDelegateProxy:
let delegateProxy = RxUserNotificationCenterDelegateProxy() UNUserNotificationCenter.current().delegate = delegateProxy
如果是自定义代理类,也要确保它能把代理方法调用转发到Rx的流中。
为什么之前会崩溃?
你遇到的崩溃是因为直接把ObjC的NSStackBlock强制转换成了不匹配的Swift闭包类型。userNotificationCenter(_:willPresent:withCompletionHandler:)的completion handler在ObjC中的签名是void (^)(UNNotificationPresentationOptions options),对应的Swift类型必须严格是(UNNotificationPresentationOptions) -> Void,不能有任何参数或返回值的差异——一旦类型不匹配,就会出现转换失败的崩溃。上面的代码通过methodInvoked拿到参数后,做了严格的类型转换,就避免了这个问题。
内容的提问来源于stack exchange,提问作者Dmitriy Ivashin

