The first time that a user accesses my app, they are asked whether they want to allow push notifications. At the same time, I have some animations going on. The push notification question is obscuring these animations.
I'm wondering if anyone knows how to determine whether this alert view is being displayed. If I can determine when it gets dismissed (regardless of what the user chooses), I could delay the start of the animations until this event occurs. Despite much research, I am at a loss as to whether this can be done or not.
Thanks for any advice.
EDIT
Thanks for the suggestions. One thing that I didn't explain is that the animations initiate from the initial view controller (SplashScreenViewController) while the push notifications happen in the AppDelegate. I need a good way to get these two classes to communicate without creating a race condition.
I think you're focused on the specific problem, which is that the alert is obscuring your animations. However, the problem you should be trying to solve is: Am I asking for remote notification registration, and if so, has it been given or not?
You should be able to do this with a combination of the methods available on UIApplication and UIApplicationDelegate. Perhaps something like this:
#implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
if([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]) {
// Show animation
}
else {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:<#notification type#>];
}
return YES;
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// Show animation
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
// Show animation
}
#end
Related
I show my local notification like this periodically.
UILocalNotification *notification = [[UILocalNotification alloc]init];
[notification setAlertBody:#"Test test"];
[notification setUserInfo:#{#"test": #"test"}];
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
I need to detect back that notification and I plan to write here.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
It always call that function whether user tap on notification or it automatically call in foreground.
So, I separate using this.
if (application.applicationState == UIApplicationStateActive)
When I show notification center, it become InActive. But, it still call didReceiveLocalNotification. I can't differentiate whether user tap on notification from notification center or because of my periodic posting notification.
How can I really know that I tap on notification (Either from InActive State or Background State) in didReceiveLocalNotification?
Assuming that I understood your issue correctly, I stumbled on the same obstacle and couldn't find a super clean solution.
So the situation where the following method
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
is called and applicationState is equal to UIApplicationStateInactive happens in two cases:
the app is in the foreground and the notification has just been fired
the notification has been fired some time ago, notification center is pulled down and user tapped on the notification
One way to distinguish these two cases is to check the notification's fireDate:
notification.fireDate.timeIntervalSinceNow < 0.5
If this expression is true, it's very likely that the first case happened. If the expression is false, it's very likely that the second case happened.
This solution depends on the system delivering the notification without delay and/or the user not being fast enough to click the notification in the notification center under 500ms since the notification's firing. I'm not sure how likely is it for a firing delay to happen. I guess it's possible if the device is under some kind of processing load.
I hope there is a cleaner solution, hopefully someone will share it.
First of all, read this from Apple Documentation:
The user taps a custom action button in an iOS 8 notification. In this
case, iOS calls either
application:handleActionWithIdentifier:forRemoteNotification:completionHandler:
or
application:handleActionWithIdentifier:forLocalNotification:completionHandler:.
In both methods, you get the identifier of the action so that you can
determine which button the user tapped. You also get either the remote
or local notification object, so that you can retrieve any information
you need to handle the action.
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.
Second, this is how you can differentiate whether didReceiveLocalNotification: is called from active or inactive state:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
UIApplicationState appState = UIApplicationStateActive;
if ([application respondsToSelector:#selector(applicationState)])
appState = application.applicationState;
if (appState == UIApplicationStateActive)
{
}
else
{
}
}
application:didReceiveLocalNotification:
Sent to the delegate when a running app receives a local notification.
Check this:
iOS UILocalNotification - No delegate methods triggered when app is running in background and the icon is clicked upon notification
Use KVO key-value-observing to know and do something when the button is tapped.
Local notification are working well in foreground. but I need to perform some background work when a notification banner displays. It is working well when I tap on the banner when local notifications appear.
Code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (notification)
{
NSLog(#"AppDelegate didFinishLaunchingWithOptions");
[self application:application didReceiveLocalNotification:notification];
}
return YES;
}
My problem is similar to this issue
I used code below to perform background task:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
[[ReminderManger sharedMager] handleNotification:[[notification userInfo] objectForKey:#"reminderId"]];
});
}
There is no way to do that. Notifications are handled by iOS itself. If app would be handling this, there might be some way to get this figured out. I've been wasting my time over it but didn't succeed.
The only thing you can do is to get extended permission from iOS that your app need multi tasking in background. And then in background make your app become post local notifications, this will show banner whenever you want but you can't customize the banner. See Apple's line "An app that is running in the background may also schedule a local notification to inform the user of an incoming message, chat, or update." at this link
Now the thing is in background, you cant change your views, but parse data, and based on this you can make changes at time of launch.
I have the following:
In Settings, Capabilities I have background fetch checked.
In appDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
return YES;
}
- (void) application:(UIApplication*) application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(#"A fetch got called");
completionHandler(UIBackgroundFetchResultNoData);
}
BUT, when I debug the code on the iPhone the performFetch doesn't fire except when I hit "Simulate Background Fetch" in the debug menu. Isn't the phone supposed to periodically fire this on its own? What am I missing?
Ok. They worked. I just wasn't patient enough. They seem to fire rather infrequently and usually when I am doing something else with the phone (making a call, entering passcode, etc). How often do they typically fire? I know I can indicate a desired fire rate by setting a background minimum fetch interval, but I am just curious how often they fire with the default?
I'm trying to determine different scenarios of UIApplication didReceiveLocalNotification:. If a user clicks the notification the while app is inactive, I should bring them to the corresponding UI page. Otherwise, if user just opened the app without tapping a notification, I should let them stay in the UI page which they left off from.
However, there is a little problem as the UILocalNotification was scheduled by myself.
[[UIApplication sharedApplication] scheduleLocalNotification:scheduledAlert];
So every time it fired by iOS, it will call the same delegate method as the callback when I manually clicked the notification on the status bar:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { ... }
I tried some tricky hack such as counting the fired times while app is inactive mode, counting 1 means iOS firing, and counting 2 means user clicked, and relative counting management code.
But I don't think this could be the best practice. I checked the iOS Messages application, which has the same functionality. If you put the app into inactive mode, such as scroll down the status bar, then it can determine the UILocalNotification is from your touch (and will open the text editing mode) or just you return back to the app (stay in the previous status).
I would appreciate if you guys could let me know what the best solution is here!
I use a custom key-value pair which I set a NSMutableDictionary, which I then assign to UILocalNotification.userInfo, to tell different scenarios.
I check the app state so that I only handle the notification if I'm coming from the background:
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateInactive) {
// Your Code Here
} else {
}
The app receives a UILocalNotification, but if the user is at the UITableViewController at the fire time, the table view (containing the scheduled notifications) does not reload. The user has to get out of that view and load the view again so that the cells are loaded and, as the notification was already fired, it will not be displayed on any cell of that table view.
Problem is: If the user touches the specific tableView cell that contained the notification that just fired, the app crashes, cause the notification is not there anymore.
I've implemented the - (void)reloadData in every place possible, and it still doesn't load in real time.
What would be a better solution for this?
Other detail, how can I push a specific view after the notification is displayed (when the user slides the app icon when the phone is locked)?
Any help will be truly appreciated, since theres are the last details remaining to publish my first App.
The problem you describe is caused by the current local notification: While the notification is handled, it is still in the list of scheduled notifications, so refreshing the table view will have no effect. A solution to this problem is to defer reloading the table view until after the notification is handled, something like
// In your app delegate
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.localNotificationsController reloadDataIfNeeded];
});
}
// In your controller that shows the local notifications
- (void)reloadDataIfNeeded
{
if (![self isViewLoaded]) return;
[self.tableView reloadData];
}
You could also delete the notification from the list if you only use notifications that only fire once (so that you are sure that the notification will disappear anyway):
// In your app delegate
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[application cancelLocalNotification:notification];
[self.localNotificationsController reloadDataIfNeeded];
}
As to the second part of your question ("how can I push a specific view after the notification is displayed (when the user slides the app icon when the phone is locked?"), there are two scenarios how an application can be activated by a local notification.
Your application was suspended, but still in memory. Then selecting the local push notification will make the app enter the foreground, -application:didReceiveLocalNotification: will be called, and [application applicationState] will be UIApplicationStateInactive
Your application is not running, i.e. not suspended, not in memory. Then you will receive the local notification in the launchOptions in -application:didFinishLaunchingWithOptions::
UILocalNotification *localNotification = [launchOptions valueForKey:UIApplicationLaunchOptionsLocalNotificationKey];
Despite what the documentation says, -application:didReceiveLocalNotification: will not be called in this case.
So to show the notification that woke up the application, you can push your controller in these two cases.