6

I'm struggling for some time here and probably this is my mistake but I can't find it anywhere, an answer for this.

I have implemented PushKit in some ways, but none of them were effective.

I have added the correct background modes, implemented the callbacks in the correct way, didUpdatePushCredentials is getting called normally...

However, the credentials: PKPushCredentials! variable, is giving me an error pointer... It´s not null... not nil... not anything... It simply does not have a value... it´s allocated garbage... and for that reason.. I´m receiving EXC_BREAKPOINT..

I have tried in 3 different devices... Same behavior...

I have already created the Certificates for VoIP Push...

I have done it in different ways:

  • By creating the void Push Registry object after didRegisterForRemotePushNotification
  • By creating the push registry without registering for remote push notification...
  • By creating the registry with main, global and custom queues..

Allways the same...

Here´s the code:

extension AppDelegate : PKPushRegistryDelegate {

func registerForVoipPush() {

    self.registry = PKPushRegistry(queue:nil)

    if self.registry != nil {

        self.registry!.delegate = self
        self.registry!.desiredPushTypes = Set<String>(arrayLiteral: PKPushTypeVoIP)
    }

    //let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories:nil)
    //UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
}

func pushRegistry(registry: PKPushRegistry!, didUpdatePushCredentials credentials: PKPushCredentials!, forType type: String!) {

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

    if credentials != nil {

        let username = NSUserDefaults.standardUserDefaults().objectForKey("username") as? String
        if username != nil {

            ServerDefinitions.subscribeForPush(username!, token: NSString(data: credentials.token, encoding: NSUTF8StringEncoding) as! String, callback: { (retMsg) -> Void in

                print(retMsg)
            })

        }
    }

}

func pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload payload: PKPushPayload!, forType type: String!) {

    let payloadDict = payload.dictionaryPayload["aps"] as? Dictionary<String, String>
    let message = payloadDict?["alert"]

    //present a local notifcation to visually see when we are recieving a VoIP Notification
    if UIApplication.sharedApplication().applicationState == UIApplicationState.Background {

        let localNotification = UILocalNotification();
        localNotification.alertBody = message
        localNotification.applicationIconBadgeNumber = 1;
        localNotification.soundName = UILocalNotificationDefaultSoundName;

        UIApplication.sharedApplication().presentLocalNotificationNow(localNotification);
    }

    else {

        dispatch_async(dispatch_get_main_queue(), { () -> Void in

            print("Incoming Call")
        })
    }

    NSLog("incoming voip notfication: \(payload.dictionaryPayload)")
}

func pushRegistry(registry: PKPushRegistry!, didInvalidatePushTokenForType type: String!) {

    NSLog("token invalidated")
}

}

EDIT:

Please note, that the above code is an extension of AppDelegate, made only to separate the code, so it gets more readable.

I also added var registry : PKPushRegistry? on the AppDelegate's. In order for this to work, you have to call registerForVoipPush() somewhere in code. In my case, I did it from a button.

PLEASE HELP ME!

Raphael Ayres
  • 864
  • 6
  • 16
  • I'm trying to run your code, but `didRegisterUserNotificationSettings` is never called, I thought I didn't need to setup any app-id and certificates in developer portal just to receive a token, or do I need that to get token? Can I get token from simulator or device only, in development mode? I have PushKit as linked library, and set the background modes, and then copied your code, but the delegate method is never called. I'd like to try help you :) –  Jan 08 '16 at 15:23
  • @Johannes, I have edited my answer to try to clarify things for you. Those are exactly the questions I´m trying to get answered. My guess is that PushKit and normal Push are 2 separate things, so, I don´t need to registerForRemoteNotification in order to use PushKit. I also, did a development profile with Push notifications enabled and some other things but, and I think we need an app-id and a profile correctly configured. But... that is just a guess.. – Raphael Ayres Jan 08 '16 at 22:25
  • Are you able to put a demo of this out on github, I did exactly those things.. I find reported bugs on iOS9 tho, what base sdk have you got? –  Jan 08 '16 at 22:28
  • I´ve got ios 9.2 as base SDK... I cannot have it lowered since I use some libs specially compiled for that SDK – Raphael Ayres Jan 08 '16 at 22:29
  • I see, well, I wonder where I am going wrong, that delegate method never gets called :/ –  Jan 08 '16 at 22:30
  • You also have to import PushKit in order to use it. Are you using simulator or device? – Raphael Ayres Jan 08 '16 at 22:32
  • I import, and I linked the library (under target settings), and I run on iPhone 6 with iOS 9.2 –  Jan 08 '16 at 22:34
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/100197/discussion-between-raphael-ayres-and-johannes). – Raphael Ayres Jan 08 '16 at 22:57

2 Answers2

3

I ran into the same issue and I solved this by adding a classic Push implementation before initiating the VoIP registration... I guess that somehow Apple wants to ensure you firstly ask the user for basic push and get the user's aknowledgment before authorizing you to process further silent voip stuff...

Enable the Push Notifications for your app... enter image description here

Then, this is the whole resulting AppDelegate file, that is quite similar to yours:

import UIKit
import PushKit


@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.

        //Enable all notification type.
        let notificationSettings = UIUserNotificationSettings(forTypes: [UIUserNotificationType.Alert, UIUserNotificationType.Badge, UIUserNotificationType.Sound] , categories: nil)

        //register the notification settings
        application.registerUserNotificationSettings(notificationSettings)


        NSLog("app launched with state \(application.applicationState.stringValue)")

        return true
    }



    func applicationWillResignActive(application: UIApplication) {

    }

    func applicationDidEnterBackground(application: UIApplication) {

    }

    func applicationWillEnterForeground(application: UIApplication) {

    }

    func applicationDidBecomeActive(application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

    func applicationWillTerminate(application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.

        NSLog("app terminated")
    }


}


extension AppDelegate {

    func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {

        //register for voip notifications
        let voipRegistry = PKPushRegistry(queue: dispatch_get_main_queue())
        voipRegistry.desiredPushTypes = Set([PKPushTypeVoIP])
        voipRegistry.delegate = self;

        NSLog("didRegisterUserNotificationSettings")
    }
}

extension AppDelegate: PKPushRegistryDelegate {


    func pushRegistry(registry: PKPushRegistry!, didUpdatePushCredentials credentials: PKPushCredentials!, forType type: String!) {

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

    func pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload payload: PKPushPayload!, forType type: String!) {

        let payloadDict = payload.dictionaryPayload["aps"] as? Dictionary<String, String>
        let message = payloadDict?["alert"]

        //present a local notifcation to visually see when we are recieving a VoIP Notification
        if UIApplication.sharedApplication().applicationState == UIApplicationState.Background {

            let localNotification = UILocalNotification();
            localNotification.alertBody = message
            localNotification.applicationIconBadgeNumber = 1;
            localNotification.soundName = UILocalNotificationDefaultSoundName;

            UIApplication.sharedApplication().presentLocalNotificationNow(localNotification);
        }

        else {

            dispatch_async(dispatch_get_main_queue(), { () -> Void in

                let alert = UIAlertView(title: "VoIP Notification", message: message, delegate: nil, cancelButtonTitle: "Ok");
                alert.show()
            })
        }

        NSLog("incoming voip notfication: \(payload.dictionaryPayload)")
    }

    func pushRegistry(registry: PKPushRegistry!, didInvalidatePushTokenForType type: String!) {

        NSLog("token invalidated")
    }
}

extension UIApplicationState {

    //help to output a string instead of an enum number
    var stringValue : String {
        get {
            switch(self) {
            case .Active:
                return "Active"
            case .Inactive:
                return "Inactive"
            case .Background:
                return "Background"
            }
        }
    }
}

For a bit more information, this is inspired by this post

JBA
  • 2,889
  • 1
  • 21
  • 38
0

As @JBA I forgot to active the push notification. It's look like even you need just the push VOIP you have to active the push (traditional)

douarbou
  • 2,283
  • 1
  • 21
  • 25