iOS CallKit的简单使用

/ 0评 / 0

监测电话状态

项目里有直播,所以要处理好状态,其他的都还好,在测试的时候遇到了接到电话时没有处理好的问题,这里简单介绍一下我的处理方法,使用CallKit框架:

#import 

@interface SGVideoViewController (){
    CXCallObserver *_center;
}
@end

@implementation SGVideoViewController
//销毁
- (void)dealloc{
    [_center setDelegate:nil queue:dispatch_get_main_queue()];
    _center = nil;
}

- (void)viewDidLoad {
	[super viewDidLoad];
	_center = [[CXCallObserver alloc] init];
    dispatch_queue_t queue =dispatch_queue_create("THIS_IS_A_CALL",NULL);
    [_center setDelegate:self queue:queue];
}

/** 以下为手动测试 如有错误欢迎指出
 拨通:  outgoing :1  onHold :0   hasConnected :0   hasEnded :0
 拒绝:  outgoing :1  onHold :0   hasConnected :0   hasEnded :1
 链接:  outgoing :1  onHold :0   hasConnected :1   hasEnded :0
 挂断:  outgoing :1  onHold :0   hasConnected :1   hasEnded :1
 
 新来电话:    outgoing :0  onHold :0   hasConnected :0   hasEnded :0
 保留并接听:  outgoing :1  onHold :1   hasConnected :1   hasEnded :0
 另一个挂掉:  outgoing :0  onHold :0   hasConnected :1   hasEnded :0
 保持链接:    outgoing :1  onHold :0   hasConnected :1   hasEnded :1
 对方挂掉:    outgoing :0  onHold :0   hasConnected :1   hasEnded :1
 */

- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call{
    SRQLog(@"outgoing :%d  onHold :%d   hasConnected :%d   hasEnded :%d",call.outgoing,call.onHold,call.hasConnected,call.hasEnded);
    if (call.hasEnded) {
        SRQLog(@"------挂断了----");
        //回到主线程
        [[RACScheduler mainThreadScheduler] afterDelay:2.f schedule:^{
            SRQLog(@"2秒后执行");
            dispatch_async(dispatch_get_main_queue(), ^{
                SRQLog(@"---当前页面为-----%@-----",[[self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count - 1] class]);
            });
        }];
    }
}

@end

CallKit 是苹果 iOS 10 新发布的框架,我上面只是用到了监控电话的功能,其实主要用到的地方时 VoIP 语音通话,下面介绍一下接收到推送弹出通话界面,要实现远程推送能拉起CallKit界面,必须要集成苹果在iOS 8推出的推送框架PushKit,因为只有PushKit能在推送到达时将APP从底层唤醒,一般推送无法实现。而只有底层唤醒了APP,才能通过APP的代码逻辑实现CallKit的调用从而展示出原生通话界面。否则消息到达时只会展示普通的推送而不能像电话到来时一样打开原生接听界面。

集成推送框架PushKit

首先要申请证书:

在工程设置里面的Background Mode里面添加voip、backgroundfetch、remotenotifications的支持。

在工程里引入pushkit框架:

#import 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {  
    //以下注册代码及详细证书配置可以在苹果PushKit官方文档中找到,在此不做赘述,详见:https://developer.apple.com/library/content/documentation/Performance/Conceptual/EnergyGuide-iOS/OptimizeVoIP.html#//apple_ref/doc/uid/TP40015243-CH30-SW1  
    dispatch_queue_t mainQueue = dispatch_get_main_queue();  
    PKPushRegistry * voipRegistry = [[PKPushRegistry alloc] initWithQueue: mainQueue];  
    voipRegistry.delegate = self;  
    voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];   
}

- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type{
	//获取token,这个token需要上传到服务器
    NSData *data = credentials.token;
    NSString *str = [NSString stringWithFormat:@"%@",credentials.token];
    NSLog(@"credentialsToken=%@",credentials.token); 
}

//收到pushkit的通知时会调用这个方法,但是不会有UI上的显示
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type{

}

集成打电话框架CallKit

在消息的入口(MessageManager)实现CallKit的唤起,先声明一个提供者provider

#import   
@interface:MessageManager()  
@property(nonatomic,strong)CXProvider * provider;  
@end  

在MessageManager收到消息时调用方法receiveCallWithName

-(void)receiveCallWithName:(NSString *)name {     
    CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];  
    NSString *remoteNickName = name;  
      
    if (!remoteNickName) {  
        remoteNickName = @"未知号码";  
    }
    [callUpdate setLocalizedCallerName:remoteNickName];   
    [callUpdate setHasVideo:[self isVideoCall]];  
      
    CXHandle *calleeHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:remoteNickName];  
    [callUpdate setRemoteHandle:calleeHandle];  
    [_provider reportNewIncomingCallWithUUID:[NSUUID UUID] update:callUpdate completion:^(NSError *error){  
        completion(error);  
    }];  
}  

在Call结束后,调用finishCallWithReason

-(void)finishCallWithReason:(CXCallEndedReason)reason  {  
    [_provider reportCallWithUUID:[NSUUID UUID] endedAtDate:nil reason:reason];  
}  

此时就可以完成CallKit功能的简单集成与实现.
另外:CallKit也有许多代理方法可供实现,分别对应不同情况下的处理,并不复杂,按需实现即可.

#pragma mark - CXProviderDelegate   
/// Called when the provider has been reset. Delegates must respond to this callback by cleaning up all internal call state (disconnecting communication channels, releasing network resources, etc.). This callback can be treated as a request to end all calls without the need to respond to any actions  
- (void)providerDidReset:(CXProvider *)provider  {  
    NSLog(@"CK: Provider did reset");  
}  
  
/// Called when the provider has been fully created and is ready to send actions and receive updates  
- (void)providerDidBegin:(CXProvider *)provider {  
    NSLog(@"CK: Provider did begin");  
}  
  
- (BOOL)provider:(CXProvider *)provider executeTransaction:(CXTransaction *)transaction {  
    NSLog(@"CK: Provider execute transaction");  
    return NO;  
}  
  
// If provider:executeTransaction:error: returned NO, each perform*CallAction method is called sequentially for each action in the transaction  
- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {  
    NSLog(@"CK: Start Call Action");  
    [action fulfill];  
}  
- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {  
    NSLog(@"CK: Answer Call Action");  
    [action fulfill];  
}  
- (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action {  
    NSLog(@"CK: End Call Action");  
    [action fulfill];  
}  
- (void)provider:(CXProvider *)provider performSetHeldCallAction:(CXSetHeldCallAction *)action {  
    NSLog(@"CK: Set Held Call Action");  
    [action fulfill];  
}  
- (void)provider:(CXProvider *)provider performSetMutedCallAction:(CXSetMutedCallAction *)action {  
    NSLog(@"CK: Set Muted Call Action");  
    [action fulfill];  
}  
- (void)provider:(CXProvider *)provider performSetGroupCallAction:(CXSetGroupCallAction *)action {  
    NSLog(@"CK: Set Group Call Action");  
    [action fulfill];  
}  
- (void)provider:(CXProvider *)provider performPlayDTMFCallAction:(CXPlayDTMFCallAction *)action {  
    NSLog(@"CK: Play DTMF Call Action");  
    [action fulfill];  
}  
  
/// Called when an action was not performed in time and has been inherently failed. Depending on the action, this timeout may also force the call to end. An action that has already timed out should not be fulfilled or failed by the provider delegate  
- (void)provider:(CXProvider *)provider timedOutPerformingAction:(CXAction *)action {  
    NSLog(@"CK: Provider Timed out");  
}  
  
/// Called when the provider's audio session activation state changes.  
- (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession *)audioSession {  
    NSLog(@"CK: Audio session activated");  
}  
- (void)provider:(CXProvider *)provider didDeactivateAudioSession:(AVAudioSession *)audioSession {  
    NSLog(@"CK: Audio session deactivated");  
}  

评论已关闭。