iOS CallKit初体验

概述

该CallKit框架提供VoIP功能,以及呼叫限制和识别的编程访问。

VoIP功能

应用程序可以使用CallKit接听来电,并使用本地电话用户界面呼出。

接收来电

要接收来电,一个应用程序创建一个CXProvider对象,并将其存储在全局访问。一个应用程序报告给provider一个来电以响应外部通知,如VoIP的推送通知。

这部分内容看起来不太容易理解,最好下载官方提供的源码,最好再看一看官方视频。
另外,源码只有Swift版没有OC版。
源码下载地址

注意

有关VoIP的推送通知和PushKit的更多信息,请参阅IP语音(VoIP)的最佳实践。

使用由外部通知中提供的信息,应用程序创建的UUID和CXCallUpdate对象唯一标识呼叫,主叫方,并把它们传递给使用 reportNewIncomingCallWithUUID:update:completion:方法。

一旦电话接通后,供应商代表发送provider:performStartCallAction:在实现中,delegate 负责配置AVAudioSession并且当操作完成的时候会调fulfill方法。

同样的,这段文字的说明也最好配合源码来解读。

外呼

用户可以以以下任何一个方式中在VoIP应用中发起外呼:

  • 执行应用程序内的交互
  • 打开支持自定义URL方案的链接
  • 启动Siri的使用VoIP通话

要拨出电话,一个应用程序从CXCallController对象请求CXStartCallAction。的动作是由一个UUID来唯一标识呼叫和CXHandle对象来指定收件人。

呼叫限制 & 验证

应用程序可以创建一个呼叫目录扩展,根据手机号码去验证或限制来电。

注意

在电话号码簿电话分机号码由CXCallDirectoryPhoneNumber类型代表,包括后面的数字序列的国码(如1北美或86中国)。

如何创建Call Directory Extension?

所有创建Extension的方式都是一样的,详见我的另一篇文章。
iOS Action Extension开发教程,实现跨APP的数据共享

使用

所有的来电验证和限制都在beginRequestWithExtensionContext:方法的实现中。
beginRequestWithExtensionContext方法在CallDirectoryHandler类中,其实CallDirectoryHandlerCXCallDirectoryProvider的子类,在你创建完Call Directory Extension后,在扩展文件中可以看到系统生成的文件,在这个文件中就可以看到该方法。

识别来电者

当一个手机接收来电时,系统会在第一时间参考用户的联系人目录去寻找匹配的手机号码。如果没有匹配的,系统会到你应用程序的Call Directory Extension中去寻找匹配的手机号码,这有益于保持用户的联系人信息和系统的联系人信息相对独立,比如一个社交网络,或者验证由应用程序发起的来电,比如客户服务支持或交付通知。

举个例子,考虑某个用户谁是Jane的社交网络app的好友,但是Jane的联系人中没有这个人手机号码。于是这个社交网络APP可以使用Call Directory Extension,下载并添加所有该用户朋友的手机号码。正因为如此,当该用户接受到一个来自于Jane的来电时,系统会显示一些像"(App Name) Caller ID: Jane Applesedd"而不是像"Unknown Caller"。

若要提供有关来电者身份信息,你可以在beginRequestWithExtensionContext:的方法实现中使用addIdentificationEntryWithNextSequentialPhoneNumber:label:方法。

代码如下:

@interface CallDirectoryHandler () 
@end

@implementation CallDirectoryHandler

//识别和拦截通过该方法设置
- (void)beginRequestWithExtensionContext:(CXCallDirectoryExtensionContext *)context {
    context.delegate = self;

    if (![self addBlockingPhoneNumbersToContext:context]) {
        NSLog(@"Unable to add blocking phone numbers");
        NSError *error = [NSError errorWithDomain:@"CallDirectoryHandler" code:1 userInfo:nil];
        [context cancelRequestWithError:error];
        return;
    }
    
    if (![self addIdentificationPhoneNumbersToContext:context]) {
        NSLog(@"Unable to add identification phone numbers");
        NSError *error = [NSError errorWithDomain:@"CallDirectoryHandler" code:2 userInfo:nil];
        [context cancelRequestWithError:error];
        return;
    }
    
    [context completeRequestWithCompletionHandler:nil];
}

//来电验证身份
- (BOOL)addIdentificationPhoneNumbersToContext:(CXCallDirectoryExtensionContext *)context {
    // Retrieve phone numbers to identify and their identification labels from data store. For optimal performance and memory usage when there are many phone numbers,
    // consider only loading a subset of numbers at a given time and using autorelease pool(s) to release objects allocated during each batch of numbers which are loaded.
    //
    // Numbers must be provided in numerically ascending order.
    CXCallDirectoryPhoneNumber phoneNumbers[] = { 18775555555, 18885555555 };
    NSArray *labels = @[ @"Telemarketer", @"Local business" ];
    NSUInteger count = (sizeof(phoneNumbers) / sizeof(CXCallDirectoryPhoneNumber));

    for (NSUInteger i = 0; i < count; i += 1) {
        CXCallDirectoryPhoneNumber phoneNumber = phoneNumbers[i];
        NSString *label = labels[i];
        //这里可以检查来电者的身份信息
        [context addIdentificationEntryWithNextSequentialPhoneNumber:phoneNumber label:label];
    }

    return YES;
}
@end

beginRequestWithExtensionContext:这个方法只有当系统启动App Extension时才会被调用,而不是每次单独来电时调用,你必须一次性指定全部的“呼叫标识信息”。

阻止来电

当一个手机接收到来电时,系统首先会参考用户的黑名单以确定哪个呼叫时应该被阻止。如果这个手机号码不在用户或者系统定义的黑名单中,系统就会参考应用程序的Call Direcotry Extension去寻找匹配的黑名单号码。这样做有益于应用程序,允许用户依据一套准则阻止任意的号码。

去阻止特定手机号码的来电,你你可以在beginRequestWithExtensionContext:的方法实现中使用addBlockingEntryWithNextSequentialPhoneNumber::方法。

代码如下:

@interface CallDirectoryHandler () 
@end

@implementation CallDirectoryHandler

//识别和拦截通过该方法设置
- (void)beginRequestWithExtensionContext:(CXCallDirectoryExtensionContext *)context {
    context.delegate = self;

    if (![self addBlockingPhoneNumbersToContext:context]) {
        NSLog(@"Unable to add blocking phone numbers");
        NSError *error = [NSError errorWithDomain:@"CallDirectoryHandler" code:1 userInfo:nil];
        [context cancelRequestWithError:error];
        return;
    }
    
    if (![self addIdentificationPhoneNumbersToContext:context]) {
        NSLog(@"Unable to add identification phone numbers");
        NSError *error = [NSError errorWithDomain:@"CallDirectoryHandler" code:2 userInfo:nil];
        [context cancelRequestWithError:error];
        return;
    }
    
    [context completeRequestWithCompletionHandler:nil];
}

//来电拦截
- (BOOL)addBlockingPhoneNumbersToContext:(CXCallDirectoryExtensionContext *)context {
    // Retrieve phone numbers to block from data store. For optimal performance and memory usage when there are many phone numbers,
    // consider only loading a subset of numbers at a given time and using autorelease pool(s) to release objects allocated during each batch of numbers which are loaded.
    //
    // Numbers must be provided in numerically ascending order.
    CXCallDirectoryPhoneNumber phoneNumbers[] = { 14085555555, 18005555555 };
    NSUInteger count = (sizeof(phoneNumbers) / sizeof(CXCallDirectoryPhoneNumber));

    for (NSUInteger index = 0; index < count; index += 1) {
        CXCallDirectoryPhoneNumber phoneNumber = phoneNumbers[index];
        [context addBlockingEntryWithNextSequentialPhoneNumber:phoneNumber];
    }

    return YES;
}
@end

总结

如果项目不使用VoIP,看 呼叫限制 & 验证 模块之后的内容就可以了。然后在扩展类的相应方法中配置各自的名单。
如果项目中使用VoIP,请继续看其他资料。

其他资料详见

本文翻译自Call Directory Extension Apple官方文档
Enhancing VoIP Apps with CallKit

你可能感兴趣的:(iOS CallKit初体验)