This is the fifth day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021.

The introduction

IOS15 introduces a new property for push notifications, interruptionLevel, with enumerated values

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));


Copy the code
  • Passive notification does not light up the phone screen and does not play sound.
  • Active: The default notification type is Active, which lights up the screen and plays a sound.
  • Time Sensitive: Lights up and plays sound. May be displayed in do not Disturb mode (focus mode).
  • Critical: Displays immediately, lights up, plays sound, invalid DND mode, bypassing mute, and uses a default sound if no sound is set.

Therefore, when our message push is relatively important, such as the notification of receipt to account, we can use the extension of message push to change the interruption level of message push to timeliness, so that the screen will light up and sound will play when the mobile phone receives it. It is displayed even in do not Disturb mode (focus mode).

We can also use Notification Service Extension to modify the push sounds field to broadcast customized sounds.

I Service Extension development steps

Implementation: Service Extension and local notification.

The addition of Service Extension in iOS 10 means that APNs will be processed by a layer of Extension Service that allows users to set up their own Settings before they reach our devices, adding variety to APNs.

In this paper, Service Extension is used to process messages and broadcast language, to solve the problem that the iOS12.1 system can not broadcast voice in the background or killed

If the minimum supported version of the main project Target is 10.0, set the extended Target system version to 10.0.

If the minimum supported version of the main project Target is greater than 10.0, the extended Target system version is the same as the main project Target version.

The mutation-content field in the content of the notification must be 1

The demo download: https://download.csdn.net/download/u011018979/14026303

1.1 create NotificationServiceExtension

  • Create a Notification Service Extension

Note:

The Bundle Identifier of the Service Extension must not be the same as the Bundle Identifier of the Main Target (i.e. your App Target).

The Bundle Identifier of the Service Extension must be in the namespace of the Main Target. For example, the BundleID of the Main Target is io.re.xxx. The Service Extension BundleID should look like io.re.xxx.yyy.

  • Create a NotificationService. M to UNNotificationServiceExtension and implementation method- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler

After the Service Extension has been successfully created, your project contains two methods.

1.didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent *contentToDeliver))contentHandler

This method is called when the APNs arrives, at which point you can process the pushed content and use contentHandler to do the processing. But if the time is too long, the APNs will be displayed as is. That is, we can process our notifications in this method and personalize them to the user.

2, serviceExtensionTimeWillExpire serviceExtensionTimeWillExpire method, before the expiration of the callback, as you can for your APNs news for emergency treatment.

  • NotificationService.m
@interface NotificationService(a)

@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 This method is called when service's time Expires then the unmodified notification will be delivered APNs arrives, and you can do something about it. Then use contentHandler to complete the process. But if the time is too long, the APNs will be displayed as is. That is, we can process our notifications in this method and personalize them to the user. * /
- (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;
#warningIf the system is greater than 12.0, use the voice packet synthesis file broadcast method
    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); }}/ * * and serviceExtensionTimeWillExpire method, before the expiration of the callback, as you can for your APNs news for emergency treatment. * /
- (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);
}


Copy the code

1.2 create AudioTool

In iOS12.1, AVAudioPlayer is used for voice broadcasting. IOS12.1 – iOS14 supports local notification for voice broadcasting. IOS15 modifies the “sounds” field to broadcast customized sounds.

Play audio files in the background or locked screen

AVAudio Session need to use AVAudioSessionCategoryPlayback or AVAudioSessionCategoryPlayAndRecord Category

            [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:NULL];
            [[AVAudioSession sharedInstance] setActive:YES error:NULL];
            
            [self playAudioFiles];

Copy the code

CategoryOptions Select this parameter based on site requirements

MixWithOthers (Mix with other sounds) DuckOthers (Turn down the volume of other sounds)

1.3 Configuration Items

  • Integrated JPush
  pod 'JPush'

Copy the code

If you encounter this problem:

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

Copy the code

Just add the official source

source 'https://github.com/CocoaPods/Specs.git'

Copy the code
  • Add Push Notification and background modes

  • Prepare the Resource file Resource

1.4 Registration push

registerJPUSH

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    
    if (yjIOS10) {
        // Notify authorization
        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        center.delegate = self;
        [center requestAuthorizationWithOptions:UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) {
            if (granted) {
                // Click Permit
                [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
                    NSLog(@"yangjing_%@: settings->%@".NSStringFromClass([self class]),settings);
                }];
            } else {
                // Click not allowed}}]; [[UIApplication sharedApplication] registerForRemoteNotifications];
        
    } else {
        IOS8 -iOS10 register remote notification method
        UIUserNotificationType types = UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound;
        UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
        [[UIApplication sharedApplication] registerUserNotificationSettings:mySettings];
        [[UIApplication sharedApplication] registerForRemoteNotifications];
    }
    
    // Initialize JPushSDK
    [[JPushTool shareTool] registerJPUSH:launchOptions];
    

    
    return YES;
}


Copy the code
  • Notification Event Handling

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated  later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    
    [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
    [[UIApplication sharedApplication] cancelAllLocalNotifications];
    
    [[JPushTool shareTool] setBadge:0];
}

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(nonnull UIUserNotificationSettings *)notificationSettings {
    // register to receive notifications
    
    [application registerForRemoteNotifications];
}

// Remote push registration succeeded
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    NSLog(@"zkn%@: deviceToken->%@".NSStringFromClass([self class]), [deviceToken description]);
    [[JPushTool shareTool] registerForRemoteNotificationsWithDeviceToken:deviceToken];
}

// Remote push registration failed
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    
}

// Receive remote push before ios10
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    NSLog(@"yangjing_%@: userInfo->%@ ".NSStringFromClass([self class]), userInfo);
    
    [[KNAudioTool sharedPlayer] playPushInfo:userInfo backModes:NO completed:nil];
}

// Receive local push before ios10
- (void)application:(UIApplication *)app didReceiveLocalNotification:(UILocalNotification *)notif {
}

// Receive push after ios10
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler  API_AVAILABLE(ios(10.0)) {NSDictionary * userInfo = notification.request.content.userInfo;
    
    // Remote push
    if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        NSLog(@"%@: userInfo->%@ ".NSStringFromClass([self class]), userInfo);
        
        // NotificationService is not handled
        if(! [userInfo.allKeys containsObject:@"hasHandled"]) {
            if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
                [[KNAudioTool sharedPlayer] playPushInfo:userInfo backModes:NO completed:nil];
                completionHandler(UNNotificationPresentationOptionAlert);
                
            } else {
                completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionAlert|UNNotificationPresentationOptionSound);

            }
            
        } else {
            if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
                completionHandler(UNNotificationPresentationOptionAlert);
                
            } else{ completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionAlert); }}}// Remote push
    else {
        completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionAlert|UNAuthorizationOptionSound);

    }
}

// Click events for iOS10 and above notifications
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^) (void))completionHandler  API_AVAILABLE(ios(10.0)) {
    completionHandler();  // The system requires this method to be executed
}

Copy the code

see also

For more, check out # Applets: iOS Reverse, which presents valuable information only for you, focusing on the mobile technology research field.