iOS CallKit與PushKit的集成(二)

聲明一下,現(xiàn)在國區(qū)的App Store應(yīng)中國特色社會主義的要求,禁止上架有callkit功能的APP,已有的也要整改,刪除callkit功能。

因為篇幅太長,所以把這個分成了三部分,希望讀者不要打我。。
上一篇文章講了PushKit的集成和CallKit打電話,那么這篇就來講講如何接電話和掛電話還有其他的一些操作。。
需要繼續(xù)完善上一篇文章的代碼哦。。
iOS CallKit與PushKit的集成(一)

接電話

首先來講一下如果接電話,來到ProviderDelegate中,添加方法:

func reportIncomingCall(uuid: UUID, handle: String, hasVideo: Bool = false, completion: ((Error?) -> Void)?) {
        //準備向系統(tǒng)報告一個 call update 事件,它包含了所有的來電相關(guān)的元數(shù)據(jù)。
        let update = self.callUpdate(handle: handle, hasVideo: hasVideo)
        //調(diào)用 CXProvider 的reportIcomingCall(with:update:completion:)方法通知系統(tǒng)有來電。
        provider.reportNewIncomingCall(with: uuid, update: update) { error in
            if error == nil {
                //completion 回調(diào)會在系統(tǒng)處理來電時調(diào)用。如果沒有任何錯誤,你就創(chuàng)建一個 Call 實例,將它添加到 CallManager 的通話列表。
                let call = Call(uuid: uuid, handle: handle)
                self.callManager.add(call: call)
            }
            //調(diào)用 completion,如果它不為空的話。
            completion?(error)
        }
 }

這個方法需要在所有接電話的地方手動調(diào)用,需要根據(jù)自己的業(yè)務(wù)邏輯來判斷。還有就是不要忘了iOS的版本兼容哦。。

在ProviderDelegate中實現(xiàn)系統(tǒng)接電話的代理:

func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
        //從 callManager 中獲得一個引用,UUID 指定為要接聽的動畫的 UUID。
        guard let call = callManager.callWithUUID(uuid: action.callUUID) else {
            action.fail()
            return
        }
        //設(shè)置通話要用的 audio session 是 App 的責任。系統(tǒng)會以一個較高的優(yōu)先級來激活這個 session。
        configureAudioSession()
        //通過調(diào)用 answer,你會表明這個通話現(xiàn)在激活
        call.answer()
        //在這里添加自己App接電話的邏輯

        //在處理一個 CXAction 時,重要的一點是,要么你拒絕它(fail),要么滿足它(fullfill)。如果處理過程中沒有發(fā)生錯誤,你可以調(diào)用 fullfill() 表示成功。
        action.fulfill()
    }

回到AppDelegate中,找到之前寫的PushKit收到推送的代理方法,在里面添加:

 func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
        guard type == .voIP else {
            log.info("Callkit& pushRegistry didReceiveIncomingPush But Not VoIP")
            return
        }
        log.info("Callkit& pushRegistry didReceiveIncomingPush")
        //別忘了在這里加上你們自己接電話的邏輯,比如連接聊天服務(wù)器啥的,不然這個電話打不通的
        if let uuidString = payload.dictionaryPayload["UUID"] as? String,
            let handle = payload.dictionaryPayload["handle"] as? String,
            let hasVideo = payload.dictionaryPayload["hasVideo"] as? Bool,
            let uuid = UUID(uuidString: uuidString)
        {
            if #available(iOS 10.0, *) {
                ProviderDelegate.shared.reportIncomingCall(uuid: uuid, handle: handle, hasVideo: hasVideo, completion: { (error) in
                    if let e = error {
                        log.info("CallKit& displayIncomingCall Error \(e)")
                    }
                })
            } else {
                // Fallback on earlier versions
            }
        }
    }

至此,CallKit接電話的邏輯完成了,你只需要在合適的地方調(diào)用reportIncomingCall就可以調(diào)出系統(tǒng)的通話頁面了。

掛電話

來到CallKitManager中,添加方法:

func end(call: Call) {
        //先創(chuàng)建一個 CXEndCallAction。將通話的 UUID 傳遞給構(gòu)造函數(shù),以便在后面可以識別通話。
        let endCallAction = CXEndCallAction(call: call.uuid)
        //然后將 action 封裝成 CXTransaction,以便發(fā)送給系統(tǒng)。
        let transaction = CXTransaction(action: endCallAction)
        requestTransaction(transaction)
}

來到ProviderDelegate中,實現(xiàn)系統(tǒng)代理:

func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
        //從 callManager 獲得一個 call 對象。
        guard let call = callManager.callWithUUID(uuid: action.callUUID) else {
            action.fail()
            return
        }
        //當 call 即將結(jié)束時,停止這次通話的音頻處理。
        stopAudio()
        //調(diào)用 end() 方法修改本次通話的狀態(tài),以允許其他類和新的狀態(tài)交互。
        call.end()
       //在這里添加自己App掛斷電話的邏輯
        //將 action 標記為 fulfill。
        action.fulfill()
        //當你不再需要這個通話時,可以讓 callManager 回收它。
        callManager.remove(call: call)
 }

添加完之后,只需要在你自己App掛斷電話的地方調(diào)用:

if #available(iOS 10.0, *) {
        if let call = CallKitManager.shared.calls.first { //因為我們這里不支持群通話,所以一次只有一個call
               CallKitManager.shared.end(call: call)
        }
}

就可以了。。這里的CallKitManager.shared.calls保存了所有CallKit的通話。是咱們自己寫的工具類哦,忘了的話自己翻翻上篇文章。

至此,CallKit掛電話的邏輯結(jié)束。。

通話暫時掛起

來到CallKitManager中,添加方法:

func setHeld(call: Call, onHold: Bool) {
        //這個 CXSetHeldCallAction 包含了通話的 UUID 以及保持狀態(tài)
        let setHeldCallAction = CXSetHeldCallAction(call: call.uuid, onHold: onHold)
        let transaction = CXTransaction()
        transaction.addAction(setHeldCallAction)
        
        requestTransaction(transaction)
}

來到ProviderDelegate中,實現(xiàn)系統(tǒng)代理:

func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {
        guard let call = callManager.callWithUUID(uuid: action.callUUID) else {
            action.fail()
            return
        }
        //獲得 CXCall 對象之后,我們要根據(jù) action 的 isOnHold 屬性來設(shè)置它的 state。
        call.state = action.isOnHold ? .held:.active
        //根據(jù)狀態(tài)的不同,分別進行啟動或停止音頻會話。
        if call.state == .held {
            stopAudio()
        } else {
            startAudio()
        }
        //在這里添加你們自己的通話掛起邏輯
        action.fulfill()
}

添加完之后,只需要在你自己App通話暫時掛起的地方調(diào)用:

if #available(iOS 10.0, *) {
        if let call = CallKitManager.shared.calls.first { 
               CallKitManager.shared.setHeld(call: call, onHold: true)
        }
}

就可以了。。

至此,CallKit通話暫時掛起的邏輯結(jié)束。。

麥克風靜音

來到CallKitManager中,添加方法:

func setMute(call: Call, muted: Bool) {
        //CXSetMutedCallAction設(shè)置麥克風靜音
        let setMuteCallAction = CXSetMutedCallAction(call: call.uuid, muted: muted)
        let transaction = CXTransaction()
        transaction.addAction(setMuteCallAction)
        requestTransaction(transaction)
}

來到ProviderDelegate中,實現(xiàn)系統(tǒng)代理:

func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
        guard let call = callManager.callWithUUID(uuid: action.callUUID) else {
            action.fail()
            return
        }
        //獲得 CXCall 對象之后,我們要根據(jù) action 的 ismuted 屬性來設(shè)置它的 state。
        call.state = action.isMuted ? .muted : .active
        //在這里添加你們自己的麥克風靜音邏輯
        action.fulfill()
    }

添加完之后,只需要在你自己App麥克風靜音的地方調(diào)用:

if #available(iOS 10.0, *) {
        if let call = CallKitManager.shared.calls.first { 
               CallKitManager.shared.setMute(call: call, muted: true)
        }
}

就可以了。。

至此,CallKit麥克風靜音的邏輯結(jié)束。。

到這里,在App內(nèi)互動的CallKit的基本功能都已經(jīng)集成完畢,其實到后面大家就能看出來,文章中所有的功能實現(xiàn),都是先在CallKitManager寫用戶需要調(diào)用的方法,在ProviderDelegate里面實現(xiàn)系統(tǒng)的代理方法,并且加上自己的通話邏輯。

關(guān)于系統(tǒng)揚聲器與聽筒的切換

這里不講如何切換揚聲器與聽筒,只講如何監(jiān)聽切換,保持App內(nèi)通話頁面免提的狀態(tài)跟系統(tǒng)通話頁面的一致。
在自己的通話頁面上添加通知監(jiān)聽:

NotificationCenter.default.addObserver(forName: NSNotification.Name.AVAudioSessionRouteChange, object: nil, queue: OperationQueue.main) {[weak self] (noti) in
            guard let w = self else { return }
            if #available(iOS 10.0, *) {
                let route = AVAudioSession.sharedInstance().currentRoute
                for desc in route.outputs {
                    if desc.portType == "Speaker" {
                        // "免提功能已開啟"
                    } else {
                        // "對方已接通,請使用聽筒接聽"
                    }
                }
            }
}

至此,CallKit的主要功能集成完畢,下一篇文章將繼續(xù)講解如何從系統(tǒng)通話記錄中直接撥打App的電話。
iOS CallKit與PushKit的集成(完結(jié)篇)

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

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

  • 聲明一下,現(xiàn)在國區(qū)的App Store應(yīng)中國特色社會主義的要求,禁止上架有callkit功能的APP,已有的也要整...
    撿書閱讀 4,110評論 3 9
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評論 19 139
  • 如果我們不去提升和擴展意識,生活中的問題始終得不到真正的解決。 什么是提升意識? 你一直抱怨老公愛喝酒,或者報怨他...
    蘭雪梅閱讀 1,321評論 0 0
  • 我小時候,過年是一年中最開心的日子,爸媽會帶我從城里回到在鄉(xiāng)下的奶奶家。奶奶家有大我兩個月的哥哥,在那個北方的小山...
    蘭呆呆和蘭萌萌閱讀 1,126評論 6 14
  • 白云藍天青草地, 陽光斜照著,暖暖的。 場上五彩繽紛, 紅黃的球衣與綠草拼湊成美麗的畫卷。 白色的界線與龍門成了畫...
    3fd25a0a9f73閱讀 550評論 0 0

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