Skip to content

Manage push notification

VTCoco edited this page Jun 19, 2020 · 3 revisions

Apple VoIP certificate

First let's activate Push Notification capability of your Xcode project:

Push Notification Xcode capability

Go on https://developer.apple.com/account/ios/certificate/create to generate the VoIP certificate by clicking on the plus button:

Create VoIP Services Certificate

Then select VoIP Services Certificate inside Production section (this certificate works on both sandbox and production environments).

VoIP Services Certificate

Pick your bundle ID associated to your VoIP certificate.

Choose bundle ID

Follow Apple's instructions to generate a .certSigningRequest file: Keychain Access > Certificate Assistant > Request a Certificate From a Certificate Authority...

Request a Certificate From a Certificate Authority

...enter your user email address, common name and save to disk.

Enter user email address, common name and save to disk

Upload your CSR file and finally download the .cer file. Double click on it, your certificate now appears inside Keychain Access.

Download .cer file

Let's now generate a .p12 file that will be uploaded to the Voxeet developer portal.

Go back to Keychain Access, select your certificate: VoIP Services: YOUR_BUNDLE_ID. Right click on it, export and pick a password.

Export certificate

Go to https://developer.voxeet.com/dashboard > YOUR_APP_NAME and upload your generate .p12 file inside iOS: VoIP APNs P12 from Third party section (don't forget to enter the .p12 file's password).

Upload **.p12* file

The Apple VoIP certificate is now generated and uploaded to Voxeet.

CallKit notifications

CallKit notification CallKit in call

Display the system-calling UI for your app's VoIP services.

CallKit is disabled by default. 'Push Notifications' capability needs to be enabled.

Some events can be used along with callKit to update the UI, such as VTCallKitStarted, VTCallKitSwapped, VTCallKitEnded, VTCallKitMuteToggled.

If CallKitSound.mp3 is overridden, the ringing sound will be replaced by your mp3. Same as IconMask.png if overridden, it will replace the CallKit default image by yours (40x40px).

Example

import VoxeetSDK

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Voxeet SDK initialization.
        VoxeetSDK.shared.initialize(consumerKey: "YOUR_CONSUMER_KEY", consumerSecret: "YOUR_CONSUMER_SECRET")
        VoxeetSDK.shared.notification.push.type = .callKit
        
        return true
    }
}

/*
 *  MARK: - Voxeet VoIP push notifications
 *  To handle VoIP push notifications before iOS 10, you must use this AppDelegate extension:
 */

extension AppDelegate {
    /// Useful below iOS 10.
    func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
        VoxeetSDK.shared.notification.push.application(application, didReceive: notification)
    }
    
    /// Useful below iOS 10.
    func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: @escaping () -> Void) {
        VoxeetSDK.shared.notification.push.application(application, handleActionWithIdentifier: identifier, for: notification, completionHandler: completionHandler)
    }
}

Warning: CallKit is banned in China (China mainland and Hong Kong) and validation of your application may be rejected from the Apple Store Connect. Fortunately VoxeetSDK automatically switches from CallKit to standard notification if China is detected. But you still may have a rejection (Apple’s bot detects "import CallKit" in VoxeetSDK even if it isn't used and returns you a rejected message at the validation proccess...).

As an example of this post on Stack Overflow, you can try to warn Apple that the application embed CallKit but doesn't use it in China: "In this version and onwards, we do not use CallKit features for users in China. We detect the user’s region using NSLocale." from https://stackoverflow.com/questions/50460864/this-app-cannot-be-approved-with-callkit-functionality-active-in-china-please-m.

If it still doesn't work try to directly contact Apple to finally unlock your validation or remove China mainland and Hong Kong from AppStoreConnect > Pricing and Availability.

Standard notifications (deprecated on iOS 13)

Standard notification Standard notification

Standard push notifications can also be used to display an incoming call.

Example

import VoxeetSDK
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Voxeet SDK initialization.
        VoxeetSDK.shared.initialize(consumerKey: "YOUR_CONSUMER_KEY", consumerSecret: "YOUR_CONSUMER_SECRET")
        VoxeetSDK.shared.notification.push.type = .standard
        
        // Ask APNs permission.
        if #available(iOS 10.0, *) {
            UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound], completionHandler: { (granted, error) in })
        } else {
            let settings = UIUserNotificationSettings(types: [.alert, .sound], categories: nil)
            UIApplication.shared.registerUserNotificationSettings(settings)
        }
        
        return true
    }
}

/*
 *  MARK: - Voxeet VoIP push notifications
 *  To handle VoIP push notifications before iOS 10, you must use this AppDelegate extension:
 */

extension AppDelegate {
    /// Useful below iOS 10.
    func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
        VoxeetSDK.shared.notification.push.application(application, didReceive: notification)
    }
    
    /// Useful below iOS 10.
    func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: @escaping () -> Void) {
        VoxeetSDK.shared.notification.push.application(application, handleActionWithIdentifier: identifier, for: notification, completionHandler: completionHandler)
    }
}

To replace localized strings, simply implement these keys / values inside your Localizable.strings files:

VT_NOTIFICATION_TITLE = "Incoming call from _HANDLE_";
VT_NOTIFICATION_BODY = "Conference: _CONFERENCE_ALIAS_";
VT_NOTIFICATION_ANSWER = "Answer";
VT_NOTIFICATION_DECLINE = "Decline";

Custom notifications

VoxeetSDK allows to fully manage your own push notification management.

Example

import VoxeetSDK
import PushKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Voxeet SDK initialization.
        VoxeetSDK.shared.initialize(consumerKey: "YOUR_CONSUMER_KEY", consumerSecret: "YOUR_CONSUMER_SECRET", connectSession: false)
        VoxeetSDK.shared.notification.push.type = .none
        
        // Push notification initialization.
        initPushNotification()
        
        return true
    }
    
    func initPushNotification() {
        // Init PushKit, register to VoIP notification.
        let pushRegistry = PKPushRegistry(queue: DispatchQueue.main)
        pushRegistry.delegate = self
        pushRegistry.desiredPushTypes = [.voIP]
        
        // Init Voxeet observers.
        // Register to VTInvitationReceivedEvent to start a new CallKit incoming call.
        NotificationCenter.default.addObserver(self, selector: #selector(incomingCallEvent), name: .VTInvitationReceivedEvent, object: nil)
        // Register to VTConferenceDestroyedPush to end a call handled by CallKit.
        NotificationCenter.default.addObserver(self, selector: #selector(conferenceDestroyedPush), name: .VTConferenceDestroyedPush, object: nil)
        
        // Set up CallKit / standard notification.
    }
}

/*
 *  MARK: - PKPushRegistryDelegate
 */

extension AppDelegate: PKPushRegistryDelegate {
    func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
        // Important: pass your push token to Voxeet in order to have custom push notifications.
        let pushToken = pushCredentials.token.map{ String(format: "%02x", $0) }.joined()
        VoxeetSDK.shared.session.pushToken = pushToken
    }
    
    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {        
        pushRegistry(registry, didReceiveIncomingPushWith: payload, for: type, completion: {})
    }
    
    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
        guard type == .voIP else { return }
        
        // Get conference information.
        guard let confID = payload.dictionaryPayload["ConfId"] as? String,
            let confAlias = payload.dictionaryPayload["ConfAlias"] as? String else {
                return
        }
        let notifType = payload.dictionaryPayload["NotifType"] as? String
        
        if notifType == "1" { /* Check if notification type is an incoming call */
            // Show CallKit or standard notification.
            completion()
        } else if notifType == "5" { /* Check if notification type is a destroy conference */
            // Hide CallKit or standard notification.
            completion()
        }
    }
    
    func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {}
}

/*
 *  MARK: - Voxeet push ups (NotificationCenter)
 */

extension AppDelegate {
    @objc func incomingCallEvent(notification: Notification) {
        // Show CallKit or standard notification.
    }
    
    @objc func conferenceDestroyedPush(notification: Notification) {
        // Hide CallKit or standard notification.
    }
}