Ive got the following code in my app delegate to display the text contained in a Push notification within a UIAlertView.
The code works fine while the app is still loaded in the background, however, once the app goes out of the background, the notification doesn't appear.
Can somebody much cleverer than me show me where I am going wrong, and what I need to do to fix it?
app Delegate.m:
#synthesize pushText;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[Parse setApplicationId:#"hGG5RdgNVdI7eCeZynV32lWYXywQRHkpp5zLdY7Q"
clientKey:#"TwmNbpBYEt4u3euE3lzNIgwyroSl8RPGF2dJFsPv"];
// Register for Push Notitications, if running iOS 8
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)]) {
UIUserNotificationType userNotificationTypes = (UIUserNotificationTypeAlert |
UIUserNotificationTypeBadge |
UIUserNotificationTypeSound);
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes
categories:nil];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
} else {
// Register for Push Notifications before iOS 8
[application registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeAlert |
UIRemoteNotificationTypeSound)];
}
return YES;
NSMutableArray *alertTextArray = [[NSUserDefaults standardUserDefaults] objectForKey:#"alertTextArray"];
NSDictionary *notifKey = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if (notifKey)
{
[alertTextArray addObject:[notifKey objectForKey:#"alert"]];
}
[[NSUserDefaults standardUserDefaults] setObject:alertTextArray forKey:#"alertTextArray"];
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (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.
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// 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.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
if (currentInstallation.badge != 0) {
currentInstallation.badge = 0;
[currentInstallation saveEventually];
}
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// Store the deviceToken in the current installation and save it to Parse.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:deviceToken];
[currentInstallation saveInBackground];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[PFPush handlePush:userInfo];
pushText = [[userInfo objectForKey:#"aps"] objectForKey:#"alert"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"News", "")
message:pushText
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles: nil];
[alert show];
}
#end
Do you mean that when the app is in Foreground (= Running), notifications alert does not show ?
This is actually by design and how iOS notifications work.
If the app is running, notifications alert are not shown and it is up to your app to display them in a way that is meaningful for your application.
You get dictionary with push info when app restarts, it's in launch options, check docs.
ps: Copying my comment as answer
Related
I'm new to iOS. I have googled a lot for this question, tried many approaches and finally, I'm here.
In my application, I have to push a notification in the background when a new record is found in the API response. As per Apple documentation, I have implemented performFetchWithCompletionHandler as below:
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
//User Login status from NSUserdefaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL Is_User_Logged_in = [defaults boolForKey:IS_USER_LOGGED_IN];
if(Is_User_Logged_in){
NSLog(#"background fetch started");
//fetchDataFromAPIs method calls 3 API's here
// and if a new record found, inserts into the local sqlitedb
if([self fetchDataFromAPIs])
{
//getUpdatedInfo method checks the new record in the local sqlitedb
// and fires a notification
if([self getUpdatedInfo])
{
NSLog(#"Content Uploaded and pushed notification");
completionHandler(UIBackgroundFetchResultNewData);
[[NSNotificationCenter defaultCenter] postNotificationName:REFRESH_HOME object:self];
NSLog(#"Time: %#", [Constants getCurrentTimeStamp]);
}
else{
completionHandler(UIBackgroundFetchResultNoData);
NSLog(#"Time: %#", [Constants getCurrentTimeStamp]);
}
}
}
}
The above method fires the notification when the app is in the background but when the app is terminated nothing is happening. Fetch time interval is unpredictable.
I have set minimum time interval to perform background fetch in the application delegate method as below:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[self registerForRemoteNotifications];
return YES;
}
How to make performFetchWithCompletionHandler to run in the background for certain regular time intervals without fail?
How to run performFetchWithCompletionHandler when the app is terminated by the user?
Also, I'm running an NSTimer with a regular time interval in the foreground of the app to show the updates as badge number and a notification as shown below:
self.timer = [NSTimer scheduledTimerWithTimeInterval:60.0 target:self selector:#selector(UpdatePages) userInfo:nil repeats:YES];
As I already implemented performFetchWithCompletionHandler in the application delegate, should I skip this NSTimer in the foreground?
Please help to sort this. Many thanks in advance.
I looked a lot questions and answers for the same issue.
And What I know now is that
'didReceiveRemoteNotification' is not called when the app is background.
didReceiveRemoteNotification is just called when app is foreground or when user comes to app by tapping notification if its in background.
application:didFinishLaunchingWithOptions: is called when user taps on notification to open the app if its killed.
My situation is below:
I'm using parse to push and get notification.
I'm successful getting notification when app is foreground and I'm handling data and show alert and list notification at notification list.
But I can not get notification when app is background or killed.
But I check my app can get notification when my app is background or killed when using 'custom audience' of Parse as I want. (Like other famous applications)
What I want to is I want to get notification when app is background or killed like when I use 'custom audience' of Parse.
But when I use just API of Parse , it doesn't work as i want.
Is there anything I'm missing now?
My registering code is below:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
NSLog(#"didReceiveRemoteNotification executed : %#",userInfo);
NSInteger bettingIDX = [[userInfo objectForKey:#"betting_idx"] integerValue];
NSString *message = [userInfo objectForKey:#"message"];
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = [NSString stringWithFormat:#"%#",message];
NSUUID *uuid = [NSUUID UUID];
NSString *notificationKey = [uuid UUIDString];
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
NSLog(#"DeviceToken : %#", deviceToken );
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:deviceToken];
[currentInstallation saveInBackground];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIUserNotificationSettings* notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
[Parse setApplicationId:#"applicationID"
clientKey:#"clientID"];
[PFAnalytics trackAppOpenedWithLaunchOptions:launchOptions];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
}
Use method didFinishLaunchingWithOptions:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
if let launchOptions = launchOptions as? [String: AnyObject],
let userInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] as? [String: String] {
}
return true
}
The official documentation says the following:
The user taps the default button in the alert or taps (or clicks) the
app icon. If the default action button is tapped (on a device running
iOS), the system launches the app and the app calls its delegate’s
application:didFinishLaunchingWithOptions: method, passing in the
notification payload (for remote notifications) or the
local-notification object (for local notifications). Although
application:didFinishLaunchingWithOptions: isn’t the best place to
handle the notification, getting the payload at this point gives you
the opportunity to start the update process before your handler method
is called.
I was wondering if there is a way to swap out the alertview style notification that happens when a user is inside the app when the notification occurs, to the banner notification style (which one would get if another app is active while receiving the push).
I am using the recommended Parse Push Notifications configuration as the following, but am unsure where I could make these changes (or that if its possible) - thanks for your help!
//In didFinishLaunchingWithOptions
[application registerForRemoteNotificationTypes: UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound];
//In appDelegate.m
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)newDeviceToken {
// Store the deviceToken in the current installation and save it to Parse.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:newDeviceToken];
[currentInstallation saveInBackground];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[PFPush handlePush:userInfo];
}
You can't. You need to create and configure your own view and display it (explicitly add and remove it as a subview when you want is on and off screen). This means not calling handlePush: and instead running your own code to display your own view.
My app doesn't receive push notifications when it's not running.
I am trying to handle remote notification sent as JSON and update data in my app using data from given JSON.
All is going well when app is active or in background.
But when app is not running, app is processing notifications only when I open my app by tapping on notification, but not when I open app by tapping on icon.
Here is the code from appDelegate class:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[Parse setApplicationId:appId
clientKey:clKey];
if (application.applicationState != UIApplicationStateBackground) {
BOOL preBackgroundPush = ![application respondsToSelector:#selector(backgroundRefreshStatus)];
BOOL oldPushHandlerOnly = ![self respondsToSelector:#selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)];
BOOL noPushPayload = ![launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (preBackgroundPush || oldPushHandlerOnly || noPushPayload) {
[PFAnalytics trackAppOpenedWithLaunchOptions:launchOptions];
}
}
[application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge|
UIRemoteNotificationTypeAlert|
UIRemoteNotificationTypeSound|
UIRemoteNotificationTypeNewsstandContentAvailability];
NSDictionary *notificationPayload = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
[self processPushNotification:notificationPayload foreground:YES];
return YES;
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
TFLog(#"didRegisterForRemoteNotificationsWithDeviceToken");
// Store the deviceToken in the current installation and save it to Parse.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:deviceToken];
[currentInstallation saveInBackground];
TFLog(#"deviceToken: %#, currentInstallation.badge: %ld", currentInstallation.deviceToken, (long)currentInstallation.badge);
TFLog(#"deviceToken: %#, deviceType: %#", currentInstallation.deviceToken, currentInstallation.deviceType);
TFLog(#"installationId: %#", currentInstallation.installationId);
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
TFLog(#"didFailToRegisterForRemoteNotificationsWithError %#", error);
if (error.code == 3010) {
TFLog(#"Push notifications are not supported in the iOS Simulator.");
} else {
// show some alert or otherwise handle the failure to register.
TFLog(#"application:didFailToRegisterForRemoteNotificationsWithError: %#", error);
}
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
TFLog(#"%#", userInfo);
[PFPush handlePush:userInfo];
[self processPushNotification:userInfo foreground:YES];
[PFAnalytics trackAppOpenedWithRemoteNotificationPayload:userInfo];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
TFLog(#"didReceiveRemoteNotification2");
[self processPushNotification:userInfo foreground:YES];
[PFAnalytics trackAppOpenedWithRemoteNotificationPayload:userInfo];
}
As result, app is receiving remote notification in all states, excepting when it is not running.
What have I missed?
You have missed the bit in the Local and Push Notification Programming Guide where it says -
If the action button is tapped (on a device running iOS), the system
launches the application and the application calls its delegate’s
application:didFinishLaunchingWithOptions: method (if implemented); it
passes in the notification payload (for remote notifications) or the
local-notification object (for local notifications).
If the application icon is tapped on a device running iOS, the
application calls the same method, but furnishes no information about
the notification
Also, this note from Apple -
Important: Delivery of notifications is a “best effort”, not
guaranteed. It is not intended to deliver data to your app, only to
notify the user that there is new data available.
If your application is launched from the app icon rather than the notification you need to check for updated content independent of any push notification that may have been received. This enables an application to behave differently when it opens from a notification and when it is opened from the app icon.
For example, the Facebook app opens directly to the item in the notification when launched from the notification alert but not when launched from the app icon - which is the "correct" behaviour from a user point of view. If I interact with the notification then I am interested in its content. If I launch the app from the icon then I just want to use the app - I can then access the notifications in the app if I want.
There's no way you can get the information about your push notification JSON payload when you launch the app by explicitly tapping the app icon.
That's as per Apple's design. When you open the application from any push notification action, then you can retrieve the push notification in application: didFinishLaunchingWithOptions: delegate method. Typically you look up for the UIApplicationLaunchOptionsRemoteNotificationKey key in your launchOptions dictionary.
But, when you open the app by explicitly tapping the application icon, though still the application: didFinishLaunchingWithOptions: delegate will be called, the UIApplicationLaunchOptionsRemoteNotificationKey key will return nil.
Hi Im currently developing an app where i have push notifications activated. I use parse.com. I have got it working so far that i can send a notification and the device receives it and i also get a badge on the app. But when i open the nothing happens and the badge does not disappear. I've set it in my appdelegate.m so parse is handling the push notifications. Heres some code that im using:
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)newDeviceToken {
// Store the deviceToken in the current installation and save it to Parse.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:newDeviceToken];
[currentInstallation saveInBackground];
}
and also:
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo {
[PFPush handlePush:userInfo];
}
Thanks
Ok thaks but how to i show the content of the push notification in the alert view? Or is that not a problem do i just have to configure a alertview with no text?
There are two scenarios:
App closed
When the app is closed and the user taps on a notification it's like when taps on the app icon. The app starts from application:didFinishLaunchingWithOptions:. To know if the app is opened normal (tap icon) or with a notification, just check the dictionary launchOptions for the key UIApplicationLaunchOptionsRemoteNotificationKey.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSDictionary * pushDictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (pushDictionary)
{
//AlertView
}
}
App opened
When the app is opened there are two possible case
App in background UIApplicationStateInactive
App in foreground UIApplicationStateActive
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
if (application.applicationState == UIApplicationStateInactive)
{
//AlertView
}
else if (application.applicationState == UIApplicationStateActive)
{
// AlertView
}
}
pushDictionary and userInfo are exactly the same dictionary that represents the notification.
Edit 1
A push notification is a JSON file that iOS automatically convert into a NSDictionary. The standard configuration is:
{"aps":
{
"alert":"Text message",
"sound":"default",
"badge":"1" //the number shown on the app's icon
}
}
From this base you can extend the JSON and put inside your own content. For example
{"aps":
{
"alert":"Text message",
"sound":"default",
"badge":"1" //the number shown on the app's icon
},
"Name":"Fry"
}
Now you can retrive the name in this very simple way:
NSString * name = userInfo[#"Name"];
and show it in the alert.
Edit 2
To show the content of push in UIAlertView just do this:
UIAlertView *av = [[UIAlertView alloc] initWithTitle:name
message:name
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[av show];
If you receive push notification while your app isn't active (killed)
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
will not be triggered. Instead of that you will have to use object with key UIApplicationLaunchOptionsRemoteNotificationKey from launchOptions dictionary inside
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions