iOS开发-NotificationServiceExtension实现实时音视频呼叫通知响铃与震动

iOS开发-NotificationServiceExtension实现实时音视频呼叫通知响铃与震动

在之前的开发中,遇到了实时音视频呼叫通知,当App未打开或者App在后台时候,需要通知到用户,用户点击通知栏后是否接入实时音视频的视频或者音频通话。

在iOS需要为工程新建Target:NotificationServiceExtension

一、主工程配置

需要在主工程中开启推送功能,配置证书。

二、新建NotificationServiceExtension的target

新建NotificationServiceExtension的target

三、实现NotificationService

在NotificationService的方法

objectivec 复制代码
// 主要代码
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler


- (void)serviceExtensionTimeWillExpire;

在didReceiveNotificationRequest实现呼叫响铃及震动效果。

objectivec 复制代码
// 通过通知的Sound设置为voip_call.caf,这里播放一段空白音频,音频结束后结束震动
                NSString *path = [[NSBundle mainBundle] pathForResource:@"blank_call.mp3" ofType:nil];
                AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:path], &soundID);

                AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, soundCompleteCallback, NULL);
                AudioServicesAddSystemSoundCompletion(kSystemSoundID_Vibrate, NULL, NULL, soundCompleteCallback, NULL);

完整代码如下

objectivec 复制代码
#import "NotificationService.h"
#import <AVFoundation/AVFAudio.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>

@interface NotificationService ()
{
    SystemSoundID soundID;
}

@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;

@property (nonatomic, strong) NSMutableArray *requests;

@end

@implementation NotificationService

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];

    BOOL playVoice = NO;
    NSString *chatPushVo = [self.bestAttemptContent.userInfo objectForKey:@"chatPushVo"];
    if (chatPushVo && [chatPushVo isKindOfClass:[NSString class]] && chatPushVo > 0) {
        NSDictionary *dict = [self dictionaryWithJsonString:chatPushVo];
        if (dict && [dict isKindOfClass:[NSDictionary class]]) {
            NSString *type = [NSString stringWithFormat:@"%@", [dict objectForKey:@"type"]];
            // type : 0 呼叫
            if ([@"0" isEqualToString:type]) {
                playVoice = YES;
                
                // 通过通知的Sound设置为voip_call.caf,这里播放一段空白音频,音频结束后结束震动
                NSString *path = [[NSBundle mainBundle] pathForResource:@"blank_call.mp3" ofType:nil];
                AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:path], &soundID);

                AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, soundCompleteCallback, NULL);
                AudioServicesAddSystemSoundCompletion(kSystemSoundID_Vibrate, NULL, NULL, soundCompleteCallback, NULL);
                
                [self playVoiceWithContent:self.bestAttemptContent.userInfo];
            }
        }
    }
    
    if (playVoice == NO) {
        self.contentHandler(self.bestAttemptContent);
    }
}


- (void)playVoiceWithContent:(NSDictionary *)userInfo {
    //  iOS 10之前前台没有通知栏
    // 此处调用之前的语音播报的内容
    [self playRegisterNotifications:userInfo];
}

- (void)playRegisterNotifications:(NSDictionary *)userInfo {
    [self registerNotifications:userInfo];
}


- (void)registerNotifications:(NSDictionary *)userInfo {
    [self startShakeSound];
}

/// 开始响铃
- (void)startShakeSound {
    // 声音
    AudioServicesPlaySystemSound(soundID);
    
    // 震动
    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);

    self.contentHandler(self.bestAttemptContent);
}

/// 结束响铃
- (void)stopShakeSound {
    AudioServicesDisposeSystemSoundID(soundID);
    AudioServicesRemoveSystemSoundCompletion(soundID);
}

//AudioServicesAddSystemSoundCompletion的回调函数
void soundCompleteCallback(SystemSoundID sound,void * clientData) {
    if (sound == kSystemSoundID_Vibrate) {
        AudioServicesPlayAlertSound(sound);//重复响铃震动
    } else {
        // 移除
        AudioServicesDisposeSystemSoundID(sound);
        AudioServicesRemoveSystemSoundCompletion(sound);

        AudioServicesDisposeSystemSoundID(kSystemSoundID_Vibrate);
        AudioServicesRemoveSystemSoundCompletion(kSystemSoundID_Vibrate);
    }
}

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

/**
 json转成NSDictionary
 
 @param jsonString json字符串
 @return NSDictionary
 */
- (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString {
    if (jsonString == nil) {
        return nil;
    }
    NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
    NSError *err;
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];
    if(err) {
        return nil;
    }
    return dic;
}

@end

四、使用推送

我这里使用的极光推送,注意需要设置推送的mutable-content=1字段。

五、点击通知后处理是否通实时音视频视频通话

在收到通知后,用户点击点击通知后处理是否通实时音视频视频通话逻辑。

在AppDelegate的方法

objectivec 复制代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

增加didReceiveRemoteNotification

objectivec 复制代码
// 获取启动时收到的APN
    NSDictionary *userInfo = [[DFPushManager shareInstance] launchOptionsRemoteNotification:launchOptions];
    if (userInfo) {
        [self application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:^(UIBackgroundFetchResult result) {
            
        }];
    }

当收到通知后,在didReceiveRemoteNotification中处理是否接通实时音视频的视频通话的逻辑

objectivec 复制代码
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    DFDebugLogger(@"didReceiveRemoteNotification");
    
    [[SDPushManager shareInstance] handleRemoteNotification:application userInfo:userInfo completion:nil];
    NSLog(@"收到通知:%@", userInfo);
    [[SDPushManager shareInstance] setBadgeNumberMax:nil];
    
    if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
        // 登录后调用check
        __weak typeof(self) weakSelf = self;
                if (userInfo && [userInfo isKindOfClass:[NSDictionary class]]) {
                    NSString *chatPushVo = [userInfo objectForKey:@"chatPushVo"];
                    if (chatPushVo && [chatPushVo isKindOfClass:[NSString class]] && appPushVo.length > 0) {
                        NSDictionary *dict = [DFJsonUtil dictionaryWithJsonString: chatPushVo];
                        if (dict && [dict isKindOfClass:[NSDictionary class]]) {
                            NSString *type = [NSString stringWithFormat:@"%@", [dict objectForKey:@"type"]];
                            // type : 0 呼叫
                            /// 0/用户呼叫
                            // 打开接听页面,进行接听
                        }
                    }
                }
    }

    completionHandler(UIBackgroundFetchResultNewData);
}

六、小结

iOS开发-NotificationServiceExtension实现实时音视频呼叫通知与语音播报

在之前的开发中,遇到了实时音视频呼叫通知,当App未打开或者App在后台时候,需要通知到用户,用户点击通知栏后是否接入实时音视频的视频或者音频通话。

学习记录,每天不停进步。

相关推荐
RollingPin1 天前
iOS八股文之 RunLoop
ios·多线程·卡顿·ios面试·runloop·ios保活·ios八股文
2501_916007471 天前
iOS 混淆工具链实战,多工具组合完成 IPA 混淆与加固(iOS混淆|IPA加固|无源码混淆|App 防反编译)
android·ios·小程序·https·uni-app·iphone·webview
LinXunFeng1 天前
Flutter webview 崩溃率上升怎么办?我的分析与解决方案
flutter·ios·webview
游戏开发爱好者81 天前
FTP 抓包分析实战,命令、被动主动模式要点、FTPS 与 SFTP 区别及真机取证流程
运维·服务器·网络·ios·小程序·uni-app·iphone
Nick56831 天前
Xcode16 避坑
ios
ii_best1 天前
IOS/ 安卓开发工具按键精灵Sys.GetAppList 函数使用指南:轻松获取设备已安装 APP 列表
android·开发语言·ios·编辑器
2501_915909061 天前
iOS 26 文件管理实战,多工具组合下的 App 数据访问与系统日志调试方案
android·ios·小程序·https·uni-app·iphone·webview
hweiyu001 天前
苹果iOS开发零基础特训(视频教程)
ios
2501_915918412 天前
掌握 iOS 26 App 运行状况,多工具协作下的监控策略
android·ios·小程序·https·uni-app·iphone·webview