CallKit框架详细解析(一) —— 基本概览(一)

版本记录

版本号 时间
V1.0 2018.02.01

前言

苹果 iOS 10 新发布了一个新的框架CallKit,使第三方VOIP类型语音通话类APP有了更好的展现方式和用户体验的提升,接下来这几篇我们就一起看一下这个框架。

Overview

先看一下该框架的基本信息。

CallKit框架可以显示App的VoIP服务的系统调用UI,并协调其他应用程序和系统与您的呼叫服务。

CallKit可让您将呼叫服务与系统上其他与呼叫相关的应用程序集成在一起。 CallKit提供了呼叫接口,您可以使用VoIP服务处理后端通信。 对于传入和传出呼叫,CallKit显示与电话应用程序相同的接口,为您的应用程序提供更原生的外观和感觉。 CallKit可以正确响应系统级别的行为,例如Do Not Disturb

除了处理呼叫之外,您还可以提供Call Directory应用程序扩展程序,以提供来电显示信息以及与您的服务关联的黑名单列表。

在苹果官方WWDC16 Enhancing VoIP Apps with CallKit(Session 230)中,苹果的工程师为我们展示了集成Call Kit后的VOIP 通话APP的效果,例如在iPhone锁屏状态下APP来电时,通过Call Kit可以像iOS原生电话来电一样展现全屏的来电及接听界面,VOIP APP与系统Call有着相同的通话优先级别,而且在通讯录中的拔号记录,Siri唤起,勿扰模式等都有着很好的支持。

下面看一下原生APP、第三方APP在CallKit框架下的关系,在需要的时刻,原生或者第三方APP通过Call Kit提供的API向系统请求诸如来电、拔出等展现服务,由Call Service统一安排调度这些请求以达成统一的交互响应。

下面看一下框架的基本结构。


Receiving an Incoming Call - 接收来电

要配置您的应用程序以接收来电,请首先创建一个CXProvider对象并将其存储以供全局访问。 应用程序会向provider报告来电,以响应外部通知,例如由PushKit生成的VoIP推送通知。

// Listing 1 Receiving a VoIP–related push notification

// MARK: PKPushRegistryDelegate
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, forType type: PKPushType) {
    // report new incoming call
}

注意:有关VoIP推送通知和PushKit的更多信息,请参阅 Voice Over IP (VoIP) Best Practices

使用外部通知提供的信息,应用程序将创建一个UUID和一个CXCallUpdate对象来唯一标识调用和调用者,并使用 reportNewIncomingCallWithUUID:update:completion:方法将它们传递给provider

// Listing 2 Handling an incoming call

if let uuidString = payload.dictionaryPayload["UUID"] as? String,
    let identifier = payload.dictionaryPayload["identifier"] as? String,
    let uuid = UUID(uuidString: uuidString)
{
    let update = CXCallUpdate()    
    update.callerIdentifier = identifier
    
    provider.reportNewIncomingCall(with: uuid, update: update) { error in
        // …
    }
}

在呼叫连接之后,系统调用provider委托provider:performStartCallAction:方法。 在你的实现中,委托负责配置一个AVAudioSession,并在完成时调用fulfill

// Listing 3 Initiating the audio for a call

func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
    // configure audio session
    action.fulfill()
}

Making Outgoing Calls - 打出电话

用户可以通过以下任何方式使用VoIP应用发起呼叫:

  • 在应用程序内执行交互
  • 使用受支持的自定义网址方案打开链接
  • 使用Siri启动VoIP呼叫

要发出呼叫,应用程序将从其CXCallController对象请求一个CXStartCallAction对象。 该操作由唯一标识呼叫的UUID和指定收件人的CXHandle对象组成。

// Listing 4 Starting an outgoing call

let uuid = UUID()
let handle = CXHandle(type: .emailAddress, value: "jappleseed@apple.com")
 
let startCallAction = CXStartCallAction(call: uuid)
startCallAction.destination = handle
 
let transaction = CXTransaction(action: startCallAction)
callController.request(transaction) { error in
    if let error = error {
        print("Error requesting transaction: \(error)")
    } else {
        print("Requested transaction successfully")
    }
}

注意:有关注册和处理URL的更多信息,请参阅 Using URL Schemes to Communicate with Apps。有关使用Siri启动呼叫的更多信息,请参阅INStartAudioCallIntentHandling协议。

收件人应答呼叫后,系统会调用provider代理 provider:performStartCallAction:方法。 在你实现这个方法的时候,配置一个AVAudioSession,并在完成时调用action对象的fulfill方法。

// Listing 5 Initiating the call

func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
    // configure audio session
    action.fulfill()
}

注意:下载Speakerbox: Using CallKit to create a VoIP app程序示例代码项目,该示例演示如何使用CallKit框架创建VoIP应用程序。


Call Blocking and Identification - 呼叫阻止和识别

应用程序可以创建一个Call Directory应用程序扩展,以通过电话号码识别和阻止来电者。

Call Directory扩展中的电话号码由CXCallDirectoryPhoneNumber类型表示,由国家代码(如北美的1或中国的86)组成,后跟数字序列。

Creating a Call Directory App Extension - 创建一个Call Directory程序扩展

您可以通过添加一个新的项目目标并选择应用程序扩展下的Call Directory扩展模板来为您的应用程序创建一个Call Directory扩展。

您在Call Directory扩展的CXCallDirectoryProvider子类的beginRequestWithExtensionContext:方法的实现中设置了传入呼叫的标识和阻止。 系统启动应用扩展程序时调用此方法。

有关应用扩展程序的更多信息,请参阅 App Extension Programming Guide

Identifying Incoming Callers - 识别来电

当电话收到来电时,系统首先咨询用户的联系人以找到匹配的电话号码。如果找不到匹配项,则系统会查询您的应用程序的呼叫目录分机以查找匹配的条目以识别电话号码。这对于维护与系统联系人分离的用户的联系人列表(例如社交网络)或者用于识别可以从应用内发起的来话呼叫的应用是有用的,诸如用于客户服务支持或者递送通知。

例如,考虑在社交网络应用程序中与Jane交朋友的用户,但是他们的联系人中没有她的电话号码。社交网络应用程序具有呼叫目录应用程序扩展,下载并添加所有用户的朋友的电话号码。因此,当用户从Jane收到来电时,系统会显示“(App Name)Caller ID:Jane Appleseed”,而不是“Unknown Caller”

要提供有关传入调用者的标识信息,可以在beginRequestWithExtensionContext:的实现中使用 addIdentificationEntryWithNextSequentialPhoneNumber:label:方法。

// Listing 6

@interface CustomCallDirectoryProvider: CXCallDirectoryProvider
@end
 
@implementation CustomCallDirectoryProvider
- (void)beginRequestWithExtensionContext:(NSExtensionContext *)context {
    NSDictionary<NSNumber *, NSString *> *labelsKeyedByPhoneNumber = @{ … };
    for (NSNumber *phoneNumber in [labelsKeyedByPhoneNumber.allKeys sortedArrayUsingSelector:@selector(compare:)]) {
       NSString *label = labelsKeyedByPhoneNumber[phoneNumber];
       [context addIdentificationEntryWithNextSequentialPhoneNumber:(CXCallDirectoryPhoneNumber)[phoneNumber unsignedLongLongValue] label:label];
    }
 
    [context completeRequestWithCompletionHandler:nil];
}
@end

由于此方法仅在系统启动应用程序扩展而不是针对每个单独呼叫时调用,因此您必须一次指定呼叫标识信息;例如,您不能向Web服务请求查找有关来电的信息。

Blocking Incoming Calls - 阻止来电

当电话收到来电时,系统首先查询用户的阻止列表以确定是否应阻止呼叫。 如果电话号码不在用户或系统定义的阻止列表中,则系统会查询您的应用程序的呼叫目录扩展以查找匹配的阻止号码。 例如,这对于维护已知律师的数据库或允许用户阻止符合一组标准的任何数字的应用程序很有用。

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

注意:您可以指定您的呼叫目录应用程序扩展在其beginRequestWithExtensionContext:的实现中添加标识和/或阻止电话号码。

// Listing 7

@interface CustomCallDirectoryProvider: CXCallDirectoryProvider
@end
 
@implementation CustomCallDirectoryProvider
- (void)beginRequestWithExtensionContext:(NSExtensionContext *)context {
    NSArray<NSNumber *> *blockedPhoneNumbers.sorted = @[ … ];
     for (NSNumber *phoneNumber in [blockedPhoneNumbers.sorted sortedArrayUsingSelector:@selector(compare:)]) {
        [context addBlockingEntryWithNextSequentialPhoneNumber:(CXCallDirectoryPhoneNumber)[phoneNumber unsignedLongLongValue]];
     }
 
    [context completeRequestWithCompletionHandler:nil];
}
@end

Topics

1. First Steps

与通话相关的操作通过您的provider及其代理进行路由,您可以使用与您的服务进行通信。

2. Incoming Calls

当PushKit通知指示来电时,您将生成适当的操作。 CallKit通过呈现用于应答呼叫的系统界面来处理该操作。

3. Outgoing Calls

呼叫控制器开始传出呼叫,并处理后续与您的provider代理交互。

4. Call-Related Actions

回应CallKit报告的操作。

5. Call Information

获取有关通话的信息,并在通话状态发生变化时收到通知。

  • CXCall

    • 电话呼叫。
  • CXCallObserver

    • 用于管理活动呼叫列表并监视呼叫更改的对象的编程接口。
  • CXCallObserverDelegate

    • 呼叫改变状态时由呼叫观察者对象调用的方法的集合。
  • CXHandle

    • 可以到达呼叫接收方的方式,例如电话号码或电子邮件地址。

6. Caller ID

使用呼叫目录应用程序扩展可阻止呼叫并提供来电显示信息。

参考文章

后记

本篇已结束,后面更精彩~~~~

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

推荐阅读更多精彩内容