我无法从应用代表处启动呼叫工具包UI来电。我该怎么做呢?我试过扬声器箱的例子,但它没有帮助。当我在ViewController中运行reportIncomingCall方法时,它可以工作。当我在AppDelegate中报告IncomingCall时,它不起作用。我需要它在Appdelegate中运行,所以我可以发送VoIP通知来报告来电。
这是我的app 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")
}
}
}
修改
如下面的评论中所述,很明显我没有设置代表。我在vc中有以下init。当我尝试在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.
答案 0 :(得分:1)
如果有帮助,请尝试此方法
将在AppDelegate中使用的我的Call Ui类
class CallUI : NSObject {
static var shared = CallUI()
var callControllerClass = CallUIController()
func initCall(payloadResponse Response: [AnyHashable:Any]) {
callControllerClass.getDataSortedFromPayload(PayloadResponse: Response)
}
}
用于处理呼叫工具包委托的类
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)
}
}