Facebook Analytics Push on iOS only works occasionally - ios

I have been battling with this for a while now but I've finally succumbed to submitting a question to see if anyone else has had an issue.
Essentially I'm trying to use the Facebook Analytics push service and only seem to be receiving push notifications some of the time. I've been intercepting the API calls using charles proxy to ensure that push tokens are being sent correctly by the Facebook SDK and events are being logged as expected. From this perspective everything is fine.
Next I use the 'Push campaign setup verification' on the settings page to try and send push and in-app push tests, and these also work fine (cards are displayed correctly on the devices etc.) on the 3 test devices I'm using.
The problem arrises when trying to create a campaign to send pushes to a certain segment of the devices. The first device I tried with worked with no issues at all, but then subsequent devices (with no changes to the codebase at all) wouldn't receive any pushes from any campaigns that I set up. My target audience is 'Device OS is iOS' so I would expect that all iOS devices would receive the push from the campaign. This seemed a bit odd, so I deleted the app off all the devices and rebuilt them again (ensuring that the test push worked on all devices) and setup the campaign again, but this time even the first device no longer worked. In the events debugger I get a 'Push Notification Error' and an error_message of 'InvalidDeviceOS' which makes no sense at all.
I've been going back and fourth with this for a few hours now with no success. I can see the tokens being sent to Facebook, and I can see the events being logged in the event debugger, I can use the push test service in settings and all devices receive pushes without an issue, but as soon as I try to use a campaign I get nothing.
For completion purposes here are the snippets of code I'm using:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// ...
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil))
// ...
}
func application(application: UIApplication, didRegisterUserNotificationSettings settings: UIUserNotificationSettings) {
UIApplication.sharedApplication().registerForRemoteNotifications()
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
FBSDKAppEvents.setPushNotificationsDeviceToken(deviceToken)
}
Finally here is the events debugger after opening a fresh install on 3 different devices (see the first launch events, 2 app launches have ID's and 1 doesn't as it's not logged in yet).
Any help with this would be great, thanks.
UPDATE: I created a brand new app and a brand new Facebook analytics instance, new push certificates etc. Initially I started receiving pushes from campaigns, but once I deleted and reinstalled the app the campaign messaging started to fail again. I then installed it onto a different device and cloned the existing campaign and tried again. This then resulted in the original device getting the push but the new device didn't... the mind boggles!
Now I don't know whats going on. On the face of it the Facebook service seems to get flaky once a push error comes back from APNS, but it's really difficult to prove that assumption since the error reporting in the Event Debugger is next to useless.
Again for completeness here is the code from the new app:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
application.registerUserNotificationSettings(UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil))
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
FBSDKAppEvents.activateApp()
}
// MARK: Notification Methods
func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
application.registerForRemoteNotifications()
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
FBSDKAppEvents.setPushNotificationsDeviceToken(deviceToken)
FBSDKAppEvents.setUserID(NSUUID().uuidString)
var token: String = ""
for i in 0 ..< deviceToken.count {
token += String(format: "%02.2hhx", deviceToken[i] as CVarArg)
}
print("Token: \(token)")
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if FBNotificationsManager.shared().canPresentPushCard(fromRemoteNotificationPayload: userInfo) {
FBNotificationsManager.shared().presentPushCard(forRemoteNotificationPayload: userInfo, from: nil, completion: nil)
} else {
print("Unknown Payload")
}
completionHandler(.newData)
}
UPDATE 2: After hours of experimenting and digging we came across this bug report on Facebook's support pages which seems to confirm this is not an isolated issue: https://developers.facebook.com/bugs/118059088679219/

Related

Push notification cheat inside app

I want to create cheat for the tester to check the push notification. I have a working example from a web link and it's working. I hit the same web-link inside the app and received the success response but no notification. The Same thing is working from a web browser.
eg. I have API suppose www.example.com/send/notif/44 to send the notification to my iPhone 6 and I am calling it from my app dashboard screen notify button.
Please help.
You have to initialize 3 methods to receive remote notifications.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
<#code#>
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
<#code#>
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
<#code#>
}
If app is in foreground, it still receives push notification but not any banner is displayed by the OS. So, a tester should go to background immediately when they push that test button.

Push notification not getting in back ground state through GCM in ios

I have integrated push notification through GCM everything is working fine. But I am not getting notification message and sound. And the function didReceiveNotification: called in app delegate. And also not getting in background state.
Before making any comment or downvote consider following things.
I assume you have configured App Identifier in Developer portal, if not visit Apple Developer center
You have generated required provisional Profile & Certificate from Apple Developer Portal. If not visit App Distribution Guide
Make sure you have configured your bundle identifier correctly as defined in Apple Developer portal.
Following answer guides to configure APNS using your custom backend to send Push Notifications not for FireBase/GCM. To configure it using Firebase or GCM(As Firebase Cloud Messaging (FCM) is the new version of GCM) follow Google documentation
If all the above things are configured correctly then follow below steps:
Step 1: Register for APNS with Appropriate settings in didFinishLaunchingWithOptions inside AppDelegate file
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let notificationTypes: UIUserNotificationType = [UIUserNotificationType.Alert, UIUserNotificationType.Badge, UIUserNotificationType.Sound]
let pushNotificationSettings = UIUserNotificationSettings(forTypes: notificationTypes, categories: nil)
application.registerUserNotificationSettings(pushNotificationSettings)
application.registerForRemoteNotifications()
return true
}
Step 2: Add delegate methods to handle success or failure for APNS registration by adding following delegate methods
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
// Convert binary Device Token to a String (and remove the <,> and white space charaters).
var deviceTokenStr = deviceToken.description.stringByReplacingOccurrencesOfString(">", withString: "", options: nil, range: nil)
deviceTokenStr = deviceTokenStr.stringByReplacingOccurrencesOfString("<", withString: "", options: nil, range: nil)
deviceTokenStr = deviceTokenStr.stringByReplacingOccurrencesOfString(" ", withString: "", options: nil, range: nil)
print(deviceTokenStr);
// *** Store device token in your backend server to send Push Notification ***
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
print(error)
}
Step 3: Now you have configured your APNS on device end, You can fire Push Notification from your server/backend, When Push Notification is received following method will be called when your app is in Foreground. Implement it into AppDelegate.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
print(userInfo)
}
To handle Push Notification while your application is in background (but not killed by removing from multitask) you need to take care of following things.
Make sure you have enabled Background Modes in Project Navigation->Targets->Capabilities->Turn on Background Modes and select Remote Notifications.
Now implement following method to handle Push Notification while in background. Make sure you handle UIBackgroundFetchResult properly.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
}
Note: If func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) method is implemented func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) method will not be called.
Read more about APNS in Apple Documentation.
Usually, iOS apps can receive push notifications via APNS not GCM and could not get any data when app is in background state. If iOS app gets push notification via APNS and it is in background state, the push notifications just shown in notification center & top of the screen with app's icon. If you see the notification, there's no problem with the server.
And there's no data arrived when app is in the background state, you should make your server api for the notifications data when the app is back on foreground state.

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.

CloudKit CKSubscription without notifications?

I'm writing a Swift app with CloudKit. When a record is modified in CloudKit by a device, I want the corresponding records to be updated in the local storage of the other devices without displaying a push notification.
Do I need to call registerUserNotificationSettings in didFinishLaunchingWithOptions (meaning that the user has to accept the notifications for my app) even if I don't plan to display any push notification?
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: .Alert, categories: nil))
In this case you do not need to call registerUserNotificationSettings.
You need to add the Info.plist setting "Required background mode" (UIBackgroundModes), "App downloads content in response to push notifications" (remote-notification). And also call registerForRemoteNotifications. Finally, set notificationInfo.shouldSendContentAvailable = YES; on your subscription.
Now since your app is being run to respond to all notifications you need to be careful to handle the case where a notification is missed, you can use airplane mode to test that, only the last is delivered.
Note, once you have created your subscription from any device, application:didReceiveRemoteNotification:fetchCompletionHandler: will be called on all devices that are using the same iCloud account and have the app installed.
Yes you do need to call registerUserNotificationSettings even all you need is background remote notification. So user will be prompt for notifications permission. It makes no sense as users will not be seeing the notifications but that's how it is.
I use this to set it up:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let settings = UIUserNotificationSettings(forTypes: .None , categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
}
Make sure when you call CloudKit saveSubscription you provide shouldSendContentAvailable = true. The following code is for subscription for a custom zone:
let subscription = CKSubscription(zoneID:zoneID, options: CKSubscriptionOptions(rawValue: 0))
let notificationInfo = CKNotificationInfo()
notificationInfo.shouldSendContentAvailable = true
subscription.notificationInfo = notificationInfo
CKContainer.defaultContainer().privateCloudDatabase.saveSubscription(subscription) { subscription, error in
}
You also need to enable Background Modes capability under Xcode for your project, and tick the box Remote Notifications.
User can go to Settings app to disable notifications for your app. But you will still receive remote notification trigger by CloudKit server.
Implement the following functions in your AppDelegate to receive remote notifications:
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {}

Parse: No Registered Devices Error

I have set up Parse.com's Push feature as follows, but I run into an issue when I try to send push notifications:
Certificates have been set up, signed, uploaded, etc.
SDK has been imported as per the instructions here
AppDelegate.swift has been edited to include the following:
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
Parse.setApplicationId("MY KEY. I KNOW IT IS CORRECT", clientKey: "MY CLIENT KEY. ALSO VERIFIED TO BE CORRECT")
let userNotificationTypes = (UIUserNotificationType.Alert |
UIUserNotificationType.Badge |
UIUserNotificationType.Sound);
let settings = UIUserNotificationSettings(forTypes: userNotificationTypes, categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
return true
}
func application(application: UIApplication!, didReceiveRemoteNotification userInfo:NSDictionary!) {
PFPush.handlePush(userInfo)
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
let installation = PFInstallation.currentInstallation()
installation.setDeviceTokenFromData(deviceToken)
installation.saveInBackground()
}
However, when I try to send a push notification by selecting my app in the Parse dashboard, selecting "Push", and selecting " + Sends A Push", I get an error
No push notifications to display yet
You may need to configure push notifications for your app.
I had this same issue, and hammered my head against the wall for a while trying to figure it out until I realized that when I went to the Push Notifications tab in Parse, it somehow switched me into another earlier app that I had set up. Make sure you have the right app selected in the upper left corner. Hopefully your solution is as simple as mine was.

Resources