CallKit框架詳細(xì)解析(二) —— 基本使用(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2019.04.14 星期日

前言

蘋果 iOS 10 新發(fā)布了一個(gè)新的框架CallKit,使第三方VOIP類型語音通話類APP有了更好的展現(xiàn)方式和用戶體驗(yàn)的提升俏橘,接下來這幾篇我們就一起看一下這個(gè)框架寺庄。感興趣的看下面幾篇文章。
1. CallKit框架詳細(xì)解析(一) —— 基本概覽(一)

開始

首先看一下寫作環(huán)境

Swift 4.2, iOS 12, Xcode 10

本篇了解您的應(yīng)用如何使用CallKit進(jìn)行系統(tǒng)級(jí)電話集成,以及如何構(gòu)建用于呼叫阻止和識(shí)別的目錄擴(kuò)展忘蟹。

iOS上的生活并不總是適合VoIP(Voice over IP)應(yīng)用程序開發(fā)人員。 特別是们陆,發(fā)送通知很困難寒瓦。 在后臺(tái)使用您的應(yīng)用程序,用戶唯一的選擇是定期通知坪仇,這很容易被遺漏杂腰。 如果沒有豐富的內(nèi)置調(diào)用UI,您的應(yīng)用程序就會(huì)感覺集成度不夠椅文。

幸運(yùn)的是喂很,Apple在iOS 10中引入了CallKit

在本教程中皆刺,您將通過構(gòu)建以下應(yīng)用程序來了解CallKit的強(qiáng)大功能:

  • 使用系統(tǒng)服務(wù)報(bào)告?zhèn)魅牒蛡鞒龊艚小?/li>
  • 管理呼叫目錄以識(shí)別或阻止來電少辣。

注意:CallKit功能在模擬器中不起作用。 要繼續(xù)學(xué)習(xí)本教程羡蛾,您需要安裝iOS 12.0或更高版本的iPhone漓帅。

在Xcode中打開項(xiàng)目文件,然后在Project導(dǎo)航器中選擇Hotline

首先更改bundle identifier忙干。 選擇項(xiàng)目后器予,轉(zhuǎn)到General選項(xiàng)卡,然后找到Identity部分捐迫。 將bundle identifier更改為唯一的標(biāo)識(shí)符:

接下來乾翔,查找簽名Signing部分。 在Team旁邊的下拉列表中選擇您喜歡的開發(fā)團(tuán)隊(duì)(在我的情況下施戴,這是我的個(gè)人團(tuán)隊(duì))反浓。 請(qǐng)務(wù)必選中自動(dòng)管理簽名(Automatically manage signing)。 這允許Xcode自動(dòng)為應(yīng)用程序創(chuàng)建配置文件(provisioning profile)赞哗。

注意:如果您看到Add Account…按鈕雷则,則需要輸入 Apple Developer帳戶憑據(jù)才能選擇開發(fā)團(tuán)隊(duì)。

要測(cè)試您的設(shè)置懈玻,請(qǐng)?jiān)趇Phone上構(gòu)建并運(yùn)行該應(yīng)用程序巧婶。

目前該應(yīng)用程序不會(huì)做太多,但你會(huì)注意到啟動(dòng)項(xiàng)目中有幾個(gè)源文件涂乌。 這些文件主要負(fù)責(zé)設(shè)置UI和處理用戶交互艺栈。 在繼續(xù)之前,有兩個(gè)主要類別值得一看:

  • Call代表一個(gè)電話湾盒。 該類公開用于標(biāo)識(shí)call的屬性(例如其UUIDhandle)以及指示用戶何時(shí)啟動(dòng)湿右,應(yīng)答或結(jié)束call的生命周期回調(diào)。
  • CallManager當(dāng)前維護(hù)應(yīng)用程序中正在進(jìn)行的呼叫列表罚勾,并具有添加或刪除呼叫的方法毅人。 您將在整個(gè)教程中進(jìn)一步擴(kuò)展此類。

What is CallKit?

CallKit是一個(gè)旨在通過允許應(yīng)用程序與本機(jī)電話UI集成來改善VoIP體驗(yàn)的框架尖殃。 通過采用CallKit丈莺,您的應(yīng)用程序?qū)⒛軌颍?/p>

  • 在鎖定和未鎖定狀態(tài)下使用本機(jī)來電屏幕(native incoming call screen)
  • 從本機(jī)電話應(yīng)用程序的“聯(lián)系人” Contacts送丰,“收藏夾” Favorites和“最近”Recents屏幕開始呼叫calls缔俄。
  • 與系統(tǒng)中的其他呼叫calls相互作用。

在本節(jié)中器躏,您將更加熟悉CallKit架構(gòu)俐载。 下圖顯示了所有主要參與者:

使用CallKit時(shí),您將與兩個(gè)主要類進(jìn)行交互:CXProvider和CXCallController登失。是時(shí)候潛入了遏佣!

1. CXProvider

您的應(yīng)用將使用CXProvider向系統(tǒng)報(bào)告任何帶外通知。這些通常是外部事件揽浙,例如來電状婶。

發(fā)生此類事件時(shí)意敛,CXProvider會(huì)創(chuàng)建一個(gè)呼叫更新(call update)以通知系統(tǒng)。呼叫更新封裝了新的或更改的呼叫相關(guān)信息太抓。它們屬于CXCallUpdate類空闲,它暴露諸如呼叫者姓名之類的屬性,或者呼叫是視頻還是僅音頻走敌。

當(dāng)系統(tǒng)想要通知應(yīng)用程序事件時(shí),它使用CXAction實(shí)例逗噩。CXAction是一個(gè)代表電話操作的抽象類掉丽。對(duì)于每個(gè)動(dòng)作,CallKit提供了CXAction的不同具體實(shí)現(xiàn)异雁。例如捶障,CXStartCallAction表示發(fā)起撥出呼叫,而CXAnswerCallAction表示接聽來電纲刀。唯一的UUID標(biāo)識(shí)可能失敗或?qū)崿F(xiàn)的每個(gè)操作项炼。

應(yīng)用程序可以通過CXProviderDelegate協(xié)議與CXProvider進(jìn)行通信,該協(xié)議定義了provider生命周期事件和傳入操作的方法示绊。

2. CXCallController

該應(yīng)用程序?qū)⑹褂?code>CXCallController通知系統(tǒng)用戶發(fā)起的請(qǐng)求锭部,例如啟動(dòng)呼叫操作。 這是CXProviderCXCallController之間的主要區(qū)別:provider向系統(tǒng)報(bào)告面褐,而呼叫控制器(call controller)代表用戶向系統(tǒng)發(fā)出請(qǐng)求拌禾。

呼叫控制器使用事務(wù)來發(fā)出這些請(qǐng)求。 由CXTransaction表示的事務(wù)包含一個(gè)或多個(gè)CXAction實(shí)例展哭。 呼叫控制器將事務(wù)發(fā)送到系統(tǒng)湃窍。 如果一切順利,系統(tǒng)將以適當(dāng)?shù)牟僮黜憫?yīng)provider匪傍。

這在實(shí)踐中是什么樣的您市?


Incoming Calls

下圖顯示了傳入呼叫流的高級(jí)概述:

  • 1) 響應(yīng)來電,應(yīng)用程序構(gòu)造一個(gè)CXCallUpdate并使用provider將其發(fā)送到系統(tǒng)役衡。
  • 2) 系統(tǒng)將此發(fā)布為對(duì)其所有服務(wù)的傳入呼叫茵休。
  • 3) 當(dāng)用戶應(yīng)答呼叫時(shí),系統(tǒng)會(huì)將CXAnswerCallAction實(shí)例發(fā)送給provider映挂。
  • 4) 該應(yīng)用程序通過實(shí)現(xiàn)適當(dāng)?shù)?code>CXProviderDelegate方法來應(yīng)答調(diào)用泽篮。

ProviderDelegate

首先,為provider創(chuàng)建委托柑船。 返回Xcode帽撑,在Project導(dǎo)航器中突出顯示App組,創(chuàng)建一個(gè)名為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
  }()
}

這就是上面代碼中發(fā)生的事情:

  • 1) 存儲(chǔ)對(duì)providercall controller的引用亏拉。 provider代理將與它們進(jìn)行交互扣蜻。
  • 2) 使用適當(dāng)?shù)?code>CXProviderConfiguration初始化provider,存儲(chǔ)為下面的靜態(tài)變量及塘。provider配置指定調(diào)用的行為和功能莽使。
  • 3) 設(shè)置委托以響應(yīng)來自provider的事件。 此行將導(dǎo)致構(gòu)建錯(cuò)誤笙僚,因?yàn)?code>ProviderDelegate尚未符合CXProviderDelegate芳肌。
  • 4) 在Hotline的情況下,provider配置允許視頻呼叫和電話號(hào)碼處理肋层,并將呼叫組的數(shù)量限制為一個(gè)亿笤。 有關(guān)進(jìn)一步的自定義,請(qǐng)參閱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)
  }
}

此幫助方法允許應(yīng)用程序調(diào)用CXProvider API來報(bào)告?zhèn)魅牒艚小?這是發(fā)生了什么:

  • 1) 準(zhǔn)備系統(tǒng)的呼叫更新,其中包含相關(guān)的呼叫元數(shù)據(jù)蒲拉。
  • 2) 在provider上調(diào)用reportNewIncomingCall(with:update:completion)以通知系統(tǒng)有來電肃拜。
  • 3) 一旦系統(tǒng)處理呼叫,將調(diào)用完成處理程序雌团。 假設(shè)沒有錯(cuò)誤燃领,您可以創(chuàng)建一個(gè)Call實(shí)例并通過CallManager將其添加到調(diào)用列表中。
  • 4) 如果它不是nil辱姨,則調(diào)用完成處理程序柿菩。

應(yīng)用程序中的其他類可以調(diào)用此方法以模擬傳入的調(diào)用。


CXProviderDelegate

下一步是確保協(xié)議一致性雨涛。 仍然在ProviderDelegate.swift中枢舶,聲明一個(gè)符合CXProviderDelegate的新擴(kuò)展:

// MARK: - CXProviderDelegate
extension ProviderDelegate: CXProviderDelegate {
  func providerDidReset(_ provider: CXProvider) {
    stopAudio()
    
    for call in callManager.calls {
      call.end()
    }
    
    callManager.removeAllCalls()
  }
}

CXProviderDelegate僅指定一個(gè)必需的方法providerDidReset(_ :)provider在重置時(shí)調(diào)用此方法替久,使您的應(yīng)用程序有機(jī)會(huì)清除正在進(jìn)行的任何調(diào)用并恢復(fù)到干凈狀態(tài)凉泄。 在此實(shí)現(xiàn)中,您將終止正在進(jìn)行的音頻會(huì)話并處理所有活動(dòng)的呼叫蚯根。

現(xiàn)在ProviderDelegate提供了一種報(bào)告來電的方法后众,現(xiàn)在是時(shí)候使用了!

打開AppDelegate.swift并首先向該類添加一個(gè)新屬性:

var providerDelegate: ProviderDelegate!

return之前將以下內(nèi)容添加到application(_:didFinishLaunchingWithOptions:)

providerDelegate = ProviderDelegate(callManager: callManager)

provider delegate已準(zhǔn)備好使用颅拦! 將以下方法添加到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的幫助程序方法蒂誉。

最后一部分是將此調(diào)用連接到用戶界面。 打開CallsViewController.swift距帅,它是應(yīng)用程序主屏幕的控制器右锨。 找到unwindForNewCall(_ :)的空實(shí)現(xiàn),并用以下代碼替換它:

// 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)
  }
}

該代碼段執(zhí)行以下操作:

  • 1) 從NewCallViewController中提取調(diào)用的屬性碌秸,NewCallViewController是此展開segue的源绍移。
  • 2) 用戶可以在操作完成之前暫停應(yīng)用程序悄窃,因此應(yīng)該使用后臺(tái)任務(wù)。

后記

本篇主要講述了CallKit框架基本使用蹂窖,感興趣的給個(gè)贊或者關(guān)注~~~

`

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末轧抗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子瞬测,更是在濱河造成了極大的恐慌横媚,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涣楷,死亡現(xiàn)場(chǎng)離奇詭異分唾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)狮斗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弧蝇,“玉大人碳褒,你說我怎么就攤上這事】戳疲” “怎么了沙峻?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長两芳。 經(jīng)常有香客問我摔寨,道長,這世上最難降的妖魔是什么怖辆? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任是复,我火速辦了婚禮,結(jié)果婚禮上竖螃,老公的妹妹穿的比我還像新娘淑廊。我一直安慰自己,他們只是感情好特咆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布季惩。 她就那樣靜靜地躺著,像睡著了一般腻格。 火紅的嫁衣襯著肌膚如雪画拾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天菜职,我揣著相機(jī)與錄音青抛,去河邊找鬼。 笑死些楣,一個(gè)胖子當(dāng)著我的面吹牛脂凶,可吹牛的內(nèi)容都是我干的宪睹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蚕钦,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼亭病!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嘶居,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤罪帖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后邮屁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體整袁,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年佑吝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坐昙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡芋忿,死狀恐怖炸客,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情戈钢,我是刑警寧澤痹仙,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站殉了,受9級(jí)特大地震影響开仰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜薪铜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一众弓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痕囱,春花似錦田轧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間登钥,已是汗流浹背狂魔。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國打工信殊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓脖阵,卻偏偏與公主長得像污秆,于是被迫代替她去往敵國和親劈猪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容