我无法触发来自应用程序委托的呼叫工具包 UI 来电。我该怎么做呢?我尝试了扬声器箱示例,但没有帮助。当我在 ViewController 中运行 reportIncomingCall 方法时,它可以工作。当我在 AppDelegate 中使用 reportIncomingCall 时,它不起作用。我需要它在 Appdelegate 中运行,以便我可以发送 VoIP 通知来报告来电。
这是我的应用程序委托:
@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")
}
}
}
编辑
正如下面的评论中所指出的,很明显我没有设置委托。我在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.