13

I'm creating an application for my school which should check every n minutes if there is a new mark on the website. To do this when the user login for the first time, the number of the actual mark is saved in the "UserDefaults". When app is terminated, after n minutes, the number of mark is recounted and compared with the previous one and send a notification in case the number is changed.

What I'd like to know if there is a way to perform this task. I've tried to create a timer in -applicationWillTerminate- but it's fired only once. This is what I tried:

func applicationWillTerminate(_ application: UIApplication) {
    DispatchQueue.main.async {
        self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(AppDelegate.findMark), userInfo: nil, repeats: true)
        self.timer.fire()
    }
}

Selector findMark is the task.

Thanks in advance!

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Sellone Ciompi
  • 143
  • 1
  • 2
  • 10
  • It's not possible to do it when the app is terminated. Please check my answer. Feel free to ask any doubts :) – KrishnaCA Nov 06 '16 at 13:17
  • Do you intend to run the app for infinite time in background or just when the app is terminated? – Annie Gupta Nov 06 '16 at 13:18
  • What i've understood is that you want to run it for infinite time in background and whenever the counter gets updated for the number of mark, you want to send notification, is that what you intend to do? – Annie Gupta Nov 06 '16 at 13:19
  • yes, @Sharpkits..infinite time – Sellone Ciompi Nov 06 '16 at 13:28
  • See: http://stackoverflow.com/a/40328936/1457385 – shallowThought Nov 06 '16 at 13:36
  • Why does the app need to terminate? If the concern is CPU cycles or memory, iOS takes care of that. If it's a matter of some login, maybe rethink that piece of logic. Several apps run "infinitely" and can ping a web server on scheduled times. –  Nov 06 '16 at 13:51

3 Answers3

12

You have two options

  1. Background App Refresh
  2. Silent push notifications

Easiest one is Background App Refresh. Because later one needs a server to send the notification. You can check following API for the usage. Basically you set Background Fetch capability on Capabilities/Background Modes of your app. Then from time to time, iOS will wake up your app and call application(_:performFetchWithCompletionHandler:) delegate. You will have around 30-45 seconds to call your function and call completion handler. If you don't finish it on time, iOS will kill your app. If you don't obey the rules, iOS will give you less chances to wake up. For more detailed usage of Background Modes, you may check following tutorial

Meanteacher
  • 2,031
  • 3
  • 17
  • 48
  • I've read that application(_:performFetchWithCompletionHandler:) doesn't perform task every n minutes.. is it right? – Sellone Ciompi Nov 06 '16 at 13:31
  • 1
    Yes. iOS schedules this call and you have no way to know when it will be called. However the more app is used, the more iOS will call this handler. Without push notifications, there is no reliable way to wake up app in n minutes. – Meanteacher Nov 06 '16 at 13:42
  • I would say VoIP push notification makes that possible. – gm333 Feb 19 '20 at 08:22
  • Hmm, then how does Alarmy App do it ? Alarmy does not need WiFi, nor GSM, nor push - how do they do it ? – iKK May 31 '20 at 11:26
  • in iOS 13. VOIP push needs to run CallKit completion handler. For further detail: https://stackoverflow.com/a/57595700/612854 – Alizain Prasla Jun 01 '20 at 10:41
  • 2
    @funct7 that's wrong too, (in iOS 13 at least) a silent ("content_available": true) push notification actually launches the app, even if it's completely killed by the user and not in the app switcher. In that case however you can't do long extended background tasks. Source: just tested this a second ago. – jxd Jun 12 '20 at 10:42
4

It's not possible to perform tasks like described in your question after the app is terminated. As described in the documentation:

App Termination

Apps must be prepared for termination to happen at any time and should not wait to save user data or perform other critical tasks. System-initiated termination is a normal part of an app’s life cycle. The system usually terminates apps so that it can reclaim memory and make room for other apps being launched by the user, but the system may also terminate apps that are misbehaving or not responding to events in a timely manner.

Suspended apps receive no notification when they are terminated; the system kills the process and reclaims the corresponding memory. If an app is currently running in the background and not suspended, the system calls the applicationWillTerminate: of its app delegate prior to termination. The system does not call this method when the device reboots.

In addition to the system terminating your app, the user can terminate your app explicitly using the multitasking UI. User-initiated termination has the same effect as terminating a suspended app. The app’s process is killed and no notification is sent to the app.

Link: https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html

Edit:

You cannot perform any task after the application is terminated. What you can do is get that calculation done from server side and send a Push Notification to the device.

Community
  • 1
  • 1
KrishnaCA
  • 5,615
  • 1
  • 21
  • 31
  • Thanks, and what should I do? Because there are a lot of offline games which send notification when, e.g. a building is terminated.. and this appen even when the app is closed manually with the multitasking manager.. – Sellone Ciompi Nov 06 '16 at 13:19
  • That's PushNotification or LocalNotification. If that is what is needed, then I will add that in the answer. Wait.. – KrishnaCA Nov 06 '16 at 13:22
  • yes, that's what I'm referring to.. but when app is closed the task which send the localNotification is called only once – Sellone Ciompi Nov 06 '16 at 13:23
  • I read your question again. You can't count the marks from the website inside your app when the app is terminated. – KrishnaCA Nov 06 '16 at 13:23
  • You cannot send UILocalNotification when the app is terminated. But, you can send Push Notification from server – KrishnaCA Nov 06 '16 at 13:27
  • and why offline games does it? :/ – Sellone Ciompi Nov 06 '16 at 13:28
  • Have you tried terminating an offline game when the task is in the middle. Try doing that once and check whether you task has progressed or not. Your UILocalNotification should be fired before your app is terminated. They are probably doing that. – KrishnaCA Nov 06 '16 at 13:30
  • O, thank you for the clarification! And sorry for the late reply! – Sellone Ciompi Oct 31 '18 at 10:49
0

Accutally , the answers is yes. But you should not. We can use Location to archive your goal. Accroding to offical document in here: https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html#//apple_ref/doc/uid/TP40009497-CH2-SW10:

Note: If your app is terminated either by a user or by the system, the system doesn’t automatically restart your app when new location updates arrive. A user must explicitly relaunch your app before the delivery of location updates resumes. The only way to have your app relaunched automatically is to use region monitoring or the significant-change location service.

So we can archive it by using Starting the Significant-Change Location Service:

If you leave the significant-change location service running and your iOS app is subsequently suspended or terminated, the service automatically wakes up your app when new location data arrives. At wake-up time, the app is put into the background and you are given a small amount of time (around 10 seconds) to manually restart location services and process the location data. (You must manually restart location services in the background before any pending location updates can be delivered, as described in Knowing When to Start Location Services.) Because your app is in the background, it must do minimal work and avoid any tasks (such as querying the network) that might prevent it from returning before the allocated time expires. If it does not, your app will be terminated. If an iOS app needs more time to process the location data, it can request more background execution time using the beginBackgroundTaskWithName:expirationHandler: method of the UIApplication class.

Call location update in willFinishLaunchingWithOptions and applicationDidBecomeActive Then you can excute your own code right after

[_locationManager startUpdatingLocation];

But is is extremly drain your battery, you may be rejected by app store.

phongnt
  • 711
  • 7
  • 13
  • I have never thought about that. I'll take a look and try to see if app store will reject it or not. – Sellone Ciompi Oct 31 '18 at 10:48
  • @SelloneCiompi Did you try to pass the Apple Review? And did it pass? I am trying to implement kind of the same and asking myself if it has any chance to pass the review process. – Teetz Dec 18 '19 at 09:28
  • Hi @Teetz, yes I did that and Apple Review passed without problem :) I did it more or less 3 years ago and I've never updated it from that day, but I think that Apple policy is still the same... I hope! Let me know :D – Sellone Ciompi Dec 18 '19 at 11:16
  • @SelloneCiompi Thank you very much for the response. It will take some time since that should be a proffessional business app in our case. I will let you know when we passed (or not) the review :) – Teetz Dec 18 '19 at 11:48
  • I do not believe it is true that updating location with a 3km accuracy will drain the battery at all -- such a coarse grained granularity will passively use cell towers for location if that info is available, and will not power up any additional sensors. The battery cost should be negligible. – davidgyoung Nov 17 '21 at 01:12