I setup a background task with beginBackgroundTaskWithExpirationHandler:^{ } and even after ending the task using
if ([[UIDevice currentDevice] isMultitaskingSupported]) {
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID];
backgroundTaskID = UIBackgroundTaskInvalid;
}
NSLog(#"App State -- %d", [[UIApplication sharedApplication] applicationState]);
if([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive)
{
//OpenGL operations
}
what I get is UIApplicationStateActive. Is this a bug ? How else do I determine the app is indeed in background ?
Instead of relying on the applicationState value, how about posting a custom notification at the end of the background task?
Set up as an observer for your custom notification:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(backgroundTaskDidFinish:) name:#"BackgroundTaskDidFinish" object:<either nil or the object that will post the notification>];
}
Post your custom notification when the background task completes
if ([[UIDevice currentDevice] isMultitaskingSupported]) {
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID];
backgroundTaskID = UIBackgroundTaskInvalid;
[[NSNotificationCenter defaultCenter] postNotificationName:#"BackgroundTaskDidFinish" object:self]
}
Respond to the notification, which your app should receive when it really returns to an active state
- (void)backgroundTaskDidFinish:(NSNotification *)notification
{
//OpenGL operations
}
Alternatively, you could look for one of the other notification messages that would indicate that your app is truly in an active state.
For instance, add your object as an observer for UIApplicationWillEnterForeground. Set a BOOL value when you receive that notification. Then, look for that BOOL value along with the UIApplicationState value.
Of course, at some point you'll have to clear the BOOL - say, when you're starting that background task - so it will be NO unless the app really did become active.
You can try to determine it with NSNotification:
- (void)viewDidLoad {
[super viewDidLoad];
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:app];
}
- (void)applicationWillResignActive:(NSNotification *)notification {
//do what you want when your app is in background
}
Related
I have app that send local notifications.
So I have two scenarios.
Application is in foreground, notification center is dragged out. Notification is recived. Notification center is being hidden.
Application is in foreground, notification center is dragged out. Notification is recived. Notification is selected.
My code to handel notifications
AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification* notification = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
//When App is launched from notification
[self setupLocalNotification:notification.userInfo];
}
-(void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
NSLog(#"USER INFO %#",notification.userInfo);
[self setupLocalNotification:notification.userInfo];
}
-(void)setupLocalNotification:(NSDictionary*)userInfo {
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if ( state == UIApplicationStateInactive || state == UIApplicationStateBackground){
[[NSUserDefaults standardUserDefaults] setObject:userInfo forKey:#"localNotification"];
//This is for situation when I'm deep in navigation controller and this also trigger viewWillAppear in ViewController
[[((MainPanelViewController*)[[self window] rootViewController]) startNC] popToRootViewControllerAnimated:false];
} else {
//something
}
}
** MainPanelViewController**
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//This is for situation when currently in ViewController
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(beacomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self checkLocalNotification];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
}
- (BOOL) checkLocalNotification{
NSDictionary* userInfo = [[NSUserDefaults standardUserDefaults] objectForKey:#"localNotification"];
if (userInfo != nil) {
NSLog(#"Notification");
[self performSegueWithIdentifier:#"ShowNotification" sender:self];
return true;
}
return false;
}
Problem
I don't know how to distinct those two situations. Because every time notification is received when App is inactive then method performSegueWithIdentifier is being called.
I'm building an app that asks the user for permission to post notifications when the user enables a switch. I'm using this code:
- (IBAction)mySwitchValueChanged:(id)sender {
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)]){
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]]; // ask the user for permission
}
if ([[UIApplication sharedApplication] respondsToSelector:#selector(currentUserNotificationSettings)]) { // Check it's iOS 8 and above
UIUserNotificationSettings *grantedSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
if (grantedSettings.types != UIUserNotificationTypeNone)
{
// Accepted
} else
{
[self.mySwitch setOn:NO]; // Declined
}
}
}
The desired behaviour is as follows:
User slides switch
Alert asks for permission
Code waits for user to decide
Either does whatever I code where I've put // accepted, or disables the switch.
The current behaviour makes the code run through at once, and doesn't wait for the user to decide. How can I change this to get the desired behaviour?
Thanks!
Note: This is workaround idea
Once you call this method :- [UIApplication sharedApplication] registerUserNotificationSettings and user grants push notification for the app, then didRegisterForRemoteNotificationsWithDeviceToken in AppDelegate get fired,
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
//call a notification when user granted Push Notifiaction
[[NSNotificationCenter defaultCenter]
postNotificationName:#"PushNotificationSuccess"
object:self];
}
So what you have to do, you can call a NSNotification from the mentioned method, to update the UI accordingly.
- (void)updateUIOnNotification:(NSNotification *) notification
{
// Accepted
}
//Call the below method in your else part and remove the line
//[self.mySwitch setOn:NO];
-(void)Showalert{
UIAlertview*Temp=[[UIAlertview alloc]initWithTitle:#"Need Permission to Send Notifications" message:#"The App Wants The Permission to Work Properly!\nPermit The App in Notification settings"delegate:self cancelButtonTitle:#"Okay!" otherButtonTitles:nil];
[Temp show];
}
#pragma mark Alertview delegate method
- (void)alertViewCancel:(UIAlertView *)alertView{
//Remove the Below line From Else part of your code
[self.mySwitch setOn:NO];
}
I have noticed that when I select a push notification for my app from notification center, that notification no longer appears in the list of notifications. However, when I receive a notification and tap the banner right away, the app opens as it should, but when I pull down to view the notification center, that notification is still there. I have the following push handling code in my delegate:
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo {
[[UIApplication sharedApplication] cancelAllLocalNotifications];
//Presenting view controllers...
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Extract the notification data
NSDictionary *notificationPayload = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if(notificationPayload)
{
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
return YES;
}
- (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.
application.applicationIconBadgeNumber = 0;
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
// when you tap on any of notification this delegate method will call...
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[[UIApplication sharedApplication] cancelLocalNotification:notification];
}
You have to use following to remove all notifications
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
[self clearNotifications];
}
- (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.
[self clearNotifications];
}
- (void)clearNotifications
{
[[UIApplication sharedApplication] setApplicationIconBadgeNumber: 1];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber: 0];
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
You can implement this delegate method:
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[application cancelLocalNotification:notification];
}
I have an app that uses local notification. Until recently everything worked fine, but after iOS 8 was released, my local notifications aren't working when app is not running(removed from processes). This is my code in app delegate where I set my local notifications.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:localReceived object:self userInfo:notification.userInfo];
// Set icon badge number to zero
application.applicationIconBadgeNumber = 0;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)])
{
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:
UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}
else if ([UIApplication instancesRespondToSelector:#selector(registerForRemoteNotificationTypes:)])
{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
}
...
// Local notificaiton example. Icon badge
UILocalNotification *locationNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
NSLog(#"locationNotification:%#",locationNotification.alertBody);
if (locationNotification) {
// Set icon badge number to zero
application.applicationIconBadgeNumber = 0;
// call local notification method
[self application:[UIApplication sharedApplication] didReceiveLocalNotification:locationNotification];
}
return YES;
}
When my app is not running, the didFinishLaunchingWithOptions: method is called. In it I call the didReceiveLocalNotification method again where I call postNotificationName. I put the observer in my ViewControllers ViewDidLoad method:
- (void)viewDidLoad
{
...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(recieveLocalNotification:) name:localReceived object:nil];
...
}
The problem is that the method recieveLocalNotification: is never called when app is not running. It's called every time when app is in background or running. Can anybody tell me what I'm doing wrong here?
Thanks in advance!
-viewDidLoad is called after the view of view controller is instantiated, the view is instantiated after the viewController.view is accessed for the first time.
So you can try call your viewController.view before posting the notification.
Is there an available view controller method that is called when the user presses the lock button? I'm looking for something like viewDidDisappear: or viewWillDisappear:, but specific to the case of the lock button being pressed.
A notification called UIApplicationDidEnterBackgroundNotification is posted when the user locks their phone. Here's how to listen for it:
In viewDidLoad: of your ViewController:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(screenLocked) name:UIApplicationDidEnterBackgroundNotification object:nil];
Then, define a method (mine was called screenLocked above) and write code you want to be executed when the screen is locked.
-(void)screenLocked{
//do stuff
}
Also, to do some necessary cleanup, add this method to your ViewController too.
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}
Try this :
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateInactive) {
NSLog(#"Sent to background by locking screen");
} else if (state == UIApplicationStateBackground) {
NSLog(#"Sent to background by home button/switching to other app");
}
}