iOS - How can I schedule something once a day? - ios

I know there is NSTimer.scheduledTimerWithInterval
Which is used like this:
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
func update() {
// Something cool
}
But I assume that the view where var timer is living must be living too, ie: can't close that view (am I right?)
How can I schedule something once a day in iOS, eg: everyday at 8pm send a notification?
In Android I've achieved this the following way:
I've used a thing called AlarmManager, that is similar to the iOS scheduledTimerWithInterval but for large intervals, which is running inside a background service.
At the startup (boot), there is another background service that setups the alarm again. This covers the case when the Android device is shut down (and so the background service is shut down too)
So, in iOS, is there something like scheduledTimerWithInterval for large intervals?
Will I need to set again the interval if the iPhone/iPad is rebooted?

Yes, to use NSTimer, the app has to be running either in foreground or background. But Apple is quite particular only allowing certain types of apps to continue to run in the background (in an effort to make sure we don't have apps randomly running on their own prerogative and killing our batteries in the process and/or affecting our performance while using the device).
When you say "notification", do you really mean notifying the user of something?
In that case, the alternative here is to create a UILocalNotification, which is a user notification (assuming they've granted your app permission to perform notifications), which is presented even when your app is not running.
For example, to register for local notifications:
let application = UIApplication.sharedApplication()
let notificationTypes: UIUserNotificationType = .Badge | .Sound | .Alert
let notificationSettings = UIUserNotificationSettings(forTypes: notificationTypes, categories: nil)
application.registerUserNotificationSettings(notificationSettings)
And then to schedule the repeating notification:
let notification = UILocalNotification()
notification.fireDate = ...
notification.alertTitle = ...
notification.alertBody = ...
notification.repeatInterval = .CalendarUnitDay
application.scheduleLocalNotification(notification)
For more information, see the Local and Remote Notification Programming Guide.
Or do you mean initiating some process, such as fetching data from a remote server.
If you want the app to fetch data even if your app isn't running, you can use background fetch. See Fetching Small Amounts of Content Opportunistically in the App Programming Guide for iOS.
Note, with background fetch, you don't specify when data is to be retrieved, but rather the system will check for data at a time of its own choosing. It reportedly factors in considerations ranging from how often the user uses the app, how often requests to see if there is data result in there actually being new data to retrieve, etc. You have no direct control over the timing of these background fetches.

Related

How to run iOS app in background forever?

I am making an iOS application for myself only. I need to execute certain code every 30 minutes when application is in background.
As I am only user of this app, don’t need to worry about batter percentage and apple review process. I can use any/all background modes VOIP, music, etc.
Is is possible to run that code in background every 30 minutes?
Kindly guide me the direction.
Its posible.
One way to do it is to create a fake VPN packet tunnel extension. And put your code in VPN Manager class.
VPN extension part will keep running while your app is in background or even force quite by user.
You can write your code in this method
NEPacketTunnelProvider
override func startTunnelWithOptions(options: [String : NSObject]?, completionHandler: (NSError?) -> Void) {
fetchData()
}
func fetchData() {
// Do not use the NSTimer here that will not run in background
let q_background = DispatchQueue.global(qos: .background)
let delayInSeconds: Double = 300.0 // seconds
let popTime = DispatchTime.now() + DispatchTimeInterval.seconds(Int(delayInSeconds))
q_background.asyncAfter(deadline: popTime) {
// Fetch your data from server and generate local notification by using UserNotifications framework
fetchData()
}
}
Why not go with Background Fetch?
It is available for apps likes News or Social media, so that apps can have the latest data even before the user interaction. It allows periodic background execution as is.
A simple call will fetch data every hour.
// Fetch data once an hour.
UIApplication.shared.setMinimumBackgroundFetchInterval(3600)
Lastly its not a workaround or any private API. Your app will be accepted by appstore as well.

Show Local Notifications When App is Killed or in Background

I have an HQ Answer bot that is in the form of an iOS app. Basically, I have the entire bot completed and it runs once the HQ Trivia websocket opens, however I am having issues with the last step, which is to send the best answer from the bot to the user in the form of a push notification, since they will be using the HQ app when the game is live and the bot is running. In my viewDidLoad I have the following code:
override func viewDidLoad() {
super.viewDidLoad()
getSocketURL()
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: Notification.Name.UIApplicationWillResignActive, object: nil)
}
getSocketURL() checks to see if the game is live and if so it runs the bot, however I want this to operate when the user has the app killed or it is in the background.
Finally here is where the notification should be sent locally when the bot finds the correct answer:
private func getMatches()
{
let options = [answer1, answer2, answer3]
AnswerController.answer(for: question, answers: options) { answer in
print("Predicted correct answer: \(answer.correctAnswer)")
print("-----")
self.bestAnswer = answer.correctAnswer
self.sendNotification(question: self.question, answer: "Best Answer: \(self.bestAnswer)") // Send the user a notification
self.updateLabels()
}
}
To sum up, I am looking for a way that will let the bot run whenever the game is live without the user having the app open and in the foreground, since they will be in the HQ app. Is there any way to do this with local notifications and if no what steps would I have to take to do something like this using something like Firebase, that is if I still want the bot to run locally on the user's phone.

Issues regarding to Timer running in the background iOS 10

I am making an app that helps people with tracking down the working intervals.
What I need is the timer should run at least 30 minutes regardless of the app is in foreground or background.
func startFocus() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(Pomodoro.focusIntervalCounter), userInfo: nil, repeats: true)
}
func focusIntervalCounter() {
dynamic_focusIntervalSecond -= 1
focusDelegate?.updatingFocusCountingDown(timeStamp: seconds2Timestamp(intSeconds: dynamic_focusIntervalSecond), secondLeft: dynamic_focusIntervalSecond)
if dynamic_focusIntervalSecond == 0 {
timer.invalidate()
focusDelegate?.focusCountingDowndid(end: true)
}
}
focusIntervalCounter() should invalid the timer when the `dynamic_focusIntervalSecond` is 0.
It works fine when the program is in the foreground, but after the screen is shut, the timer just works a little while and stop.
Is this any approach to continue the timer's counting?
Thanks in advance.
No, you can run background tasks for up to 5 minutes, use the background update functionality to trigger app refresh, or use notifications to trigger background actions. There is no way in iOS to guarantee that any code will be run consistently in the background after 30 minutes. Local notifications will enable your code to run after the user selects an action in a notification. Silent push notifications can run some code in the background open receipt, but require an internet connection and are not guaranteed to be delivered.
See this question for more info:
iOS Timer in the background
No, You cannot run Timers in background mode. You can create a UIBackgroundTaskIdentifier which will give you 180sec as i have observed, i'm not sure it might vary with OS version.
You can try scheduling local notifications for 30 mins.
You can enable back ground modes if you're using push notification, Airplay, Location, VOIP apps, Bluetooth, News stand, Background fetch, for more details read this apple developer document BackgroundExecution
Some things are not possible in background, Have you switched your project to enable the background modes? You can reed more about it here

How to run a service in iOS background to receive local notification?

I am working on app where there is an MQTT connection with the server and server is sending some values related to device and UI changes accordingly. But when app is in background user should get local notification that certain values are changed. I know background service are not allowed in iOS but I want to make sure that is that there is no way to achieve this.
I successfully added local notification with app in background by UIApplication.shared.beginBackgroundTask but it's only work for 3 min exact after that apple terminates the app.
func registerBackgroundTask() {
backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
self?.endBackgroundTask()
}
assert(backgroundTask != UIBackgroundTaskInvalid)
}
func endBackgroundTask() {
print("Background task ended.")
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = UIBackgroundTaskInvalid
}
And just calling self.registerBackgroundTask() makes the app runnable in background for three min.
Next that I am going to try is that background fetch and widget to run service, Here I just want some suggestion that is there any chance that one of above two will work ?
It sounds like "Communicating with an External Accessory" would be the background mode that fits your application.
See Apple Docs for reference.
You have to activate Backround Mode for your project and set the value to "external-accessory". Then you can do ongoing small downloads in background. Apple mentions heart rate monitors as an example.
Please note that continous background polling is waste of energy and would deplete battery quickly. Check if this is really needed for your application. If the user just needs infrequent notifications/alarms, remote notifictions would be a much better solution. I use remote notifications in my own projects and it works very smooth and reliable. Additional benefit is, that it would wake up an app even if the user has closed it before.
For more than 3 Minute. You will be enable any mode. Otherwise when app will enter in background app. After 3 min.App will not perform any action.

Dynamic location based local notifications Swift iOS

I am trying to fetch some arrival times for buses when a user approaches a stop, I have tested to ensure that the regions are correctly being trigged by sending a basic local notification and I have also tested my web service call to ensure it is working properly.
However I am having a hard time fetching the info then sending a notification.
Here is my code:
var bgTask: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier()
bgTask = UIApplication.shared().beginBackgroundTask {
self.webService?.getStopEstimates(routeStopIds: stopRouteIdSet, routeNameDict: routeNameDict, completion: { (result) in
if result == "error" {
return
}
let notification = UILocalNotification()
notification.fireDate = Date(timeIntervalSinceNow: 1)
notification.alertTitle = region.identifier + " Stop Details"
notification.alertBody = result
notification.soundName = UILocalNotificationDefaultSoundName
UIApplication.shared().scheduleLocalNotification(notification)
})
UIApplication.shared().endBackgroundTask(bgTask)
}
Any one see why it might not be sending? I have enabled background fetch and location services. This is inside didEnterRegion
I did the same thing (when a user approach a store that has a discount he/she will be notified).
First of all I would I would suggest you to download all the data and save them on Core Data or SQLite if that is an option.
Second take a look at Significant Location Change here. It will update your location in the background each 500 meters if you are moving and it will save a lot of battery instead of the didUpdateLocation.
Third after using SLC, on each call, fetch from your Core Data or SQLite the 20 nearest locations of your current location and add them for monitoring(Use Haversine to calculate all the points distances from your current location and then get the 20 nearest). Each time SCL is called update the regions that you monitor(if saving your data offline is not an option I would recommend to send the request at your webservice here. The ideal scenario is that you will send your location to your webservice and it will reply back with the nearest points. then add them for monitoring)
When the didEnterRegion called make sure you have your data downloaded and then create a local notification.
Let me know if you need more details
PS. Have in mind that your app might be terminated (in background) by the system if is consuming a lot of resources. In that case you have to reinitialise everything you need for pulling and showing the data (Networks requests, Location manager etc.) You can detect that the App relaunched because of a location event in AppDelegate's didFinisingLaunchingWithOptions:
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {}
check this example on GitHub because I think your issue here is how you are handling the background task. If the web service call took too long, the background task will be invalidated. I don't know how well you know ObjC but in the example above, you will find a BackgroundTaskManager class handling that for you.
Things you should make sure of are:
Background Fetch in Target Settings (Background Modes section) are
ON.
You have asked and obtained "always" authorization (in plist)
Location Updates in Target Settings (Background Modes section) are ON.
Registered for User Notifications in appDidFinishLaunching in application delegate.
Renew expired background tasks if service took too long.
In the case of calling startUpdatingLocation while your app is in the background, CoreLocation still responds by starting the location updates for your app, but the system will no longer hold an assertion for you, and your app will be suspended once the allowed time in the background is spent. This time is (currently) is 10 seconds. But actually your app is now suspended and can no longer receive the updates. There is a way to extend this duration to (currently) up to 3 minutes by employing beginBackgroundTaskWithExpirationHandler: read more about it in Apple's CoreLocation Documentation
Whether this extended time is enough will depend on your app.
For your use case, Significant Location Change service is quite efficient.
You can start location updates when your app is in the foreground and defer location updates while your app is in the background. You can also read Apple's documentation about Significant Location Change

Resources