Ios Push notifications Solution - ios

New to swift coding!
Need some help with Push Notification.
I just implemented the push notification in my app.
I have a Device token id, key id and the software which I am using is Push notification Tester.
My question is once I upload my app on the store will I able to trigger the notification to all the users through the tester app or do I need to write a script for that??
Any help is appreciated!!
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register: \(error)")
}

Related

Two functions to request APNs device token

There are now two functions to request push token, the new one for location pushes (https://developer.apple.com/documentation/corelocation/cllocationmanager/3746837-startmonitoringlocationpushes):
func startMonitoringLocationPushes(completion: ((Data?, Error?) -> Void)? = nil)
and the old one for alerts, backround notifications etc. (https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622958-application):
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
Will both of them return the same device token?

FCM ios configuration

I'm trying to use FCM for push notification . I follow the docs and I'm trying to use :
InstanceID.instanceID().instanceID { (result, error) in
if let error = error {
print("Error fetching remote instange ID: \(error)")
} else if let result = result {
print("Remote instance ID token: \(result.token)")
self.instanceIDTokenMessage.text = "Remote InstanceID token: \(result.token)"
}
}
as specified in the docs but I'm not sure where I should put it, should it be inside didFinishLaunchingWithOptions?
I get this compilation error :
Static member 'instanceID' cannot be used on instance of type
'InstanceID'
Add Observer in didFinishLaunchingWithOptions.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
NotificationCenter.default.addObserver(self, selector: #selector(self.tokenRefreshNotification), name: NSNotification.Name.InstanceIDTokenRefresh, object: nil)
}
Call it inside didRegisterForRemoteNotificationsWithDeviceToken
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
self.connectToFcm()
}
Method
#objc func tokenRefreshNotification(_ notification: Notification) {
self.connectToFcm()
}
Create 1 function.
func connectToFcm() {
InstanceID.instanceID().instanceID { (result, error) in
if let error = error {
print("Error fetching remote instange ID: \(error)")
}
else {
print("FCM Token = \(String(describing: result?.token))")
print("Remote instance ID token: \(result.token)")
self.instanceIDTokenMessage.text = "Remote InstanceID token: \(result.token)"
}
}
}
I am certain that a few people coming through here and experiencing this issue are not having their problem solved by the solution given by Kuldeep.
I had the same exact issue.
Inside didRegisterForRemoteNotificationsWithDeviceToken instead of using the InstanceID.instanceID().instanceID as suggested by the capacitor guide, do this.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidRegisterForRemoteNotificationsWithDeviceToken.name()), object: Messaging.messaging().fcmToken )
}
The only difference from the standard didRegisterForRemoteNotificationsWithDeviceToken is that we are using Messaging.messaging().fcmToken.
Found solution on medium. You would expect this or at least a working version to be on the capacitor docs... It is up to you which guide you follow, I used all of capacitors guide but needed this to test notifications with the fcmToken. The medium guide has more about the fcmToken, so, depending on your use case it may be a good idea to look into this guide.

didRegisterForRemoteNotificationsWithDeviceToken only called if user allows push notifications

Currently the callback didRegisterForRemoteNotificationsWithDeviceToken is only being called when the users clicks 'allow' after UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .alert, .sound]){ (granted, error) in } is called.
I believe there should be a way to always get the users APN token, but currently I am only getting it if the user allows push notifications. Is there a way to always get the APN token even if the user does not allow?
The intention is to user the token for silent notifications even if they did not allow notifications to be displayed
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
firstLaunchCheck()
UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .alert, .sound]){ (granted, error) in }
application.registerForRemoteNotifications()
return true
}
// Called when APNs has assigned the device a unique token
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Convert token to string
let passedTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
//store token in db
print("APNs device token: \(passedTokenString)")
}
Managed to figure this out after hours of frustration. I simply had to enable "Remote notifications" under Background Modes in the capabilities sections of Xcode
Now I get the APN token regardless if user allows push notifications or not

application:didRegisterForRemoteNotificationsWithDeviceToken: not called ios 10.3.2

I want to develop a simple code which use push notifications. I create certificates and provision profiles based on existing tutorials, such as link, and this, but the register function application:didRegisterForRemoteNotificationsWithDeviceToken: is not called at all, thus I can't get device token.
I also turned on Push notification and Remote notification for background in Capabilities tab in xcode. here is my code in AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .alert, .sound]){ (granted, error) in }
UIApplication.shared.registerForRemoteNotifications()
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
print("APNs device token: \(deviceTokenString)")
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("APNs registration failed: \(error)")
}
func application(_ application: UIApplication, didReceiveRemoteNotification data: [AnyHashable : Any]) {
print("Push notification received: \(data)")
}
I am working with an iPhone 5s running iOS 10.3.2
Please call registerForNotifications from didFinishLaunchingWithOptions method in AppDelegate
//MARK:- Methods
func registerForNotifications(){
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options:[.alert,.sound]) { (granted, error) in
if granted{
UIApplication.shared.registerForRemoteNotifications()
}else{
print("Notification permission denied.")
}
}
} else {
// For ios 9 and below
let type: UIUserNotificationType = [.alert,.sound];
let setting = UIUserNotificationSettings(types: type, categories: nil);
UIApplication.shared.registerUserNotificationSettings(setting);
UIApplication.shared.registerForRemoteNotifications()
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = String(format: "%#", deviceToken as CVarArg).trimmingCharacters(in: CharacterSet(charactersIn: "<>")).replacingOccurrences(of: " ", with: "")
print(token)
}
I couldn't find why it wasn't called, but when I changed my test target to an iPhone 7 which is running iOS 10.3.2, it worked fine.
I'm not sure why it is happening, but changing your target may solve the problem

APNS Firebase Notification failed to fetch token

For Swift3 / iOS10 see this link:
ios10, Swift 3 and Firebase Push Notifications (FCM)
I'm trying to use the Firebase for Notifications and I integrated it exactly as described in the docs.
But I don't understand why is doesn't work. When I build my project I see this line:
2016-05-25 16:09:34.987: <FIRInstanceID/WARNING> Failed to fetch default token Error Domain=com.firebase.iid Code=0 "(null)"
This my AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
FIRApp.configure()
FIRDatabase.database().persistenceEnabled = true
var service: DataService = DataService()
service.start()
registerForPushNotifications(application)
application.registerForRemoteNotifications()
return true
}
func registerForPushNotifications(application: UIApplication) {
let notificationSettings = UIUserNotificationSettings(
forTypes: [.Badge, .Sound, .Alert], categories: nil)
application.registerUserNotificationSettings(notificationSettings)
}
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
if notificationSettings.types != .None {
application.registerForRemoteNotifications()
}
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
let tokenChars = UnsafePointer<CChar>(deviceToken.bytes)
var tokenString = ""
for i in 0..<deviceToken.length {
tokenString += String(format: "%02.2hhx", arguments: [tokenChars[i]])
}
FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.Unknown)
print("Device Token:", tokenString)
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
// Print message ID.
print("Message ID: \(userInfo["gcm.message_id"]!)")
// Print full message.
print("%#", userInfo)
}
I too had the same issue and nothing worked for me. But all you have to do is go to your firebase console and then find your project and goto its settings, there check in its cloud messaging tab and upload your .p12 certificate into that.
thats it! happy coding :)
1.Set Notification Observer in didFinishLaunchingWithOptions Method
2.And Set tokenRefreshNotification method then u get Token in this method.
See below Code
import Firebase
import FirebaseMessaging
override func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
FIRApp.configure()
NotificationCenter.default.addObserver(self,
selector: #selector(self.tokenRefreshNotification(notification:)),
name: NSNotification.Name.firInstanceIDTokenRefresh,
object: nil)
}
// NOTE: Need to use this when swizzling is disabled
public func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.Sandbox)
}
func tokenRefreshNotification(notification: NSNotification) {
// NOTE: It can be nil here
let refreshedToken = FIRInstanceID.instanceID().token()
print("InstanceID token: \(refreshedToken)")
connectToFcm()
}
func connectToFcm() {
FIRMessaging.messaging().connectWithCompletion { (error) in
if (error != nil) {
print("Unable to connect with FCM. \(error)")
} else {
print("Connected to FCM.")
}
}
}
public func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
print(userInfo)
}
1 - Have you correctly configured your certificates as specified in the google documentation ( I won't recall the process here, it is quite long... )?
( https://firebase.google.com/docs/cloud-messaging/ios/certs#configure_an_app_id_for_push_notifications )
2 - I've been through some difficulties when setting up FCM. Once I thought everything was ok but notifications were still not working, I've decided to completely remove the app from the phone, clean my build folder and reinstall the whole thing. After that, it was working.
3 - The app was receiving notifications, but I was still getting the "Failed to fetch default token..." message. It disappeared after a while. Don't ask me why!
This is not really a proper answer, I just share my experience because I know configuring notification is not easy and every clue is welcome. So maybe this one can help. Cheers :)
After trying all of the above (and anything I could find elsewhere), what resolves the problem for me is to move
let token = FIRInstanceID.instanceID().token()
to be called when pressing a button, and not on app loading.
I know it's probably not the most elegant solution, but it's good enough for debugging purposes.
Im guessing the token is not available immediately by the server, and takes some time to be generated.
The answers above cover most of the issue, but I had the same issue and I found the following info useful:
Firebase can 'rotate' (change) a user's FCM token at any time. This is the 128 character ID that your server will use to send the push notification to the device.
Firebase docs say best practice is to use a delegate to monitor for changes with the delegate callback method:
- (void)messaging:(nonnull FIRMessaging *)messaging didRefreshRegistrationToken:(nonnull NSString *)fcmToken
[Obj - C]
func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String)
[Swift]
The delegate method should be called on every change, at which point you can update the record in your server.
Unfortunately that wasn't working for me, I had a delegate but the callback wasn't being invoked. So I had to resort to manually updating the token on each app launch (as suggested by #micheal chein above) as follows:
NSString *deviceToken = [FIRInstanceID instanceID].token; // Send this to your server
[Obj-C]
let token = FIRInstanceID.instanceID().token() // Send this to your server
[Swift]
** Important: update the token after a delay (20-25s), as the rotation can sometimes only reflect after some time. You can use a timer for this.
After this I still get the APNS warning/error message:
2017-06-06 09:21:49.520: <FIRInstanceID/WARNING> Failed to fetch APNS token Error Domain=com.firebase.iid Code=1001 "(null)"
BUT, push notifications work every time without fail. So I think that log message is a bit off (possibly mistimed). If you can get option 2 to work for you, definitely do that!
FCM was working for me then just stopped. I did what Rabs G. suggested and removed the app and installed again and the notifications started working again.

Resources