ios - 从应用程序委托(delegate)调用时未加载调用工具包 ui

标签 ios swift callkit

我无法触发来自应用程序委托(delegate)的调用套件 UI 来电。我该怎么做呢?我尝试了扬声器盒示例,但没有帮助。当我在 ViewController 中运行 reportIncomingCall 方法时,它起作用了。当我在 AppDelegate 中运行 reportIncomingCall 时,它不起作用。我需要它在 Appdelegate 中运行,以便我可以发送 VoIP 通知来报告来电。

这是我的应用委托(delegate):

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, PKPushRegistryDelegate {

    class var shared: AppDelegate {
        return UIApplication.shared.delegate as! AppDelegate
    }

    var window: UIWindow?

    var providerDelegate: ViewController?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        return true
    }

    func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {

        //register for voip notifications
        let voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
        voipRegistry.desiredPushTypes = Set([PKPushType.voIP])
        voipRegistry.delegate = self;
    }

    func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenForType type: PKPushType) {

         NSLog("token invalidated")

    }

    func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, forType type: PKPushType) {

        //print out the VoIP token. We will use this to test the notification.
        NSLog("voip token: \(credentials.token)")

        print("didUpdatePushCredentials: %@ - Type: %@", credentials.token, type)
        var token: String = ""
        for i in 0..<credentials.token.count {
            token += String(format: "%02.2hhx", credentials.token[i] as CVarArg)
        }

        print(token)

    }

    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, forType type: PKPushType) {

        print("notification receivd!")
        guard type == .voIP else { return }

        let uuidString = payload.dictionaryPayload["UUID"] as? String!
        let roomName = payload.dictionaryPayload["roomName"] as? String!
        print("uuid", uuidString!)
        print("roomName", roomName!)


        providerDelegate?.performStartCallAction(uuid: UUID(), roomName: "Test")

    }

    //Intents
    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
        guard let viewController = window?.rootViewController as? ViewController, let interaction = userActivity.interaction else {
            return false
        }

        var personHandle: INPersonHandle?

        if let startVideoCallIntent = interaction.intent as? INStartVideoCallIntent {
            personHandle = startVideoCallIntent.contacts?[0].personHandle
        } else if let startAudioCallIntent = interaction.intent as? INStartAudioCallIntent {
            personHandle = startAudioCallIntent.contacts?[0].personHandle
        }

        if let personHandle = personHandle {
            viewController.performStartCallAction(uuid: UUID(), roomName: personHandle.value)
        }

        return true
    }

}

在 ViewController 类中:

extension ViewController : CXProviderDelegate {

    func providerDidReset(_ provider: CXProvider) {
        logMessage(messageText: "providerDidReset:")

        localMedia?.audioController.stopAudio()
    }

    func providerDidBegin(_ provider: CXProvider) {
        logMessage(messageText: "providerDidBegin")
        //_ = Timer.scheduledTimerWithTimeInterval(15, target: self, selector: #selector(ViewController.expireCall), userInfo: nil, repeats: false)
    }

    func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
        logMessage(messageText: "provider:didActivateAudioSession:")

        localMedia?.audioController.startAudio()
    }

    func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
        logMessage(messageText: "provider:didDeactivateAudioSession:")
    }

    func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {
        logMessage(messageText: "provider:timedOutPerformingAction:")
    }

    func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
        logMessage(messageText: "provider:performStartCallAction:")

        localMedia?.audioController.configureAudioSession(.videoChatSpeaker)

        callKitProvider.reportOutgoingCall(with: action.callUUID, startedConnectingAt: nil)

        performRoomConnect(uuid: action.callUUID, roomName: action.handle.value)

        // Hang on to the action, as we will either fulfill it after we succesfully connect to the room, or fail
        // it if there is an error connecting.
        pendingAction = action
    }

    func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
        logMessage(messageText: "provider:performAnswerCallAction:")

        // NOTE: When using CallKit with VoIP pushes, the workaround from https://forums.developer.apple.com/message/169511
        //       suggests configuring audio in the completion block of the `reportNewIncomingCallWithUUID:update:completion:`
        //       method instead of in `provider:performAnswerCallAction:` per the Speakerbox example.
        // localMedia?.audioController.configureAudioSession()

        performRoomConnect(uuid: action.callUUID, roomName: self.roomTextField.text)

        // Hang on to the action, as we will either fulfill it after we succesfully connect to the room, or fail
        // it if there is an error connecting.
        pendingAction = action
    }

    func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
        NSLog("provider:performEndCallAction:")

        localMedia?.audioController.stopAudio()
        room?.disconnect()

        action.fulfill()
    }

    func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
        NSLog("provier:performSetMutedCallAction:")
        toggleMic(sender: self)
        action.fulfill()
    }

    func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {
        NSLog("provier:performSetHeldCallAction:")

        let cxObserver = callKitCallController.callObserver
        let calls = cxObserver.calls

        guard let call = calls.first(where:{$0.uuid == action.callUUID}) else {
            action.fail()
            return
        }

        if call.isOnHold {
            holdCall(onHold: false)
        } else {
            holdCall(onHold: true)
        }

        action.fulfill()
    }
}



extension ViewController {



     func performStartCallAction(uuid: UUID, roomName: String?) {
        let callHandle = CXHandle(type: .generic, value: roomName ?? "")
        let startCallAction = CXStartCallAction(call: uuid, handle: callHandle)
        let transaction = CXTransaction(action: startCallAction)

        callKitCallController.request(transaction)  { error in
            if let error = error {
                NSLog("StartCallAction transaction request failed: \(error.localizedDescription)")
                return
            }

            NSLog("StartCallAction transaction request successful")

            let callUpdate = CXCallUpdate()
            callUpdate.remoteHandle = callHandle
            callUpdate.supportsDTMF = false
            callUpdate.supportsHolding = true
            callUpdate.supportsGrouping = false
            callUpdate.supportsUngrouping = false
            callUpdate.hasVideo = true

            self.callKitProvider.reportCall(with: uuid, updated: callUpdate)
        }
    }

      func reportIncomingCall(uuid: UUID, roomName: String?, completion: ((NSError?) -> Void)? = nil) {
        let callHandle = CXHandle(type: .generic, value: roomName ?? "")

        print("calling!")
        let callUpdate = CXCallUpdate()
        callUpdate.remoteHandle = callHandle
        callUpdate.supportsDTMF = false
        callUpdate.supportsHolding = true
        callUpdate.supportsGrouping = false
        callUpdate.supportsUngrouping = false
        callUpdate.hasVideo = true

        callKitProvider.reportNewIncomingCall(with: uuid, update: callUpdate) { error in
            if error == nil {
                NSLog("Incoming call successfully reported.")

                // NOTE: CallKit with VoIP push workaround per https://forums.developer.apple.com/message/169511
                self.localMedia?.audioController.configureAudioSession(.videoChatSpeaker)
            } else {
                NSLog("Failed to report incoming call successfully: \(error?.localizedDescription).")
            }

            completion?(error as? NSError)
        }
    }

     func performEndCallAction(uuid: UUID) {
        let endCallAction = CXEndCallAction(call: uuid)
        let transaction = CXTransaction(action: endCallAction)

        callKitCallController.request(transaction) { error in
            if let error = error {
                NSLog("EndCallAction transaction request failed: \(error.localizedDescription).")
                return
            }

            NSLog("EndCallAction transaction request successful")
        }
    }

}

编辑

如以下评论所述,很明显我没有设置委托(delegate)。我在 vc 中有以下初始化。当我尝试在 didFinishLaunchingWithOptons 函数中设置它时,它要求我添加参数编码器。

ViewController init
required init?(coder aDecoder: NSCoder) {
    let configuration = CXProviderConfiguration(localizedName: "TestApp")
    configuration.maximumCallGroups = 1
    configuration.maximumCallsPerCallGroup = 1
    configuration.supportsVideo = true
    if let callKitIcon = UIImage(named: "iconMask80") {
        configuration.iconTemplateImageData = UIImagePNGRepresentation(callKitIcon)
    }

    callKitProvider = CXProvider(configuration: configuration)
    callKitCallController = CXCallController()

    super.init(coder: aDecoder)

    callKitProvider.setDelegate(self, queue: nil)

}

Appdelegate/didFinishLoadingWithOptions

providerDelegate = ViewController(coder: NSCoder) //this is where its messing up.

最佳答案

如果对你有帮助,试试这个方法

将在 AppDelegate 中使用的我的 Call Ui 类

class CallUI : NSObject {  
    static var shared = CallUI()
    var callControllerClass = CallUIController()

    func initCall(payloadResponse Response: [AnyHashable:Any]) {
        callControllerClass.getDataSortedFromPayload(PayloadResponse: Response)
    }
}

用于处理调用 Kit Delegates 的类

class CallUIController : UIViewController, CXProviderDelegate {

    var payloadResponse : [AnyHashable:Any]?
    var notificationTypeRx : CallNotificationType?
    var providerName : String?

    let provider = CXProvider(configuration: CXProviderConfiguration(localizedName: SAppName))
    let update = CXCallUpdate()
    var uuidUsed : UUID?
    var providerConfiguration : CXProviderConfiguration?

    func getDataSortedFromPayload(PayloadResponse Response: [AnyHashable:Any]) {
        if let APSData = Response["aps"] as? [String:Any] {
            if let notifyType = APSData["type"] as? String {
                if notifyType == "calling" {
                    self.notificationTypeRx = .StartCall
                    self.showCallUI(ProviderName: nameUsed ?? "")
                }
                else if notifyType == "disconnectCalling" {
                    /// Need to Disconnect Call
                    self.notificationTypeRx = .Endcall
                    /// Dismiss if any Loaded UI
                }
                else{
                    print("Type of notification wasn't found")
                }
            }
        }
        else{
            print("Aps Data was not found")
        }
    }

    func showCallUI(ProviderName Name: String) {

        provider.setDelegate(self, queue: nil)
        uuidUsed = UUID()
        update.hasVideo = true
        update.remoteHandle = CXHandle(type: .phoneNumber, value: Name)
        provider.reportNewIncomingCall(with: uuidUsed!, update: update, completion: { error in })
    }

    func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
        /// Accept Action
    }

    func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
        /// Decline Action
    }


    func providerDidReset(_ provider: CXProvider) {
        print("Declined Status")
    }

}

AppDelegate 用法

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
   print("Payload API Response \(payload.dictionaryPayload)")
   if UIApplication.shared.applicationState == .background {
        CallUI.shared.initCall(payloadResponse: payload.dictionaryPayload)
   }
}

关于ios - 从应用程序委托(delegate)调用时未加载调用工具包 ui,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40777196/

相关文章:

swift - 无法从类中访问值

ios - CallKit Audio Session 仅在导航到应用程序时启动

call - 如何在使用 CallKit 接听电话后保持 iOS 原生通话 UI

iphone - 我在哪里可以指定哪个 UIView 用于 UIViewController 的 View 属性?

ios - 如何在Itunes App Store上合并2个应用程序并制作一个应用程序?

ios - 单击按钮时如何更改按钮的文本? - swift

ios - 如何以编程方式激活 "Installed"?

Swift 4 - 获得按钮宽度的一半

ios - didActivate 没有被 CallKit 回调

objective-c - 如何获取 SecKeychainRef 的属性?