引言
iOS15引入了消息推送的新属性中断级别interruptionLevel,具体的枚举值
typedef NS_ENUM(NSUInteger, UNNotificationInterruptionLevel) { // Added to the notification list; does not light up screen or play sound UNNotificationInterruptionLevelPassive, // Presented immediately; Lights up screen and may play a sound UNNotificationInterruptionLevelActive, // Presented immediately; Lights up screen and may play a sound; May be presented during Do Not Disturb UNNotificationInterruptionLevelTimeSensitive, // Presented immediately; Lights up screen and plays sound; Always presented during Do Not Disturb; Bypasses mute switch; Includes default critical alert sound if no sound provided UNNotificationInterruptionLevelCritical, } API_AVAILABLE(macos(12.0), ios(15.0), watchos(8.0), tvos(15.0));
- Passive:被动类型的通知不会使手机亮屏并且不会播放声音。
- Active: 活动类型的通知会使手机亮屏且会播放声音,为默认类型。
- Time Sensitive(时效性):会使手机亮屏且会播放声音;可能会在免打扰模式(焦点模式)下展示。
- Critical(关键):会立刻展示,亮屏,播放声音,无效免打扰模式,并且能够绕过静音,如果没有设置声音则会使用一种默认的声音。
因此当我们的消息推送比较重要的时候,比如收款到账的通知,可以利用消息推送扩展来修改消息推送的中断级别为时效性,这样手机接收的时候会亮屏且会播放声音;即使在免打扰模式(焦点模式)下也会展示。
我们也可以通过Notification Service Extension修改推送sounds字段来播报自定义的语音。
I Service Extension开发步骤
实现方式:采用Service Extension并结合本地通知进行实现。
iOS 10新增了Service Extension,这意味着在APNs到达我们的设备之前,还会经过一层允许用户自主设置的Extension服务进行处理,为APNs增加了多样性。
本文就是利用Service Extension处理消息并语言播报,来解决iOS12.1系统以上在后台或者被杀死无法语音播报的问题
若主工程 Target 最低支持版本小于10.0,扩展 Target 系统版本设置为10.0。
若主工程 Target 最低支持版本大于10.0,则扩展 Target 系统版本与主工程 Target 版本一致。
通知的内容中 mutable-content 字段必须为1
demo下载:https://download.csdn.net/download/u011018979/14026303
1.1 创建NotificationServiceExtension
- 新建Notification Service Extension
注意:
1、Service Extension的Bundle Identifier不能和Main Target(也就是你自己的App Target)的Bundle Identifier相同,否则会报BundeID重复的错误。
2、Service Extension的Bundle Identifier需要在Main Target的命名空间下,比如说Main Target的BundleID为io.re.xxx,那么Service Extension的BundleID应该类似与io.re.xxx.yyy这样的格式。
- 创建NotificationService.m继承UNNotificationServiceExtension ,并实现方法
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler
Service Extension服务已经创建成功之后,你的项目中包含两个方法。
1、
didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent *contentToDeliver))contentHandler
APNs到来的时候会调用这个方法,此时你可以对推送过来的内容进行处理,然后使用contentHandler完成这次处理。但是如果时间太长了,APNs就会原样显示出来。也就是说,我们可以在这个方法中处理我们的通知,个性化展示给用户。2、
serviceExtensionTimeWillExpire
而serviceExtensionTimeWillExpire方法,会在过期之前进行回调,此时你可以对你的APNs消息进行一下紧急处理。
- NotificationService.m
@interface NotificationService () @property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); @property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; @end @implementation NotificationService /** Call contentHandler with the modified notification content to deliver. If the handler is not called before the service's time expires then the unmodified notification will be delivered APNs到来的时候会调用这个方法,此时你可以对推送过来的内容进行处理,然后使用contentHandler完成这次处理。但是如果时间太长了,APNs就会原样显示出来。 也就是说,我们可以在这个方法中处理我们的通知,个性化展示给用户。 */ - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; NSLog(@"NotificationService_%@: dict->%@", NSStringFromClass([self class]), self.bestAttemptContent.userInfo); self.bestAttemptContent.sound = nil; #warning 如果系统大于12.0就走语音包合成文件播报方法 if (yjIOS10) { __weak typeof(self) weakSelf = self; [[KNAudioTool sharedPlayer] playPushInfo:weakSelf.bestAttemptContent.userInfo backModes:YES completed:^(BOOL success) { __strong typeof(weakSelf) strongSelf = weakSelf; if (strongSelf) { NSMutableDictionary *dict = [strongSelf.bestAttemptContent.userInfo mutableCopy] ; [dict setObject:[NSNumber numberWithBool:YES] forKey:@"hasHandled"] ; strongSelf.bestAttemptContent.userInfo = dict; strongSelf.contentHandler(self.bestAttemptContent); } }]; } else { self.contentHandler(self.bestAttemptContent); } } /** 而serviceExtensionTimeWillExpire方法,会在过期之前进行回调,此时你可以对你的APNs消息进行一下紧急处理。 */ - (void)serviceExtensionTimeWillExpire { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. self.contentHandler(self.bestAttemptContent); }
1.2 创建 AudioTool
iOS12.1 以下使用AVAudioPlayer进行语音播报。iOS12.1 - iOS14 可以使用本地通知进行语音播报。iOS15 通过修改推送sounds字段来播报自定义的语音。
后台或者锁屏状态下播放音频文件
AVAudio Session的Category值需要使用
AVAudioSessionCategoryPlayback
或AVAudioSessionCategoryPlayAndRecord
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:NULL]; [[AVAudioSession sharedInstance] setActive:YES error:NULL]; [self playAudioFiles];
CategoryOptions根据实际需要可选择
MixWithOthers(与其他声音混音) DuckOthers(调低其他声音的音量)
1.3 配置项目
- 集成JPush
pod 'JPush'
如果遇到这个问题:
CDN: trunk URL couldn't be downloaded: https://raw.githubusercontent.com/CocoaPods/Specs/master/Specs/b/0/d/JPush/3.3.3/JPush.podspec.json Response: Couldn't connect to server
添加一下官方source即可
source 'https://github.com/CocoaPods/Specs.git'
- 添加 push notification 及background modes
准备资源文件Resource
1.4、注册推送
registerJPUSH
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. if (yjIOS10) { //通知授权 UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; center.delegate = self; [center requestAuthorizationWithOptions:UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) { if (granted) { // 点击允许 [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { NSLog(@"yangjing_%@: settings->%@", NSStringFromClass([self class]),settings); }]; } else { // 点击不允许 } }]; [[UIApplication sharedApplication] registerForRemoteNotifications]; } else { // iOS8-iOS10注册远程通知的方法 UIUserNotificationType types = UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound; UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil]; [[UIApplication sharedApplication] registerUserNotificationSettings:mySettings]; [[UIApplication sharedApplication] registerForRemoteNotifications]; } //初始化JPushSDK [[JPushTool shareTool] registerJPUSH:launchOptions]; return YES; }
- 通知事件处理