Search code examples
iosswiftbackground-processcallkitpushkit

IOS app crashing when recieving pushkit voip notification after force close


I'm trying to make pushkit voip messages work when the application is closed. The calls work and get displayed when app is in the foreground or in the background. But after the user force kills the app, when the notification gets recieved, the app terminates with signal 9 (killed by user/ios).

How can I fix this issue?

I've got background fetch, voip, audio and push notifications enabled in my app. Also tried removing all the Unity methods, putting the Callkit call in the PushRegistry method, creating a new provider when recieving a notification, even subscribing to the UIApplicationDidFinishLaunchingNotification event, but nothing worked. I've made it so the app is compliant to showing a call when recieving a voip notification. Here's my code:

@objcMembers class CallPlugin: UIResponder, UIApplicationDelegate, PKPushRegistryDelegate, CXProviderDelegate {

static var Instance: CallPlugin!
var provider: CXProvider!
var registry:PKPushRegistry!
var uuid:UUID!
var callController: CXCallController!

//class entry point
public static func registerVoIPPush(_ message: String) {
    Instance = CallPlugin()

    //Pushkit
    Instance.registry = PKPushRegistry(queue: DispatchQueue.main)
    Instance.registry.delegate = Instance
    Instance.registry.desiredPushTypes = [PKPushType.voIP]

    //Callkit
    let providerConfiguration = CXProviderConfiguration(localizedName: "testing")
    providerConfiguration.supportsVideo = true
    providerConfiguration.supportedHandleTypes = [.generic]
    Instance.provider = CXProvider(configuration: providerConfiguration)
    Instance.provider.setDelegate(Instance, queue: nil)        

    UnitySendMessage("ResponseHandler", "LogNative", "registration success")
}

//Get token
func pushRegistry( _ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
    if type == PKPushType.voIP {
        let deviceTokenString = credentials.token.map { String(format: "%02.2hhx", $0) }.joined()
        UnitySendMessage("ResponseHandler", "CredentialsRecieved",deviceTokenString)
    }
}       

//Get notification
func pushRegistry( _ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type:PKPushType, completion: @escaping () -> Void) {

    //UnitySendMessage("ResponseHandler", "LogNative", "Got something push")
    reportInComingCallWith(uuidString: "111", handle: "Paul", isVideo: false)
    completion()
}

//show the call
func reportInComingCallWith(uuidString:String,handle:String,isVideo:Bool) {
    //UnitySendMessage("ResponseHandler", "LogNative", "attempting call")
    let callUpdate = CXCallUpdate()        
    callUpdate.remoteHandle = CXHandle(type: .generic, value: handle)        
    callUpdate.hasVideo = false

    uuid = NSUUID() as UUID

    provider.reportNewIncomingCall(with: uuid as UUID, update: callUpdate){ (error) in
        if let error = error {
            UnitySendMessage("ResponseHandler", "LogNative", "error in starting call"+error.localizedDescription)
        }
    }
}

Solution

  • The issue was that my app was not creating the delegate and pushregistry objects in time for the notification to get fully handled.

    I solved it by overriding the app delegate and putting the notification initializations in WillFinishLaunching, like so:

    -(BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(nullable NSDictionary<UIApplicationLaunchOptionsKey,id> *)launchOptions{
       [CallPlugin registerVoIPPush:@"hmm"];
       [super application:application willFinishLaunchingWithOptions:launchOptions];
       return true;
    }
    

    That way everything is ready for the notification to be handled. I tried to put this in DidFinishLaunching first, but it was already too late for the notification and IOS was killing my application by then.