We have a VoIP app where we are currently using standard push notifications. We would like to update to using PushKit and VoIP push notifications. I'm a bit unsure how to migrate from our current standard APNS setup to the new. Questions:
1) Will our current APNS production certificate be able to send push messages to new VoIP clients?
2) Will our new VoIP push certificate be able to send push messages to existing, standard APNS apps (tokens)?
Please do refer pushkit demo https://github.com/hasyapanchasara/PushKit_SilentPushNotification
Objective c demo is also there https://github.com/hasyapanchasara/PushKit_SilentPushNotification/tree/master/Objective%20C%20Demo/PushKitDemoObjectiveC
Below is swift code to register for push kit and receive pushkit payload.
import UIKit
import PushKit
class AppDelegate: UIResponder, UIApplicationDelegate,PKPushRegistryDelegate{
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let types: UIRemoteNotificationType = [.Alert, .Badge, .Sound]
application.registerForRemoteNotificationTypes(types)
self. PushKitRegistration()
return true
}
//MARK: - PushKitRegistration
func PushKitRegistration()
{
let mainQueue = dispatch_get_main_queue()
// Create a push registry object
if #available(iOS 8.0, *) {
let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)
// Set the registry's delegate to self
voipRegistry.delegate = self
// Set the push type to VoIP
voipRegistry.desiredPushTypes = [PKPushTypeVoIP]
} else {
// Fallback on earlier versions
}
}
#available(iOS 8.0, *)
func pushRegistry(registry: PKPushRegistry!, didUpdatePushCredentials credentials: PKPushCredentials!, forType type: String!) {
// Register VoIP push token (a property of PKPushCredentials) with server
let hexString : String = UnsafeBufferPointer<UInt8>(start: UnsafePointer(credentials.token.bytes),
count: credentials.token.length).map { String(format: "%02x", $0) }.joinWithSeparator("")
print(hexString)
}
#available(iOS 8.0, *)
func pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload payload: PKPushPayload!, forType type: String!) {
// Process the received push
// From here you have to schedule your local notification
}
}
When your app is based on VOIP, then once you receive pushkit payload then you can schedule local notification. As per interactive local notification you can receive, disconnect etc feature can process ( Same like Whatsapp, Skype etc ).
1) Will our current APNS production certificate be able to send push messages to new VoIP clients?
No, you have to create pem file and configure at back end as well.
2) Will our new VoIP push certificate be able to send push messages to existing, standard APNS apps (tokens)?
No, Pushkit tokens will be different from APNS tokens.
List item
Refer more for Debug, Certificate, Local notification
https://github.com/hasyapanchasara/PushKit_SilentPushNotification
Related
I have a project which use VoiP pushes for calling and APNS pushes for simple notifications. APNS push comes, but VoiP doesn't come. When server tries to send me VoiP push by my VoiP token server throws exception "Invalid Token". Please, see my solution and let me know what I do worst.
I created two certificates (APNS and VoiP)
I added certificate to identify, but I can add just APNS
Next, I generated p12 keys and send to server for using them.
In UIApplicationDelegate I retrieve APNS token and send to server
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
//next send APNS token
}
APNS token I receive successful here
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void)
VoiP push notification I register at first
func registerForPushVoipNotifications() {
let voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
voipRegistry.delegate = self
voipRegistry.desiredPushTypes = [.voIP]
}
Here I receive VoiP push token
public func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
let token:String? = credentials.token.map { String(format: "%02x", $0) }.joined()
//send VoiP token to server
}
By documentation sending VoiP push I must to receive here
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: #escaping () -> Void)
But when server sends VoiP push it gets error Invalid Token. What do I worst?
Firstly, you should use p8 authKey to send push notifications
Secondly, PKPushRegistry is stored in the func? This is strange. Move it to field of your class
Thirdly, try converting the token from data by the following code:
class TokenConverter {
static func token(from data: Data) -> String {
let tokenParts = data.map { data -> String in
return String(format: "%02.2hhx", data)
}
return tokenParts.joined()
}
}
I am attempting to send a simple push notification from the firebase notification console to a specific device using an FCM token. The firebase notification console shows the notification as sent but the device does not receive it. I have tried sending the notification and then waiting to see if the console logs from didReceiveRemoteNotification, but the notification takes too long (hours) to be shown as sent in the firebase console (even when I set the priority to high).
App Delegate
import UIKit
import Firebase
import FirebaseStorage
import FirebaseDatabase
import FirebaseMessaging
import CoreData
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Use Firebase library to configure APIs
FirebaseApp.configure()
/////
// For Firebase Cloud Messaging (FCM)
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
// End of [for Firebase Cloud Messaging (FCM)]
/////
return true
}
///////////////////////
// FCM Setup
// Monitor token generation for FCM: Be notified whenever the FCM token is updated
func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
}
// Monitor token generation for FCM:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
} // Handle messages received through the FCM APNs interface
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
print("didReceiveRemoteNotification")
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
// gcm_message_id
if let messageID = userInfo["gcmMessageIDKey"] {
print("Message ID: \(messageID)")
}
^My guess is that the issue may have to do with the "gcm_message_id"/"gcmMessageId"/"gcm.message_id" as it is different in each of the three approaches I tried below
// Print full message.
print(userInfo)
}
// Handle messages received through the FCM APNs interface
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("didReceiveRemoteNotification (withCompletionHandeler)")
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo["gcmMessageIDKey"] {
print("Message ID: \(messageID)")
}
^My guess is that the issue may have to do with the "gcm_message_id"/"gcmMessageId"/"gcm.message_id" as it is different in each of the three approaches I tried below
// Print full message.
print(userInfo)
completionHandler(UIBackgroundFetchResult.newData)
}
// End of [FCM Setup]
///////////////////////
}
View Controller
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Retrieve the current registration token for Firebase Cloud Messaging (FCM)
let token = Messaging.messaging().fcmToken
print("FCM token: \(token ?? "")")
}
}
Entitlements & Enabling Push Notifications
I have added the push entitlements and enabled background modes for push notifications and added the GoogleService-Info.plist to my project.
Method for sending Notification
I am creating notification from the Firebase Notifications console (as shown below) so there should be no issue with the structure of the notification itself.
I have tried the following approaches to remedy the issue, but all have produced the same result:
Google Firebase Documentation
Remote Notifications w/ firebase tutorial
Google Firebase Quickstart
Does anyone know why the notification is being marked as sent in the firebase notification console but not showing up on the device?
A couple of troubleshooting steps I use when working with push notifications are:
Get push working independent of the firebase services first. I use this tool.
Make sure that you dont have any bundle identifier namespace collisions. So for example having any combination of appstore build, testflight build, and / or develop build of an app on the device. Delete all but one instance of the app. The bundle identifier is how your device knows which app to route the push notification to.
When all else fails - I try to isolate the issue by building a new sample project and hook it up to a new firebase project and see if I can narrow my focus down to just being able to get push working without any other business logic in my app. This helps me prove to my self that I haven't gone insane, and proves to me that it's not some mysterious network condition leading to my woes.
I hope this helps you as you work get it all figured out.
Try to add the following code to didRegisterForRemoteNotificationsWithDeviceToken func:
Messaging.messaging().apnsToken = deviceToken
So it will look like this:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
}
It is work for me.
Does anyone know why the notification is being marked as sent in the firebase notification console but not showing up on the device?
Because "sent" does not mean "received".
Notifications cannot be guaranteed to be received on the device. With basic APNS infrastructure you even cannot get the information if a notifications was received or processed on the device.
If you don't receive a successfully sent message on the device there can be many reasons. Furthermore, even if you receive a Firebase token, that does not mean that your device can receive the notification in any case.
To isolate the problem I would suggest to build up the minimal setup and use APNS without Firebase. You could use Terminal or NWPusher (https://github.com/noodlewerk/NWPusher) for sending notifications from your local macOS system and the iOS native remote push notifications framework for receiving notifications.
Keep care to convert the APNS device token to the correct format required for submitting a notification:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.hexEncodedString()
print("Token: \(token)")
}
Data extension:
extension Data {
func hexEncodedString() -> String {
return map { String(format: "%02hhx", $0) }.joined()
}
}
I see that you have done everything in your project's Capabilities.
Some points:
Conform your class to messaging delegate like so:
Messaging.messaging().delegate = self
Make sure you've setup your certificates properly and uploaded everything on Firebase Push Notification Configurations (not exact term).
I've done several applications that uses Firebase Push Notification Service and making a sample app from scratch might help you figure out what you've been doing wrong.
And... here's a nicer code block for registering your application for push notification.
// Setup Push Notifications
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in
if error == nil{
DispatchQueue.main.async(execute: {
application.registerForRemoteNotifications()
})
}
}
}
else {
let notificationTypes: UIUserNotificationType = [.sound, .alert, .badge]
let notificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)
application.registerForRemoteNotifications()
application.registerUserNotificationSettings(notificationSettings)
}
I'm searching informations about push notifications for VoIP push notifications. Some point remains unclear to me :
1) If the user has not the application opened, and then he receives a call. Is there a mean to launch the applications from the notification ?
2) How does the application wait for a specific event ? How does my device would know he receive a call from someone for example ?
3) I use the Appdelegate file from https://github.com/Hitman666/ios-voip-push but in my case it doesn't work (many errors), here is a preview of the error i get.
Thanks
1) If the user has not opened the application, and then he receives a call. Is there a mean to launch the applications from the notification ?
- First time user has to tap on app icon and open it then only device id will get register to receive push kit payload. then no need to open app. It will also work when app is in killed state and you have to schedule local notification as per push kit payload.
2) How does the application wait for a specific event ? How does my device would know he receive a call from someone for example ?
- You have to schedule local notification as per push kit payload.
3) I use the Appdelegate file from https://github.com/Hitman666/ios-voip-push but in my case it doesn't work (many errors), here is a preview of the error i get.
- Refer below code
import UIKit
import PushKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,PKPushRegistryDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let types: UIRemoteNotificationType = [.Alert, .Badge, .Sound]
application.registerForRemoteNotificationTypes(types)
self.PushKitRegistration()
return true
}
//MARK: - PushKitRegistration
func PushKitRegistration()
{
let mainQueue = dispatch_get_main_queue()
// Create a push registry object
if #available(iOS 8.0, *) {
let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)
// Set the registry's delegate to self
voipRegistry.delegate = self
// Set the push type to VoIP
voipRegistry.desiredPushTypes = [PKPushTypeVoIP]
} else {
// Fallback on earlier versions
}
}
#available(iOS 8.0, *)
func pushRegistry(registry: PKPushRegistry!, didUpdatePushCredentials credentials: PKPushCredentials!, forType type: String!) {
// Register VoIP push token (a property of PKPushCredentials) with server
let hexString : String = UnsafeBufferPointer<UInt8>(start: UnsafePointer(credentials.token.bytes),
count: credentials.token.length).map { String(format: "%02x", $0) }.joinWithSeparator("")
print(hexString)
}
#available(iOS 8.0, *)
func pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload payload: PKPushPayload!, forType type: String!) {
// Process the received push
}
}
Get some more information about how to integrate pushkit for VOIP based app.
Just updated with Swift 4.0 code as well.
https://github.com/hasyapanchasara/PushKit_SilentPushNotification
In my XCode project, i have used setKeepAliveTimeout method in applicationDidEnterBackground method like below code.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
[self performSelectorOnMainThread:#selector(keepAlive) withObject:nil waitUntilDone:YES];
[application setKeepAliveTimeout:600 handler: ^{
[self performSelectorOnMainThread:#selector(keepAlive) withObject:nil waitUntilDone:YES];
}];
}
It shows that setKeepAliveTimeout method is deprecated and they wants to use UIRemoteNotificationTypeVoip method.
I searched for UIRemoteNotificationTypeVoip method, but not enough results are given. Even developer.apple.com doesn't have documentation for that method.
Problem: How to change UIRemoteNotificationTypeVoip where setKeepAliveTimeout is used?
If anyone knows, then give me an answer.
Thanks in Advance!
Use below structure to achieve your task.
Some reference
https://github.com/hasyapanchasara/PushKit_SilentPushNotification
Use this simplepush.php file
Use below commands to create pem file and use it in above code.
After that go to simplepush.php location and fire command -> php simplepush.php
This way you can test your push kit notification setup architecture.
https://zeropush.com/guide/guide-to-pushkit-and-voip
https://www.raywenderlich.com/123862/push-notifications-tutorial
Download
import UIKit
import PushKit
class AppDelegate: UIResponder, UIApplicationDelegate,PKPushRegistryDelegate{
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let types: UIRemoteNotificationType = [.Alert, .Badge, .Sound]
application.registerForRemoteNotificationTypes(types)
self. PushKitRegistration()
return true
}
//MARK: - PushKitRegistration
func PushKitRegistration()
{
let mainQueue = dispatch_get_main_queue()
// Create a push registry object
if #available(iOS 8.0, *) {
let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)
// Set the registry's delegate to self
voipRegistry.delegate = self
// Set the push type to VoIP
voipRegistry.desiredPushTypes = [PKPushTypeVoIP]
} else {
// Fallback on earlier versions
}
}
#available(iOS 8.0, *)
func pushRegistry(registry: PKPushRegistry!, didUpdatePushCredentials credentials: PKPushCredentials!, forType type: String!) {
// Register VoIP push token (a property of PKPushCredentials) with server
let hexString : String = UnsafeBufferPointer<UInt8>(start: UnsafePointer(credentials.token.bytes),
count: credentials.token.length).map { String(format: "%02x", $0) }.joinWithSeparator("")
print(hexString)
}
#available(iOS 8.0, *)
func pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload payload: PKPushPayload!, forType type: String!) {
// Process the received push
// From here you have to schedule your local notification
}
}
Through pushkit payload you can schedule local notification. when pushkit payload comes your app will active in background or terminated state upto your local notification sound plays. you can also keep details in NSUserDefault. so on interactive local notification or didfinishlaunching with option you can do whatever you want.
So our app makes use of UILocalNotifications which are scheduled for every morning at 9am. The content of this message is dependant on an API call which I want to make 5 minutes before the notification is scheduled for.
So for example, the notification might be set to remind the user to do task x, but there is the chance that the user has already task x (via the online portal and not app), in which case we want to tell them to do task Y instead. For various reasons we don't have a push notification server set up yet but will soon, so this is an interim solution fo rhte purpose of testing.
So my question is: How can I schedule an API call to be made which replaces the immiment notification message depending on the response, even if the app is in the background, or even closed?
Thanks!
You have to use silent push notification. in payload you can have get various information from server. then schedule UILocalNotification at 9:00 AM.
When you are using silent push notification your app will invoke in background / terminated state ( Note - would not come foreground ) upto your UILocalNotification sound file plays ( max 30 seconds ), in this 30 seconds you can do API related work.
When UILocalNotification display in notification center, For example there are 2 buttons with notification "Accepted task" and "Later". So when you tap on though buttons even your app will invoke in background / terminated state, even you can open app and redirect to specific view controller. with both case you can do API related work.
If you keep your UILocalNotification object in NSUserDefault then you can also retrieve it on didFinishLaunchingWithOption, in case your device is restarted and UILocalNotification and further API work is very crucial.
Let me know you need any help for silent push notification, UILocalNotification, didFinishLaunchingWithOption or anything.
Referral note
https://www.raywenderlich.com/123862/push-notifications-tutorial
https://www.sinch.com/tutorials/ios8-apps-and-pushkit/
https://developer.apple.com/reference/pushkit
Source https://github.com/hasyapanchasara/PushKit_SilentPushNotification
Push kit implementation code
import UIKit
import PushKit
class AppDelegate: UIResponder, UIApplicationDelegate,PKPushRegistryDelegate{
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let types: UIRemoteNotificationType = [.Alert, .Badge, .Sound]
application.registerForRemoteNotificationTypes(types)
self. PushKitRegistration()
return true
}
//MARK: - PushKitRegistration
func PushKitRegistration()
{
let mainQueue = dispatch_get_main_queue()
// Create a push registry object
if #available(iOS 8.0, *) {
let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)
// Set the registry's delegate to self
voipRegistry.delegate = self
// Set the push type to VoIP
voipRegistry.desiredPushTypes = [PKPushTypeVoIP]
} else {
// Fallback on earlier versions
}
}
#available(iOS 8.0, *)
func pushRegistry(registry: PKPushRegistry!, didUpdatePushCredentials credentials: PKPushCredentials!, forType type: String!) {
// Register VoIP push token (a property of PKPushCredentials) with server
let hexString : String = UnsafeBufferPointer<UInt8>(start: UnsafePointer(credentials.token.bytes),
count: credentials.token.length).map { String(format: "%02x", $0) }.joinWithSeparator("")
print(hexString)
}
#available(iOS 8.0, *)
func pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload payload: PKPushPayload!, forType type: String!) {
// Process the received push
// From here you can schedule UILocalNotification
}
}