iOS local notifications with objective c - ios

I am using local notifications on my app. I handle a notification perfectly when it arrives and you press on it even the app is running even not, and when you open the notification from the "Notifications" of the device. BUT when the app is not running and you open direct the app then the notification screen doesn't open and the badge number is not going to 0. What am I missing?
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UILocalNotification *locationNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (locationNotification) {
UILocalNotification *localNotif =
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
NotificationsViewController *vc = nil;
if (localNotif) {
//base on notification create right view controller
vc = [[NotificationsViewController alloc] init];
NSLog(#"Recieved Notification %#",localNotif);
}
else
{
//create default view controller
vc = [[NotificationsViewController alloc] init];
}
// Add the view controller's view to the window and display.
_window.rootViewController = vc;
[_window makeKeyAndVisible];
// Set icon badge number to zero
application.applicationIconBadgeNumber = 0;
}
return YES;
}
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NotificationsViewController *vc = nil;
vc = [[NotificationsViewController alloc] init];
_window.rootViewController = vc;
// Set icon badge number to zero
application.applicationIconBadgeNumber = 0;
//We take the rootViewController first which is expected to be a UINavigationController in your case
}

if you don't open the app from a notification, the app doesn't know there are notifications
Checking applicationIconBadgeNumber you can know if there is a notification, but you can't get the notification info (message or other data you send)

When your app is not running
if you "didFinishLaunchingWithOptions" will be called not didReceiveLocalNotification, you should track the parameter launchOptions to detect start mode:
If the app start direct from app icon you should not redirect user to notification screen.
If the app start from NSNotification you should redirect user to notification, unschedule notification, update app icon badges etc...
So in this case when you open the app from app icon locationNotification may nil.

Related

Objective-c badge count on app icon when notification received in background?

I'm using the below code to set a badge on my app icon when a notification is received while the app is running in the background. That said, my code/log is never triggered when a notification is received while the app is minimized (see log: "NSLog APP WAS IN BACKGROUND") and I'm not sure why?
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[NSNotificationCenter defaultCenter] postNotificationName:#"pushNotification" object:nil userInfo:userInfo];
NSLog(#"application Active - notication has arrived while app was opened");
completionHandler(UIBackgroundFetchResultNewData);
NSLog(#"Notification received when open");
if(application.applicationState == UIApplicationStateInactive) {
NSLog(#"Inactive - the user has tapped in the notification when app was closed or in background");
completionHandler(UIBackgroundFetchResultNewData);
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UITabBarController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"tabBarController"]; // determine the initial view controller here and instantiate it with [storyboard instantiateViewControllerWithIdentifier:<storyboard id>];
[viewController setSelectedIndex:0];
;
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotificationReceived" object:nil];
}
if (application.applicationState == UIApplicationStateBackground) {
NSLog(#"APP WAS IN BACKGROUND");
static int i=1;
[UIApplication sharedApplication].applicationIconBadgeNumber = i++;
}
}
- (void)applicationDidEnterBackground:(UIApplication *)application{
static int i=0;
[UIApplication sharedApplication].applicationIconBadgeNumber = i;
NSLog(#"Triggered!");
}
This is the correct behaviour for a remote notification.
Your app won't receive the didReceiveRemoteNotification call when your app is in the background, unless the user taps the notification alert.
Here's how it works.
1) When your app is in the background (or suspended) and a remote notification is received, an iOS system alert is shown.
2) If the user opens your app by tapping the notification alert, then your app will move the foreground and didReceiveRemoteNotification will be called.
3) If the user ignores the notification, or dismisses it, then your app remains in the background, and didReceiveRemoteNotification will not be called.
That said, there is no need to set the application badge in code. Your push notification payload, can include a key which iOS uses to set the application badge when the system receives your notification.
You simple include the key badge in your notification's payload:
{
"aps" : {
"alert" : {
"title" : "Notification title",
"body" : "Notification body"
},
"badge" : 5
}
}
I'd suggest you take a look at Apple's documentation for creating a remote notification payload, which explains all the options:
https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification?language=objc
That all being said, it is possible to make sure iOS calls didReceiveRemoteNotification when your app is in the background.
To achieve this, you need to set the content-available parameter in your payload and send a "silent" notification. A silent notification means that the user will never see an alert, but your app will be silently brought to the foreground for a finite amount of time, and didReceiveRemoteNotification will be called.
It's not an appropriate choice for your scenario, though. It's intended for updating an app's content, not just updating the badge.
However, if you're interested in silent notifications, you can see the documentation here:
https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_updates_to_your_app_silently?language=objc
[UIApplication sharedApplication].applicationIconBadgeNumber = 3;

iOS: Display notification message when application opens

I have a textview and an imageview in my app so i can display the notification text and the image that i want to show. i receive this image from a url that i send via the notification. When i send notifications to my application and the app isn't running, the textview and the imageview doesnt show the content, but when the app is running or when the app state is in background the content updates.
I suppose that i have to configure the didFinishLaunchingWithOptions function when the app starts in order to update the fields of the view controller.
how can i do that? and why the didReceiveRemoteNotification function doesnt update the view when the app starts and updates the view only in two states (active & background)?
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSLog(#"New notification received!");
[self.window makeKeyAndVisible];
//Notification recieved & handle the aps
NSDictionary * notificationDict = [userInfo objectForKey:#"aps"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"refreshView" object:notificationDict];
NSString * alertString = [notificationDict objectForKey:#"alert"];
NSString * serverUrl = [notificationDict objectForKey:#"acme"];
NSLog(#"Notification text is: %#", alertString);
[[NSUserDefaults standardUserDefaults] setObject: alertString forKey: #"alertMSG"];
[[NSUserDefaults standardUserDefaults] synchronize];
//handle the notification when the app is active, inactive or in background
if ( application.applicationState == UIApplicationStateActive )
{
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [sb instantiateViewControllerWithIdentifier:#"viewController2"];
[self.window.rootViewController presentViewController:vc animated:YES completion:nil];
application.applicationIconBadgeNumber = 0;
}
else if(application.applicationState == UIApplicationStateInactive)
{
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [sb instantiateViewControllerWithIdentifier:#"viewController2"];
[self.window.rootViewController presentViewController:vc animated:YES completion:nil];
application.applicationIconBadgeNumber = 0;
}
else //ApplicationState Background
{
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [sb instantiateViewControllerWithIdentifier:#"viewController2"];
[self.window.rootViewController presentViewController:vc animated:YES completion:nil];
application.applicationIconBadgeNumber = 0;
}
}
EDIT:
I have configure the didFinishLaunchingWithOptions to starts my controller when the app is launched from a notification, but the content still doesn't update beside the fact that i have signed an observer between the view controller and the app delegate. what am i missing ?
here is the code :
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions{
if (launchOptions != nil)
{
NSDictionary * dictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (dictionary != nil)
{
[self handleRemoteNotification:dictionary];
}
}
}
-(void)handleRemoteNotification:(NSDictionary*)payload{
NSDictionary * notificationDict = [payload objectForKey:#"aps"]; //extract some information from payload
[[NSNotificationCenter defaultCenter] postNotificationName:#"refreshView" object:notificationDict];
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [sb instantiateViewControllerWithIdentifier:#"viewController2"];
[self.window.rootViewController presentViewController:vc animated:YES completion:nil];
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;}
ViewController2.m:
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
//Observer for remote notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refreshView:) name:#"refreshView" object: nil];
notifTextf.delegate = self;
}
//Update View when notification is received
-(void)refreshView:(NSNotification *) notification
{
// Do whatever you like to respond to text changes here.
NSLog(#"observer notification message text is: %#", [notification.object objectForKey:#"alert"]);
NSLog(#"observer notification message url is: %#", [notification.object objectForKey:#"acme"]);
[self.notifTextf setText: [notification.object objectForKey:#"alert"]];
//Download image from Url sended by notification and display it
NSString * imageurl = [notification.object objectForKey:#"acme"];
NSURL *url = [NSURL URLWithString:imageurl];
self.notifImg.image = [UIImage imageWithCIImage:[CIImage imageWithContentsOfURL:url]];
}
why the didReceiveRemoteNotification function doesnt update the view
when the app starts and updates the view only in two states (active &
background)?
If the app is not running when a push notification arrives, the system launches the app and provides the appropriate information in the launch options dictionary. The app does not call application:didReceiveRemoteNotification: method to handle that push notification. Instead, your implementation of the application:willFinishLaunchingWithOptions: or application:didFinishLaunchingWithOptions: method needs to get the push notification payload data and respond appropriately.
Implement the application:didReceiveRemoteNotification:fetchCompletionHandler: method instead whenever possible (iOS 7 and above).
Unlike the application:didReceiveRemoteNotification: method, which is called only when your app is running in the foreground, the system calls the application:didReceiveRemoteNotification:fetchCompletionHandler: method when your app is running in the foreground or background. In addition, if you enabled the remote notifications background mode (to support this mode, include the UIBackgroundModes key with the remote-notification value in your app’s Info.plist file), the system launches your app (or wakes it from the suspended state) and puts it in the background state when a push notification arrives. However, the system does not automatically launch your app if the user has force-quit it. In that situation, the user must relaunch your app or restart the device before the system attempts to launch your app automatically again.
Continue reading...

IOS - get notification and decide what to do

I write some app that sends local notification with delay and when the notification received i want to decide what to do based on device movement.
if device is in move - set the same notification with new delay and stay in background.
if device not in move - pop specific view controller.
i do succeed with the "no drive" mode but when the device is in move - i don't really know hot to handle this situation.
here is my code for now, hope for some help
thanks!
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSLog(#"Notification recieved from background...");
//check if device is in move
CLLocationManager *locationManager = [[CLLocationManager alloc]init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
locationManager.distanceFilter = 50;
[locationManager startMonitoringSignificantLocationChanges];
if (locationManager.location.speed > 10) {
NSLog(#"Device is in drive....");
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:10];//TODO - Debug Set Real Time Before publish
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
NSLog(#"New Notification sent to device");
self.window.rootViewController = nil;
[self.window makeKeyAndVisible];
}
else{
NSLog(#"Device is not in drive....");
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
IGUViewFillDetailsController *vc = (IGUViewFillDetailsController *)[mainStoryboard instantiateViewControllerWithIdentifier:#"IGUViewFillDetailsController"];
vc.dic = notification.userInfo;
UINavigationController *navVC = [[UINavigationController alloc] initWithRootViewController: vc];
self.window.rootViewController = vc;
//[self.window addSubview:vc.view];
[self.window makeKeyAndVisible];
}
}
Problem1:
You scheduled a notification and it will show up at the fire date, you can cancel the notification 1 second before the fire date and schedule it again. Say you have this notification to be fired in 10 seconds, you use a NSTimer that will be fired in 9 second, in the selector of the timer you check if the user is moving or not, if he is moving, you cancel the notification and delay it by 10 seconds and schedule it again, remember you also have to update the timer to do the check before next fire date of new notification.
Problem2:
It is the default behaviour, iOS will take a snap shot before app enter background, and when app resign background, it will show this snap shot, you can add a empty view in applicationDidEnterBackground, then this empty view will show up before the wanted view.

Issue in open view controller when APNS is received in iOS

Hey I'm new to iPhone and I have been trying to use an Apple push notification. Basically, what I want to do is that when user clicks on the received push notification message, then i need to open a specific view controller, it is working for me. I have added custom data with key parameter "type" to my payload JSON.
here is my payload JSON :
{"aps":{"alert":"This is testing message","type":"Notify","badge":1,"sound":"default"}}
here is my code :
#synthesize viewControllerNotify;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[[UIApplication sharedApplication] cancelAllLocalNotifications];
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
screenBounds = [[UIScreen mainScreen] bounds];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
if (launchOptions != nil) {
//Launched from push notification
NSDictionary *userInfo = [launchOptions valueForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"];
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
NSMutableString *notificationType = [apsInfo objectForKey:#"type"];
//For redirect to the view
if([notificationType isEqualToString:#"Notify"]){
//Notify updates
UpdatesViewController *uvc1 = [[UpdatesViewController alloc] initWithNibName:#"UpdatesViewController" bundle:nil];
self.viewControllerNotify = uvc1;
}
else if([notificationType isEqualToString:#"Voting"] || [notificationType isEqualToString:#"QA"]){
//Voting & QA
VotingQAViewController *votingQAViewController = [[VotingQAViewController alloc] initWithNibName:#"VotingQAViewController" bundle:nil];
self.viewControllerNotify = votingQAViewController;
}
else if([notificationType isEqualToString:#"Survey"] || [notificationType isEqualToString:#"Quiz"]){
//Survey & Quizzes
SurveysViewController *surveysViewController = [[SurveysViewController alloc] initWithNibName:#"SurveysViewController" bundle:nil];
self.viewControllerNotify = surveysViewController;
}
UINavigationController *nav=[[UINavigationController alloc]initWithRootViewController:self.viewControllerNotify];
self.window.rootViewController = nav;
[nav setNavigationBarHidden:YES];
}
else{
//Normal Launch
if(screenBounds.size.height == 568) {//iPhone5
splashViewController = [[SplashViewController alloc] initWithNibName:#"SplashViewController_5" bundle:nil];
}
else{
splashViewController = [[SplashViewController alloc] initWithNibName:#"SplashViewController" bundle:nil];
}
UINavigationController *nav=[[UINavigationController alloc]initWithRootViewController:splashViewController];
self.window.rootViewController = nav;
[nav setNavigationBarHidden:YES];
}
[self.window makeKeyAndVisible];
return YES;
}
- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo
{
NSDictionary *aps = (NSDictionary *)[userInfo objectForKey:#"aps"];
int badge = [[aps objectForKey:#"badge"] intValue];
NSMutableString *notificationType = [aps objectForKey:#"type"];
NSLog(#"Number of badge is = %d", badge);
NSLog(#"notification type is = %#", notificationType);
//For redirect to the view
if([notificationType isEqualToString:#"Notify"]){
//Notify updates
UpdatesViewController *uvc1 = [[UpdatesViewController alloc] initWithNibName:#"UpdatesViewController" bundle:nil];
self.viewControllerNotify = uvc1;
}
else if([notificationType isEqualToString:#"Voting"] || [notificationType isEqualToString:#"QA"]){
//Voting & QA
VotingQAViewController *votingQAViewController = [[VotingQAViewController alloc] initWithNibName:#"VotingQAViewController" bundle:nil];
self.viewControllerNotify = votingQAViewController;
}
else if([notificationType isEqualToString:#"Survey"] || [notificationType isEqualToString:#"Quiz"]){
//Survey & Quizzes
SurveysViewController *surveysViewController = [[SurveysViewController alloc] initWithNibName:#"SurveysViewController" bundle:nil];
self.viewControllerNotify = surveysViewController;
}
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UINavigationController *nav=[[UINavigationController alloc]initWithRootViewController:self.viewControllerNotify];
self.window.rootViewController = nav;
[nav setNavigationBarHidden:YES];
[self.window makeKeyAndVisible];
}
My question is: Is this the correct way to redirect to the view controller when push notification is received?
Using the above code, it is redirecting to the view controller class on behalf on Notification type (custom key of notification payload json) successfully in forground and background mode but that redirected View Controller back button is not working. I don't know where i am doing wrong here. If anyone knows then please help me. Thanks.
Better You can post the notification using NSNotificationCentre in didReceiveRemoteNotification and receive the notification on the other classes. So that you can push the required view controller from the current view controller (need not to set as root view controller). Then the back button will works.
For Ex.
[[NSNotificationCenter defaultCenter] postNotificationName:kMesssagePushNotification object:nil userInfo:userInfo];
The back button is not working, because you are setting the target view as a root view. You should instead construct the full navigation stack, i.e. usually, if you have a navigation view controller as a root view controller, you should create that first and then construct all the view controllers that are between the root and your leaf view.
For example, if MainViewController would be your normal start view and the back button from UpdatesViewController should lead to MainViewController you would do:
MainViewController *mainVC = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
UpdatesViewController *updatesVC = [[UpdatesViewController alloc] initWithNibName:#"UpdatesViewController" bundle:nil];
UINavigationController *navVC=[[UINavigationController alloc]initWithRootViewController:mainVC];
[navVC setViewControllers:#[mainVC, updatesVC] animated:NO];
self.window.rootViewController = navVC;
There are two chances that you will access the notification data.
If you receive the notification when your app isn't on, then click the notification and you will get the notification data in the following function:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
use
launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]
to access the notification data and open the view controller you expected.
If you receive the notification when your app is on, but your app can be in background or foreground. If it is the former case, you will receive the notification in notification center, your app will invoke the following function after you click the notification:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
If it is the later case, you app will directly invoke the function before. And you can distinguish them using the flowing code:
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (state == UIApplicationStateBackground || state == UIApplicationStateInactive){
//notification is received when your app is in background
//open the view controller you expected
}else if(state == UIApplicationStateActive){
//notification is received when your app is in foreground
//do nothing
}
sorry for my pool English, hope it helps~

Opening Local Notification Opens to Specific View

When user clicks the notification, I want the app to open up to the third tab on my tab bar controller. I can get the notification to fire, but not the opening up right part. In my app's didFinishLaunchingWithOptions I have:
UILocalNotification *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif) {
tabBarController.selectedIndex = 3;
}
And then in AppDelegate as well, I have:
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification {
application.applicationIconBadgeNumber = 0;
NSString *reminderText = [notification.userInfo
objectForKey:kRemindMeNotificationDataKey];
[viewController showReminder:reminderText];
tabBarController.selectedIndex = 3;
}
However, clicking the notification just opens it up, like normal, to the first tab.
Here's an example. And also the third tab would be tabBarController.selectedIndex = 2 since the count starts from 0.

Resources