iOS: Background / Foreground Events - ios

I´m working on an application in which, when it is pushed in background via the home button, a timer should start and when the application gets back to foreground and the timer has passed a certain amount of time, something should be executed.
My questions are
How do I handle the events when my app goes to
background/foreground?
Is there a special method or an other technique?
Thanks a lot.

A possible implementation could look like:
#define YOUR_TIME_INTERVAL 60*60*5 //i.e. 5 hours
- (void)applicationDidEnterBackground:(UIApplication *)application
{
//... your oder code goes here
NSNumber *timeAppClosed = [NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults timeAppClosed forKey:#"time.app.closed"];
[defaults synchronize];
}
and
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSNumber *timeAppClosed = [[NSUserDefaults standardUserDefaults] valueForKey:#"time.app.closed"];
if(timeAppClosed == nil)
{
//No time was saved before so it is the first time the user
//opens the app
}
else if([[NSDate date] timeIntervalSinceDate:[NSDate dateWithTimeIntervalSince1970:[timeAppClosed doubleValue]]] > YOUR_TIME_INTERVAL)
{
//Place your code here
}
}

In the appDelegate of the app you have some delegate methods which you can implement.
You can check out the UIApplicationDelegate protocol to which you AppDelegate should conform.
When the app is pushed in the background the function applicationDidEnterBackground: will be called. When entering the foreground applicationWillEnterForeground: is called.
Better not use a timer, but store a NSDate reference in the applicationDidEnterBackground: method. When your app is entering the foreground you can calculate the timeDifference using the stored NSDate using the
- (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate
function.

Related

How to fetch data in the background, when the app is terminated and when app is in foreground

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.

Timer when leaving app and returning on ios

Currently on my iOS app, when the user exits to the home screen and goes back into the app, it requests a login credentials which is set in my AppDelegate. But what I am trying to do is if the user goes out of the app and back in within for example 2 minutes, the timer resets and the user does not need to input his password. When the user goes back into the app after 2 minutes, it will alert him to input the password again. Any help will be greatly appreciated. Thanks!
Use NSUserDefaults to store the NSDate in your app delegate
- (void)applicationDidEnterBackground:(UIApplication *)application {
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:#"myDateKey"];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
NSDate *bgDate = (NSDate *)[[NSUserDefaults standardUserDefaults] objectForKey: #"myDateKey"];
if(fabs([bgDate timeIntervalSinceNow]) > 120.00) {
//logout
}
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"myDateKey"];
}
Update:
Good point by #mun chun if the app has to implement something to handle clock changes we can use something like this
- (void)applicationDidEnterBackground:(UIApplication *)application {
[[NSUserDefaults standardUserDefaults] setFloat: [[NSProcessInfo processInfo] systemUptime] forKey:#"myDateKey"];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
float bgTime = [[NSUserDefaults standardUserDefaults] floatForKey: #"myDateKey"];
if(fabs([[NSProcessInfo processInfo] systemUptime] - bgTime) > 120.00) {
//logout
}
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"myDateKey"];
}
Obviously once the phone restarts the time will be reset in that case we have to make sure we add validation. Also to note is that the myDateKey should be removed in appropriate application modes.
User may adjust the system time to earlier when the app is in background. It maybe not reliable to compare a stored time with current system time when app reopened.
We can use NSTimer+BackgroundTask to assure the amount of time elapsed.
In the applicationWillResignActive: delegate, setup a background task and a NSTimer.
When the timer fired (i.e. at 120 seconds), set the session expired, and end the background task.
When the app re-opened, in the applicationDidBecomeActive: delegate, check the session for request login if it expired.
static BOOL sessionActive;
static NSTimer *timer;
static UIBackgroundTaskIdentifier bgTask;
- (void)applicationWillResignActive:(UIApplication *)application
{
sessionActive = YES;
timer = [NSTimer scheduledTimerWithTimeInterval:120.0 target:self selector:#selector(sessionExpired) userInfo:nil repeats:NO];
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{[self sessionExpired];}];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[self cleanup];
if(!sessionActive)
{
//session expired, request login credentials
}
}
-(void)sessionExpired
{
[self cleanup];
sessionActive = NO;
}
-(void)cleanup
{
if([timer isValid]) [timer invalidate];
timer = nil;
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}

Get the length of time the iPhone was locked

How would I get the length of time a phone was locked if I wanted to use it to increment a timer progress view when the phone was resumed, or schedule a notification to fire when the phone was still locked?
Implement the UIApplicationDelegate method applicationWillResignActive: and applicationDidBecomeActive:.
You will have to store the current time when the former is called and calculate the difference when the latter is called. Specifically, in your application delegate:
#define TIMESTAMP_KEY #"timestamp"
- (void)applicationWillResignActive:(UIApplication *)application
{
NSInteger *timestamp = [[NSDate date] timeIntervalSince1970];
[[NSUserDefault standardUserDefaults] setInteger:timestamp forKey:TIMESTAMP_KEY];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSInteger *newTimestamp = [[NSDate date] timeIntervalSince1970];
NSInteger *oldTimestamp = [[NSUserDefault standardUserDefaults] integerForKey:TIMESTAMP_KEY];
NSInteger *secondsPassed = newTimestamp - oldTimestamp;
// Now you can resynch your timer with the secondsPassed
}

Open different view on initial run

I'm trying to make my app launch a different view on the first time it is loaded up. I've got this code at the moment which implements that something should happen when the app is first launched. I've got this code but it lacks the code to open Initialviewviewcontroller. I have no idea how to do this so help would be much appreciated
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL hasRunBefore = [defaults boolForKey:#"FirstRun"];
if (!hasRunBefore) {
[defaults setBool:YES forKey:#"FirstRun"];
[defaults synchronize];
// what goes here??
else
{
NSLog (#"Not the first time this controller has been loaded");
So I should launch a different view controller in the if statement. But what should I put ?
Solution No. 1
I've written a simple snippet for this thing because I use it quite a lot. You can find it here.
Feel free to use it, fork it or modify it!
Solution No. 2
You can do something like this in your AppDelelegate.m
Add this simple method at the bottom:
- (BOOL)hasEverBeenLaunched
{
// A boolean which determines if app has eer been launched
BOOL hasBeenLaunched;
// Testig if application has launched before and if it has to show the home-login screen to login
// to social networks (facebook, Twitter)
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasAlreadyLaunched"]) {
// Setting variable to YES because app has been launched before
hasBeenLaunched = YES;
// NSLog(#"App has been already launched");
} else {
// Setting variable to NO because app hasn't been launched before
hasBeenLaunched = NO;
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasAlreadyLaunched"];
[[NSUserDefaults standardUserDefaults] synchronize];
// NSLog(#"This is the first run ever...");
}
return hasBeenLaunched;
}
After implementation of this method you can use it like that:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Determining Storyboard identifier for first view
NSString *storyboardID = [self hasEverBeenLaunched]? #"MainView" : #"LoginView";
// Setting proper view as a rootViewController
self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:storyboardID];
return YES;
}

IOS: store an array with NSUserDefault

I want to store an array with NSUserDefault, then, I put in applicationDidEnterBackground
[[NSUserDefaults standardUserDefaults] setObject:myArray forKey:#"myArray"];
and in application didFinishLaunchingWithOption
myArray= [[NSMutableArray alloc]
initWithArray:[[NSUserDefaults standardUserDefaults]
objectForKey:#"myArray"]];
it's ok for multitasking device, but for not-multitasking device, how can I solve?
Store the object in NSUserDefaults in -applicationWillTerminate:, if it hasn't already been saved by the invocation of -applicationDidEnterBackground: (i.e. check if multitasking is supported, if it is, then don't save it because it's already been saved.)
- (void) applicationWillTerminate:(UIApplication *) app {
if([[UIDevice currentDevice] respondsToSelector:#selector(isMultitaskingSupported)] &&
![[UIDevice currentDevice] isMultitaskingSupported]) {
[[NSUserDefaults standardUserDefaults] setObject:myArray forKey:#"myArray"];
}
}
Do not forget to sync the buffer before going into background:
[[NSUserDefaults standardUserDefaults] synchronize];
The previous answers are all correct, but note that neither applicationDidEnterBackground nor applicationWillTerminate are guaranteed to be called in all situations. You are usually better off storing important data whenever it has changed.
Save NSUserDefaults at
- (void)applicationWillTerminate:(UIApplication *)application
set
[[NSUserDefaults standardUserDefaults] setObject:myArray forKey:#"myArray"];
in
applicationWillTerminate
and don't forget to use the encodeWithCoder and initWithCoder inside the object that you are trying to save and that is contained in the array

Resources