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 () <CXCallDirectoryExtensionContextDelegate>
@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<NSString *> *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 () <CXCallDirectoryExtensionContextDelegate>
@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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • 新特性 对于开发者来说,好消息是 iOS 10 中并没有加入太多内容。按照适配的需求,来年的 iOS 开发至少应该...
    9d710097e616阅读 777评论 0 1
  • WWDC2106 iOS10新特性及开发者要注意什么 当晚看了WWDC2016直播,第二天我安装了iOS10试用版...
    五八四十阅读 2,058评论 0 1
  • 关于郑州我想的全是你,想来想去都是忏悔和委屈;关于郑州我爱的全是你,爱来爱去不明白爱的意义。关于郑州郑州只是偶尔想...
    周小粥粥阅读 667评论 8 7
  • 主持人: 大家很不容易走过90天。 毕业总结会议结束后,大群就会解散了,要移到大群的同学会,还没有进群的同学找雅文...
    小溪01号家庭陪伴师阅读 301评论 0 0