Possible to make a task scheduler for iOS? - ios

I want to make an app where I can have a task (GET URL) run at predefined times (selectable in UI). For instance, Monday to Friday at 8am.
Is this possible in iOS?
I tried searching but haven't found anything very useful, probably using the wrong search terms. Does anyone happen to find some sample code for what I'm trying to do?
Edit: Pointing out that I want the app to perform these tasks even if the app is not running. I want to user to just select wanted days of the week and time, and then the phone will take care of everything - even if the phone is restarted.

If you want to regularly wake up to download content, you can register to get push notifications, and download based on the contents of the notification. You are likely to get some cycles to do this close to the scheduled time. If you want to 'opportunistically' download content you can register for background 'fetch' but there is no guarantee of scheduling.
See
https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html

So, first of all, the app has to be open for any kind of task to run. After that, there are 2 ways you can do it:
You can set a timer with a selector, or you can use grand central dispatch. Both have their strengths and weaknesses depending on what the task is...
https://developer.apple.com/Library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/index.html
https://developer.apple.com/library/ios/documentation/performance/reference/gcd_libdispatch_ref/index.html
check out those links and see if they can help.
Remember that you should do stuff like this on a background thread, and that the UI cannot be updated from any thread by the main thread. Additionally, remember the limitations iOS puts on background applications.

Yes, it is possible.
You can use -
[self performSelector:#selector(myFunc:) withObject:nil afterDelay:5.0];

Related

Where to put code that gets checked frequently?

I have some code that needs to get called frequently, such as check what day it is, if it's the next day then move the day strings in the tableView.
Now I thought that the viewDidLoad would get called all the time and so it would be 'fine' to put it in there. However, I've left the simulator overnight, and I've pressed the home button and clicked again, changed VCs etc. and viewDidLoad hasn't been hit.
What are my options for doing sporadic checks such as, is it a new day? As x happened etc.
In this specific case, you can subscribe to NSCalendarDayChangedNotification to be notified when the date changes and respond accordingly in your view controller. In general, didBecomeActive or viewDidAppear would likely work.
What are my options for doing sporadic checks such as, is it a new day
It depends what the meaning of "is" is! In particular, "is" when? You say "sporadic", but that's just fluff. When do you need to know this? To what stimulus do you want to respond? When the user opens your app? Then put it in applicationDidBecomeActive. Every day at noon? Then run an NSTimer. Really, the problem here is that you don't seem to know, yourself, just when you need to perform these checks.
Whilst in your app, its quite easy to continually check for something. You simply create a background thread. However, what you describe is a thread that persists from outside the app's lifecycle.
Have a read on this documentation provided by Apple itself. You have to have good excuse to put a background thread. The scope of such thread is limited to only certain scenarios such as downloading background stuff, playing sounds etc.
For your scenario, I'd look at applicationDidBecomeActive(_:) found in your Application Delegate. There you can mimic such continual check. Beware however, don't put heavy word load on start up or your app might be killed automatically if it fails to become active in reasonable amount of time.

How to make network call when the app is in background too

I have an app if it goes background it has to make network API call every 1 minute.
So far what I studied suggests it's not possible.
Is there any way or workaround to do it But I couldn't find any so far ?
Thanks
You can utilize background fetch api introduced in ios 7, there are few limitation's while using this api but overall it is a good way of updating apps in the background. limitations including but not limited to only 30 seconds time is allotted, cannot execute when battery is low, for the most part cannot set exact time as system decides it, for more info go through these articles
http://www.appcoda.com/ios7-background-fetch-programming/
http://code.tutsplus.com/tutorials/ios-7-sdk-working-with-background-fetch--mobile-20520
http://www.devfright.com/ios-7-background-app-refresh-tutorial/
Hope it helps !

grand central dispatch, operation queues, async, view will and did disappear

Hopefully these questions will seem helpful to people out there. I've been learning objective c, mostly from this book, which I found to be amazing and helpful even for a noob. My questions all have to deal with this:
What happens to the queue when the user changes the view? I can't seem to find a good explanation anywhere.
From my understanding, using the NSOperation and its queue, you can always cancel it using the "cancel"...but what if you don't want it to cancel? What if, say a user selects multiple images to upload to the server, and you create a queue with the order, and the user switches to a new view controller? This might be time consuming, especially on a slow mobile network. I remember reading somewhere that iOS gives around 20 seconds extra time for a method to finish its work, but I think that's only when the app enters to the background.
For the GCD, there is no cancel method...so what happens in the background if you use async? I guess if you don't have a response to the queue, I mean you don't update the UI in any way, shouldn't the queue finish since it's sent to another thread?
I'm sure there is much more that I don't understand as far as threading goes, but I hope I made my question clear. And please please don't tell me to use the AFNetworking stuff...I tried using all those keychain wrappers out there and it all failed. Thanks to the book, the straight Apple code from the book did everything easily. I would rather learn the basics first before using the easier way out.
I would really appreciate if someone took the time to talk about this. Thanks in advance!
Your concern about only having a set amount of time to finish tasks only applies to when you switch away from your app to another app. And in that case you can use the beginBackgroundTaskWithExpirationHandler method so your app can request time to finish those tasks if your app happens to go into background. See the Executing a Finite-Length Task in the Background section of the App States and Multitasking section of the iOS App Programming Guide for more information.
But if you're still within your app (whether you transitioned to another view controller or not), anything you've added to your operation queue will continue to run until you cancel those operations (or the app is suspended or terminated). Likewise, anything you've added to a GCD queues will continue to run until the app is suspended or terminated.
In both of these scenarios, the above beginBackgroundTaskWithExpirationHandler will give you a few minutes to finish your queued tasks/operations after your app goes into the background.

On iOS, can you begin work on the background thread before applicationDidEnterBackground is called?

I'm updating some content in my app and I want that to finish up when the user switches out of the app. It seems like I have to stop my currently-running update and start another one in the applicationDidEnterBackground method. It would be much more convenient if I could mark some work as something I want to run in the background before that method is called.
Here's the scenario:
I'm trying to update content and start running a SQL update that takes a bit of time. (More than the five seconds you have to return from applicationDidEnterBackground.)
The user leaves the app. The current update is suspended, but I really want it to finish.
I can start a new update which picks up where the other left off, but if the user switches back into the app I have SQL-level concurrency issues.
Is the only option to break down the SQL queries to smaller batches so I can switch over cleanly in the applicationDidEnterBackground callback? It almost doubles the execution time. (I'm not worried about the OS killing my background task, resume is handled fine.)
Ideally I'd be able to have the existing work continue seamlessly in the background (at the pleasure of the OS), is that possible? Are there better options?
(I've read the Programming Guide's section on executing background tasks.)
You can continue to run your current threads. You don't have to stop any of them and start new one.
The only thing which you need to do, if to use beginBackgroundTaskWithExpirationHandler (as proxi mentioned) when you entering background and use endBackgroundTask when you are done. This method gives your application up to 10 minutes of execution. UI of your application won't be accessible (since a user switched to another app), but all threads of your app will continue to run. System will pause all threads when your will do endBackgroundTask or 10 minutes will expire.
I would organize it like this
Have you processing threads running
In applicationDidEnterBackground call beginBackgroundTaskWithExpirationHandler.
Save UIBackgroundTaskIdentifier somewhere accessbile.
At the end of your processing thread, check whether UIBackgroundTaskIdentifier isn't 0 and if it's not, call endBackgroundTask. Set UIBackgroundTaskIdentifier to zero.
If I understand right, you just have to wrap your long-running operation into beginBackgroundTaskWithExpirationHandler block. See the method's documentation for details on how to use it.

how to use GANTracker for long running iOS apps, including background audio

My App is typically run overnight as a baby monitor, either as foreground app, or with background audio running.
Goals:
Track total app startups ie. active user count.
Track total usage time in foreground vs background and total session time.
Track various page-views if they navigate the settings screens.
As recommended, I start the tracker in didFinishLaunchingWithOptions, and track my first ViewController as my first 'page-view'. My App might stay on this page then for the next 8 hours...
A couple of issues then appear:
When do I call stopTracker and what does it do? I'm hoping that it terminates the tracking session. But since google kindly hid their code in a static lib, I have no idea what's going on under the covers, and the .h doesn't say much. First instinct is to put stopTracker in applicationWillResignActive however, if the user decides to enable background audio my app is still running...
Next I read that a session can timeout after 30mins with no new pageviews, or at midnight. I could set a repeating timer to send the same page-view every 20mins, that should keep my session alive, at least until midnight, but then my page views are going to be much larger? unless it's smart enough to know I'm on the same page with every call. google analytics blog
[Update: each call seems to be counted as a new pageview, and numbers are thus skewed, so still an issue how to handle this]
If my timer above runs past midnight and the session has expired, I'm going to end up with a new session and double the actual active user count?
If I do call stopTracker in applicationWillResignActive, will the next call to track a page-view restart the tracker? or do I need to call startTrackerWithAccountID again?
If instead I start the tracker in applicationDidBecomeActive, I lose the session that might have been running in the background.
[update: this seems to be the best approach so far, but testing is very slow due to time lag on analytics reports, I will report back soon]
PS EasyTracker doesn't seem to handle this any better.
I got this working by using a pageview called 'Backgrounded', and when the user has selected no background functionality, then instead the app is calling stopTracker. I see multiple hits, with an average session of 20mins, but i can multiple pageview by time to see total time for goal 2. I found two solutions for goal one, events (which were not exposed in easy tracker), and also in my applicationDidBecomeActive (if it's not a restore of backgrounded app) then i track a pageview for AppStarted. I ended up wrapping the whole thing in a utility class and rolled it into a couple of my apps, so will be interesting too see the results. If anyone else tries this, you might want to think about using the custom variables too. I added my app version to this, so I can also monitor how many users are migrating to the latest app releases.

Resources