版本记录
版本号 | 时间 |
---|---|
V1.0 | 2019.04.14 星期日 |
前言
苹果 iOS 10 新发布了一个新的框架CallKit,使第三方VOIP类型语音通话类APP有了更好的展现方式和用户体验的提升,接下来这几篇我们就一起看一下这个框架。感兴趣的看下面几篇文章。
1. CallKit框架详细解析(一) —— 基本概览(一)
开始
首先看一下写作环境
Swift 4.2, iOS 12, Xcode 10
本篇了解您的应用如何使用CallKit进行系统级电话集成,以及如何构建用于呼叫阻止和识别的目录扩展。
iOS上的生活并不总是适合VoIP(Voice over IP)
应用程序开发人员。 特别是,发送通知很困难。 在后台使用您的应用程序,用户唯一的选择是定期通知,这很容易被遗漏。 如果没有丰富的内置调用UI,您的应用程序就会感觉集成度不够。
幸运的是,Apple在iOS 10
中引入了CallKit
!
在本教程中,您将通过构建以下应用程序来了解CallKit
的强大功能:
- 使用系统服务报告传入和传出呼叫。
- 管理呼叫目录以识别或阻止来电。
注意:
CallKit
功能在模拟器中不起作用。 要继续学习本教程,您需要安装iOS 12.0
或更高版本的iPhone。
在Xcode中打开项目文件,然后在Project
导航器中选择Hotline
。
首先更改bundle identifier
。 选择项目后,转到General
选项卡,然后找到Identity
部分。 将bundle identifier
更改为唯一的标识符:
接下来,查找签名Signing
部分。 在Team
旁边的下拉列表中选择您喜欢的开发团队(在我的情况下,这是我的个人团队)。 请务必选中自动管理签名(Automatically manage signing)
。 这允许Xcode自动为应用程序创建配置文件(provisioning profile)
。
注意:如果您看到Add Account…
按钮,则需要输入 Apple Developer帐户凭据才能选择开发团队。
要测试您的设置,请在iPhone上构建并运行该应用程序。
目前该应用程序不会做太多,但你会注意到启动项目中有几个源文件。 这些文件主要负责设置UI和处理用户交互。 在继续之前,有两个主要类别值得一看:
-
Call
代表一个电话。 该类公开用于标识call的属性(例如其UUID
或handle
)以及指示用户何时启动,应答或结束call的生命周期回调。 -
CallManager
当前维护应用程序中正在进行的呼叫列表,并具有添加或删除呼叫的方法。 您将在整个教程中进一步扩展此类。
What is CallKit?
CallKit
是一个旨在通过允许应用程序与本机电话UI集成来改善VoIP
体验的框架。 通过采用CallKit
,您的应用程序将能够:
- 在锁定和未锁定状态下使用本机来电屏幕
(native incoming call screen)
。 - 从本机电话应用程序的“联系人”
Contacts
,“收藏夹”Favorites
和“最近”Recents
屏幕开始呼叫calls
。 - 与系统中的其他呼叫
calls
相互作用。
在本节中,您将更加熟悉CallKit
架构。 下图显示了所有主要参与者:
使用CallKit时,您将与两个主要类进行交互:CXProvider和CXCallController。是时候潜入了!
1. CXProvider
您的应用将使用CXProvider
向系统报告任何带外通知。这些通常是外部事件,例如来电。
发生此类事件时,CXProvider
会创建一个呼叫更新(call update)
以通知系统。呼叫更新封装了新的或更改的呼叫相关信息。它们属于CXCallUpdate
类,它暴露诸如呼叫者姓名之类的属性,或者呼叫是视频还是仅音频。
当系统想要通知应用程序事件时,它使用CXAction
实例。CXAction
是一个代表电话操作的抽象类。对于每个动作,CallKit
提供了CXAction
的不同具体实现。例如,CXStartCallAction
表示发起拨出呼叫,而CXAnswerCallAction
表示接听来电。唯一的UUID
标识可能失败或实现的每个操作。
应用程序可以通过CXProviderDelegate
协议与CXProvider
进行通信,该协议定义了provider
生命周期事件和传入操作的方法。
2. CXCallController
该应用程序将使用CXCallController
通知系统用户发起的请求,例如启动呼叫操作。 这是CXProvider
和CXCallController
之间的主要区别:provider
向系统报告,而呼叫控制器(call controller)
代表用户向系统发出请求。
呼叫控制器使用事务来发出这些请求。 由CXTransaction
表示的事务包含一个或多个CXAction
实例。 呼叫控制器将事务发送到系统。 如果一切顺利,系统将以适当的操作响应provider
。
这在实践中是什么样的?
Incoming Calls
下图显示了传入呼叫流的高级概述:
- 1) 响应来电,应用程序构造一个
CXCallUpdate
并使用provider
将其发送到系统。 - 2) 系统将此发布为对其所有服务的传入呼叫。
- 3) 当用户应答呼叫时,系统会将
CXAnswerCallAction
实例发送给provider
。 - 4) 该应用程序通过实现适当的
CXProviderDelegate
方法来应答调用。
ProviderDelegate
首先,为provider
创建委托。 返回Xcode,在Project
导航器中突出显示App组,创建一个名为ProviderDelegate.swift
的新文件。
将以下代码添加到文件中:
import AVFoundation
import CallKit
class ProviderDelegate: NSObject {
// 1.
private let callManager: CallManager
private let provider: CXProvider
init(callManager: CallManager) {
self.callManager = callManager
// 2.
provider = CXProvider(configuration: ProviderDelegate.providerConfiguration)
super.init()
// 3.
provider.setDelegate(self, queue: nil)
}
// 4.
static var providerConfiguration: CXProviderConfiguration = {
let providerConfiguration = CXProviderConfiguration(localizedName: "Hotline")
providerConfiguration.supportsVideo = true
providerConfiguration.maximumCallsPerCallGroup = 1
providerConfiguration.supportedHandleTypes = [.phoneNumber]
return providerConfiguration
}()
}
这就是上面代码中发生的事情:
- 1) 存储对
provider
和call controller
的引用。provider
代理将与它们进行交互。 - 2) 使用适当的
CXProviderConfiguration
初始化provider
,存储为下面的静态变量。provider
配置指定调用的行为和功能。 - 3) 设置委托以响应来自
provider
的事件。 此行将导致构建错误,因为ProviderDelegate
尚未符合CXProviderDelegate
。 - 4) 在
Hotline
的情况下,provider
配置允许视频呼叫和电话号码处理,并将呼叫组的数量限制为一个。 有关进一步的自定义,请参阅CallKit documentation。
在配置下方,添加以下帮助方法:
func reportIncomingCall(
uuid: UUID,
handle: String,
hasVideo: Bool = false,
completion: ((Error?) -> Void)?
) {
// 1.
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .phoneNumber, value: handle)
update.hasVideo = hasVideo
// 2.
provider.reportNewIncomingCall(with: uuid, update: update) { error in
if error == nil {
// 3.
let call = Call(uuid: uuid, handle: handle)
self.callManager.add(call: call)
}
// 4.
completion?(error)
}
}
此帮助方法允许应用程序调用CXProvider API来报告传入呼叫。 这是发生了什么:
- 1) 准备系统的呼叫更新,其中包含相关的呼叫元数据。
- 2) 在
provider
上调用reportNewIncomingCall(with:update:completion)
以通知系统有来电。 - 3) 一旦系统处理呼叫,将调用完成处理程序。 假设没有错误,您可以创建一个
Call
实例并通过CallManager
将其添加到调用列表中。 - 4) 如果它不是
nil
,则调用完成处理程序。
应用程序中的其他类可以调用此方法以模拟传入的调用。
CXProviderDelegate
下一步是确保协议一致性。 仍然在ProviderDelegate.swift
中,声明一个符合CXProviderDelegate
的新扩展:
// MARK: - CXProviderDelegate
extension ProviderDelegate: CXProviderDelegate {
func providerDidReset(_ provider: CXProvider) {
stopAudio()
for call in callManager.calls {
call.end()
}
callManager.removeAllCalls()
}
}
CXProviderDelegate
仅指定一个必需的方法providerDidReset(_ :)
。 provider
在重置时调用此方法,使您的应用程序有机会清除正在进行的任何调用并恢复到干净状态。 在此实现中,您将终止正在进行的音频会话并处理所有活动的呼叫。
现在ProviderDelegate
提供了一种报告来电的方法,现在是时候使用了!
打开AppDelegate.swift
并首先向该类添加一个新属性:
var providerDelegate: ProviderDelegate!
在return
之前将以下内容添加到application(_:didFinishLaunchingWithOptions:)
:
providerDelegate = ProviderDelegate(callManager: callManager)
provider delegate
已准备好使用! 将以下方法添加到AppDelegate.swift
:
func displayIncomingCall(
uuid: UUID,
handle: String,
hasVideo: Bool = false,
completion: ((Error?) -> Void)?
) {
providerDelegate.reportIncomingCall(
uuid: uuid,
handle: handle,
hasVideo: hasVideo,
completion: completion)
}
此方法允许其他类访问provider delegate
的帮助程序方法。
最后一部分是将此调用连接到用户界面。 打开CallsViewController.swift
,它是应用程序主屏幕的控制器。 找到unwindForNewCall(_ :)
的空实现,并用以下代码替换它:
// 1.
guard
let newCallController = segue.source as? NewCallViewController,
let handle = newCallController.handle
else {
return
}
let videoEnabled = newCallController.videoEnabled
// 2.
let backgroundTaskIdentifier =
UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
AppDelegate.shared.displayIncomingCall(
uuid: UUID(),
handle: handle,
hasVideo: videoEnabled
) { _ in
UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier)
}
}
该代码段执行以下操作:
- 1) 从
NewCallViewController
中提取调用的属性,NewCallViewController
是此展开segue
的源。 - 2) 用户可以在操作完成之前暂停应用程序,因此应该使用后台任务。
后记
本篇主要讲述了CallKit框架基本使用,感兴趣的给个赞或者关注~~~
`