My app execute background fetches to update some data. In my initial view controller's viewDidLoad method I want to detect wether my app runs for background fetch or not?
I know that it's very easy to check the app's state using this:
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
return; // have a breakpoint here <----
}
But for some reason even when I launch app normally sometimes program can reach this breakpoint. Which means that applicationState returns UIApplicationStateBackground. Maybe app launch happens at the moment when background fetch is running or something like that?
I don't want to execute code that in my viewDidLoad method when app runs in background mode, so I need this if block in viewDidLoad.
By the time your viewDidLoad method is called, it will be in the foreground (with a state either UIApplicationStateInactive or UIApplicationStateActive), so it doesn't make sense to ask for UIApplicationStateBackground in the viewDidLoad, for more info read this link.
Related
I've been using interactive notification and it works well when my app is suspended in the background, but causes problems when my app has been terminated.
I've used NSLog to determine the app's lifecycle when the interactive notification is triggered when the app has not launched.
The following goes on, in the order listed, without the app visibly launching:
didfinishlaunching
handleActionWithIdentifier
viewdidload
viewwilllayoutsubviews
viewdidlayoutsubviews
viewdidappear
The app then seems to terminate without calling
didenterbackground or willTerminate
The reason why it's causing issues for me is because I create timers in viewdidload that are invalidated in didenterbackground.
Because didenterbackground isn't being called, when the app is subsequently launched after triggering an interactive notification I'm ending up with two instances of the timer.
Could anyone shed some light on why the app terminates but didenterbackground or willTerminate aren't being called?
You should not assume that a call to didfinishlaunching means that you are in the foreground. As #PaulW11 says in his comment, for local notifications when your app is not running, you get launched directly into the background, and never see a foreground-to-background transition, because you were never in the foreground in the first place.
The system will call applicationDidBecomeActive: if you are being launched into the foreground. You should put your code that starts your timers there.
The docs say that local notification notices get invoked in the background:
When the user taps a custom action in the alert for a remote or local
notification’s, the system calls the
application:handleActionWithIdentifier:forRemoteNotification:completionHandler:
or
application:handleActionWithIdentifier:forLocalNotification:completionHandler:
method in the background so that your app can perform the associated
action.
You should write your application:handleActionWithIdentifier:forLocalNotification:completionHandler: method to check to see if it's being called from the foreground or from the background.
I use (Objective-C) code like this to tell if I'm in the foreground or the background:
BOOL inBackground = [UIApplication sharedApplication].applicationState == UIApplicationStateBackground;
It appears that the method viewWillTransitionToSize:withTransitionCoordinator: gets called multiple times when the app goes into the background or inactive since iOS 9.
For example, if the app is in the portrait on iPad, pressing the home button will cause the app to receive method call first with size of 1024x768 (landscape) and then with 768x1024 (back to portrait). That leads me to conclude that iOS does this in order to get screenshots for app switcher.
Our application logic depends on the screen size and changes to the screen size trigger tasks that update our model with regard to the new size. We need to do this when user rotates the device or goes into multitasking mode (split view), but we must not do it when user goes into the background.
One idea was to use UIApplicationWillResignActiveNotification notification, but that turned out to be mission impossible because sometimes viewWillTransitionToSize:withTransitionCoordinator: gets called before the notification is sent and othertimes it gets called after the notification is sent :/
Any ideas?
In viewWillTransitionToSize:withTransitionCoordinator you can use the UIViewControllerTransitionCoordinator to tell when the transition is complete. Then, check that your app is not backgrounded and perform your logic.
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
// after transition
if ([UIApplication sharedApplication].applicationState != UIApplicationStateBackground) {
// perform logic
}
}];
}
I currently have the same problem. I do a lot of work when the size changes and can't have it happening when going into the background. One thing that is working for me "most" of time is to check if the app is in the background using [uiapplication sharedApplication] applicationState]. The application state is set to UIApplicationStateBackground before the 2 extra calls to viewWillTransitionToSize are made. This seems to be consistent. I'm having some problems when the app is swiped in using multitasking then goes into the background and then foreground again at full size. The order of calls doesn't seem to be consistent in this case to determine which size changes should be used.
Yup, same problem. You could, instead of using 0.1 seconds, use the coordinator's transitionDuration(). Then, check for background status.
I have a camera app that is running fine on iOS 7. In the viewDidAppear call of my MainViewControllerI am first checking if the application state in not inactive and the application is not in background.
The code sample is given below.
-(void) viewDidAppear
{
if ((UIApplicationStateBackground != [UIApplication sharedApplication].applicationState)
&& (UIApplicationStateInactive != [UIApplication sharedApplication].applicationState))
{
// check if the camera is running
// perform the animation of opening shutter.
}
}
My problem is that on iOS 8 beta 2 [UIApplication sharedApplication].applicationState returns UIApplicationStateInactive hence the check fails. But on iOS 7 [UIApplication sharedApplication].applicationState returns UIApplicationStateActiveand works without any problem.
Has anyone else faced the same issue?
EDIT
A simple experiment of putting breakpoints in viewDidAppear and appDidBecomeActive in xcode 6 reveals that viewdidAppear gets called first. I suppose its a bug in iOS 8
Your view could not possibly appear unless the app was active, or at least becoming active. Views don't do things like appear when the app is inactive or in the background. So I would just delete that condition entirely if I were you. It was never serving any useful function.
(By the way, if you're encountering this situation on launch, what you're experiencing sounds like an issue that I've reported to Apple in another form: in iOS 8, the application doesn't switch to active (so that application:didBecomeActive: fires) until very late, well after the whole interface is up and running. This has caused me to have to rewrite quite a lot of my code. For example, if use my root view controller's viewDidAppear: to register for the applicationDidBecomeActive notification, I then receive that notification shortly afterwards — which is nutty.)
I have a button which when pressed creates an object thing. It then sleeps for ten seconds and then calls thing.go which gets the application state like
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
Then following this question's answer and comments I check if the app is in the background, and if it is, it displays a local notification.
So I press the button and immediately press the home key to go to the main screen (thereby putting the app in the background)
However, I can tell that the if statement is returning false and is therefore not executing any code within (by putting an NSLog inside the if statement).
So my next thought process was to somehow display the date in the console using NSLog(state). Obviously I cannot do this.
So how can I display the applicationState to resolve my issue? Or is there another way I can check to see if the app is running in the background within this class's method?
Here is the go function which is called inside the IBAction when the button is pressed
-(void)go {
if (state == UIApplicationStateBackground) {
NSLog(#"App is in background");
}
}
Here is the IBAction which is linked to the button, i.e. runs when button is pressed
- (IBAction)button_help {
myclass* thingy = [[myclass alloc] init];
sleep(10);
thingy.go;
}
You should pretty much NEVER use sleep. Forget it exists. It locks up your app, and nothing changes, including transitions to the background.
What you want to do is to set a timer for 10 seconds that then invokes thing.go.
However, that's going to be complicated by the fact that normally timers don't run in the background. In fact, unless you take special steps, your app doesn't get any processor time in the background. You get told that you are going to the background, and then the next call you get is the return-to-foregraound.
When your app gets a message that it is being sent to the background, it will need to make the system call that asks for background processing time. (I don't remember the call off the top of my head, and don't have Xcode running at the moment.)
Apple changed the rules for background processing in iOS 7, but you say this is iOS 6, so those changes don't apply.
If I understand correctly, you're checking to see if the app is returning from a local notification or if it was already active?
if (state == UIApplicationStateActive) {
// do something
NSLog(#"application was active ");
} else {
NSLog(#"sent from notification");
}
I am currently developing an app that will need to terminate after running in the background for more than five minutes. In order to do this, I will have to have a timer running in the background after the the Home button has been pressed or in case of an interruptions such as an SMS or a telephone call, then, after five minutes the applicationWillTerminate method will be called. My first question is should I put the applicationWillTerminate in the applicationWillResignActive method or in the applicationDidEnterBackground method? My second question is since this is an app with more that one view, Should I write these things in the AppDelegate class or elsewhere? Thank you for your response.
1) You can't force your app to finish programatically.
2) You should never call these AppDelegate methods by yourself. They're meant to be called only by the system.
Reference: UIApplicationDelegate Protocol Reference.
This is pretty ghetto, but what you can do is make your app crash when you want it to exit, and it will close automatically, granted that's not closing the app, but there's no real harm in it as long as you are in control of how it crashes try to go for a bad access error, aka trying to access something that has been deallocated
as for running a timer in the background, i don't know per say if you can do that, but as an alternative you can save the time when they leave the app aka the app goes into the background and then you can have all the events return to your app of the view controller that is first responder, and each UIEvent has a time stamp, and regardless of which event it is you can compare the time stamps and see if it's greater than 5 minutes
Regardless i don't suggest any of the above, but that is the best answer i can come up with for your question
the code for receiving events out side of your app
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
will start the event tracking and the call back is:
- (void)remoteControlReceivedWithEvent:(UIEvent *)event { }
but you have to remember to
[self becomeFirstResponder];
this tells the device which view controller to go to for the event tracking, oh and don't forget to resign first responder, and endReceivingRemotecontrolEvents