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

版本記錄

版本號 時(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)級電話集成,以及如何構(gòu)建用于呼叫阻止和識別的目錄擴(kuò)展。

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

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

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

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

注意: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)識符:

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

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

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

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

  • Call代表一個(gè)電話。 該類公開用于標(biāo)識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會創(chuàng)建一個(gè)呼叫更新(call update)以通知系統(tǒng)。呼叫更新封裝了新的或更改的呼叫相關(guān)信息。它們屬于CXCallUpdate類,它暴露諸如呼叫者姓名之類的屬性,或者呼叫是視頻還是僅音頻。

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

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

2. CXCallController

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

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

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


Incoming Calls

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

  • 1) 響應(yīng)來電,應(yīng)用程序構(gòu)造一個(gè)CXCallUpdate并使用provider將其發(fā)送到系統(tǒng)。
  • 2) 系統(tǒng)將此發(fā)布為對其所有服務(wù)的傳入呼叫。
  • 3) 當(dāng)用戶應(yīng)答呼叫時(shí),系統(tǒng)會將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) 存儲對providercall controller的引用。 provider代理將與它們進(jìn)行交互。
  • 2) 使用適當(dāng)?shù)?code>CXProviderConfiguration初始化provider,存儲為下面的靜態(tài)變量。provider配置指定調(diào)用的行為和功能。
  • 3) 設(shè)置委托以響應(yīng)來自provider的事件。 此行將導(dǎo)致構(gòu)建錯(cuò)誤,因?yàn)?code>ProviderDelegate尚未符合CXProviderDelegate。
  • 4) 在Hotline的情況下,provider配置允許視頻呼叫和電話號碼處理,并將呼叫組的數(shù)量限制為一個(gè)。 有關(guān)進(jìn)一步的自定義,請參閱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ī)會清除正在進(jìn)行的任何調(diào)用并恢復(fù)到干凈狀態(tài)。 在此實(shí)現(xiàn)中,您將終止正在進(jìn)行的音頻會話并處理所有活動(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)該使用后臺任務(wù)。

后記

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

`

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容