This question already has answers here:
iOS Run Code Once a Day
(5 answers)
Closed 9 years ago.
I have an app right now that allows a user to pull information from a website. I unfortunately don't have any access to an API for the site, it's just something I screen scrape. Ideally the user will scrape the information once a day from the site.
Currently I set a time and use performFetchWithCompletionHandler. When performFetchWithCompletionHandler gets called, it checks the time that was set and if the time is the current time, it will perform the scraping. Unfortunately, performFetchWithCompletionHandler doesn't work this way (I can't guarantee it will fire during the hour & minute that the user specified). Is there a way to let my app run in the background and scrape a website at a designated interval?
this may be helpful to you...
First declare ,
NSTimer *timerAppBG;
in your AppDelegate.h file....
-> Please put bellow all methods in AppDelegate.m File...
- (void)applicationWillResignActive:(UIApplication *)application
{
timerAppBG = [NSTimer scheduledTimerWithTimeInterval:60.0f target:self selector:#selector(applicationWillResign) userInfo:nil repeats:YES];
}
- (void) applicationWillResign
{
NSLog(#"About to lose focus");
//Write your code here...
[self myVcInitMethod];
}
- (void) myVcInitMethod
{
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(applicationWillResign)
name:UIApplicationWillResignActiveNotification
object:NULL];
}
Thanks.
Related
I am running into a situation where I'm not able to properly handle NSTimer.
In my app, I've an option of user chats (I'm not using XMPP because of a low budget project, but the chat is working through API calls ONLY). I've scheduled a timer at a time interval of 15 seconds. If any new chats available I'll get it and will update chat view.
Here's the working scenario:
As this is a UITabbar based app, a user will come to "Chat" tab.
A User will have a list of persons with whom he can chat.
A User will select any of a user – will push to Chat Screen.
Where all locally saved chats will be visible and an API call will be made for new chats, on success (or error) of API call, a timer will be scheduled to sync chats at a time interval of 15 seconds.
If a user goes back (pops), in viewDidDisappear: method, I'm invalidating the (running) timer.
In my Unit testing, if I'll continuously push & pop to/from Chat screen, there'll be multiple instances of that timer will get scheduled. I suspect, this is WRONG.
I'm not sure what I'm doing is correct or not though I need your help to understand the right and the correct way to get my functionality done. I guess here there's no need of the code for above explanation.
First of all, why are you not exploring option of push notification? Polling server every 15 second is a bad design :-(.
Second, when it comes to NSTimer it is important to start and stop them from the same thread. I would advise you encapsulate your timer start/stop code in below block always ensuring you deal on main thread with your timer.
dispatch_async(dispatch_get_main_queue(), ^{
});
This is the way o usually work with NSTimer. Hope it helps.
#implementation MainViewController{
NSTimer *myTimer
}
- (void)startTimer{
//Prevents multiple timers at the same time.
[myTimer invalidate];
myTimer = [NSTimer scheduledTimerWithTimeInterval:5.0f target:self selector:#selector(update) userInfo:nil repeats:YES];
}
- (void)update
{
//Stops the timer if the view in not on the screen
if (!(self.isViewLoaded && self.view.window)) {
[myTimer invalidate];
}
}
#end
I am working on Pedometer application via using CMPedometer Class. It works fine and I cross-checked the value via built in app- HealthKit. However, when I wake up this morning and I was still seeing the yesterday value.
Then I killed the app and reopened it again, then it showed me today value.
My question is how to handle this issue?
you can use applicationWillEnterForeground Delegate in your ViewController or Model ...
( if you want handle in specific VC or model you must register it ) or you can use this Delegate from your appDelegate
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateData) name:UIApplicationDidBecomeActiveNotification object:[UIApplication sharedApplication]]; // Dont forgot about removeObserve
-(void)updateData{
[[CMPedometer sharedInstance]update]; // update your data
// now you should update Your UI
[self.tableView reloadData];}
So, i need to start a timer in the app delegate method applicationDidEnterBackground:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
globalBackgroundTimer = [NSTimer timerWithTimeInterval:30 invocation:nil repeats:NO];
}
the timer is declared like so in app delegate.h:
extern NSTimer * globalBackgroundTimer;
While the timer runs, i receive background location updates (is enabled in plist), in a view controller, and i want to check constantly in locationManager:didUpdateLocations:
For when the timer has expired so i can end the location updates.
... //code omitted
//this is called repeatedly when the app is in the background, and checks whether the global variable is instantiated, and if it has expired.
if(globalBackgroundTimer)
{
NSLog(#"timer alive");
if(!globalBackgroundTimer.isValid)
{
[locationManager stopUpdatingLocation];
NSLog(#"background timer invalid, stopping location updates");
}
}
But i can't make it work (Mach-O-linker error),
however i also read that this approach was ill-advised. So what do you guys suggest?
Figure out a mechanism to store the state of the timer when the app enters the background (NSUserDefaults or Documents sandbox). Then, when the app enters foreground, recalculate the difference using the NSDate information you saved.
(I realize this may already have been what you were doing before you posted this question.)
One question and one issue:
I have the following code:
- (void) registerForLocalCalendarChanges
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(localCalendarStoreChanged) name:EKEventStoreChangedNotification object:store ];
}
- (void) localCalendarStoreChanged
{
// This gets call when an event in store changes
// you have to go through the calendar to look for changes
[self getCalendarEvents];
}
These methods are in a class/object called CalendarEventReporter which contains the method getCalendarEvents (in the callback).
Two things:
1) If the app is in the background the callback does not run. Is there a way to make it do that?
2) When I bring the app back into the foreground (after having changed the calendar on the device) the app crashes without any error message in the debug window or on the device. My guess is that the CalendarEventReporter object that contains the callback is being garbage-collected. Is that possible? Any other thoughts on what might be causing the crash? Or how to see any error messages?
1) In order for the app to run in the background you should be using one of the modes mentioned in the "Background Execution and Multitasking section here:
uses location services
records or plays audio
provides VOIP
services
background refresh
connection to external devices
like through BLE
If you are not using any of the above, it is not possible to get asynchronous events in the background.
2) In order to see the crash logs/call stack place an exception breakpoint or look into the "Device Logs" section here: Window->Organizer->Devices->"Device Name" on left->Device Logs on Xcode.
To answer your first question, take a look at https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
What I did to get code running in the background is to do something like
In the .h file
UIBackgroundTaskIdentifier backgroundUploadTask;
In the .m file
-(void) functionYouWantToRunInTheBackground
{
self.backgroundUploadTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
//code to do something
}
-(void) endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUploadTask];
self.backgroundUploadTask = UIBackgroundTaskInvalid;
}
The code above I pretty much learned from objective c - Proper use of beginBackgroundTaskWithExpirationHandler
As for your second question, you should set a breakpoint where code is supposed to run when you bring the app back to the foreground. No one can figure out why an app crashes if not given enough code or information.
The solution to the second part of the question was to raise the scope of the object containing the callback code. I raised it to the level of the containing ViewController. This seems to work. I still can't figure out how to raise the Notification (i.e. execute the call back) if the notification comes while the app is in the background/suspended. This prevented the object containing the callback from being cleaned up.
I am pretty new to bonjour/networking with ObjC (although well versed in other areas!) I am asking for a bit of advice - I have an iOS app that will run on multiple iPads, in a store. The apps occasionally have to share some data, and the internet isn't always available so a webservice is not an option, hence I decided on using bonjour.
I have setup the Bonjour/NSNetservices and everything is functioning correctly, the ipads basically form an 'ad-hoc network' and connect automatically at app launch, however I am looking for advice for the following situation:
The app normally shares data in the background, without any user intervention - however there is one function where when a button is pressed on one app, data should be returned from another app remotely. The UI then updates when the data has been received from the other device - however if the connection should be lost to the other device, the data will never reach the users device, and the data will not be displayed. I am wanting to implement some form of timout, but unsure how to do this - any suggestions would be much appreciated!
The process flow for this is something like this:
button press on 'dev 1' > 'dev 1' broadcasts 'dev 2 please send data message' > 'dev 2' responds with requested data [timeout required here] > UI is updated if data is received /[if timeout fires, error message is displayed]
So I really just need a timeout for the last section - and I really cannot think of a way to implement it.
I can post code for this if required.
Thanks!
This solution may work if you have the possibility to manually cancel the request.
I believe you can use a simple NSTimer to cancel the request after a wait. I'm sure it's not the best solution, but it will probably work.
Here's how I would do it.
Create your timer and an NSInteger (to store the timer value) in your class :
NSTimer *timer;
NSInteger timerValue;
Call this method with timer = [self startTimer]; when you fire your request :
- (NSTimer*)startTimer {
timerValue = 30;
return [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerTicked:) userInfo:nil repeats:YES];
}
Implement the timerTicked: method :
- (void)timerTicked:(NSTimer*)timer {
timerValue --;
if (timerValue <= 0) {
// Cancel the request
// Show an alert
}
}
You can cancel the timer with [timer invalidate]; but remember this will "destroy" your timer, so it won't fire events ever again.
As you've not indicated how you are currently requesting data from your other device, I have had to make some assumptions.
You can use NSURLRequest with a timeout using requestWithURL:cachePolicy:timeoutInterval:
See NSURLRequest documentation