概述
该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
类中,其实CallDirectoryHandler
是CXCallDirectoryProvider
的子类,在你创建完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