can't start beginBackgroundTask swift 3 - ios

Sorry I am stuck, but I am trying to start background task (XCode8, swift 3)
Example from here: https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html#//apple_ref/doc/uid/TP40007072-CH4-SW3
In AppDelegate.swift:
func applicationDidEnterBackground(_ application: UIApplication) {
var bgTask: UIBackgroundTaskIdentifier = 0;
bgTask = application.beginBackgroundTask(withName:"MyBackgroundTask", expirationHandler: {() -> Void in
print("The task has started")
application.endBackgroundTask(bgTask)
bgTask = UIBackgroundTaskInvalid
})
}
The app has never shown "The task has started" message. What am I doing wrong?

Your use of the background task is all wrong.
Waiting for the expiration handler to be called to call endBackgroundTask is poor practice and makes your app waste more resources than it needs. You should tell iOS immediately when your background task is complete.
So you should do something like:
func applicationDidEnterBackground(_ application: UIApplication) {
var finished = false
var bgTask: UIBackgroundTaskIdentifier = 0;
bgTask = application.beginBackgroundTask(withName:"MyBackgroundTask", expirationHandler: {() -> Void in
// Time is up.
if bgTask != UIBackgroundTaskInvalid {
// Do something to stop our background task or the app will be killed
finished = true
}
})
// Perform your background task here
print("The task has started")
while !finished {
print("Not finished")
// when done, set finished to true
// If that doesn't happen in time, the expiration handler will do it for us
}
// Indicate that it is complete
application.endBackgroundTask(bgTask)
bgTask = UIBackgroundTaskInvalid
}
Also note that you should use beginBackgroundTask/endBackgroundTask around any code in any class that you want to keep running for a short period time even if the app goes into the background.
In this setup if the task isn't finished while the while loop is still working then the expirationHandler is called in parallel on a different thread. You should use the handler to stop your code and allow it to reach the application.endBackgroundTask(bgTask) line.

The expiration handler block gets called after a period of time in background (usually 5 minutes or so).
This is meant to be used to write the clean up logic if in case you background task is taking a lot of time to complete.
There is nothing wrong with your code, you just need to wait in the background for the background task to expire.

Related

How to Extending 'backgroundTimeRemaining' more time?

I wanna extend backgroundTimeRemaining more than 30 seconds
and according to Apple
"The value is valid only after the app enters the background and has started at least one task using beginBackgroundTask(expirationHandler:) in the foreground.
System conditions may end background execution earlier, either by calling the expiration handler, or by terminating the app."
so I try to add and edit but it can't work
here's what I tried
//MARK:- BeginBackgroundTask
func registerBackgroundTask() {
backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
self?.endBackgroundTask()
print(self!.beginTime)
}
//TODO: Add new background time ex: 60 sec
var backgroundTimeRemaining: TimeInterval {
get{
return 60
}
}
assert(backgroundTask != .invalid)
}
//MARK:- EndBackgroundTask
func endBackgroundTask() {
print("Background task ended.")
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
backgroundTimeRemaining is informational to your app. The app does not control how much time the system gives it. You can request some background time in order to finish up some user-requested action, and you may receive it, but you don't have any control over how much time. You will need to redesign to not require this.
The point of beginBackgroundTask is to mark finite-length activities that, if the app were to go into the background in the middle, it would be useful to get a few extra seconds to finish up. If, for example, you are starting background tasks in willEnterBackground, or you are not calling a balancing endBackgroundTask in a timely manner, you are probably misusing the system and the system will tend to not give you background time at all.
See Advances in App Background Execution for Apple's latest guidance on background execution.

In Swift what is purpose of using UIBackgroundTaskIdentifier. how below mentioned code will execute

self.backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: {
print("animateRightToLeft: went here")
if let indentifier = self.backgroundTaskIdentifier {
print("animateRightToLeft: stop here")
UIApplication.shared.endBackgroundTask(indentifier)
}
})
My App auto killed after some time if App goes background.
Can some one advice is it because of the above code?
It would be much easier to help you if you explain what you are trying to do? The code you provided will only allow your app to execute code in background for limited amount of time (currently 180 seconds on my iPhone 7).
Detailed:
Once you call beginBackgroundTask, you are given a timer which starts running after your app goes to background. While that timer is running, your app will be executing code even in background. When this timer runs out, or you call endBackgroundTask, your code will stop executing in background. Also if that timer runs out before you called endBackgroundTask, your expiration handler will be called and you should call endBackgroundTask there.
Please note that the code you wrote in the expirationHandler will be called only if you don't call endBackgroundTask before timer runs out.
You can use this code to test how it all behaves, e.g. if you run it as is, app will print backgroundTimeRemaining in the console even when in background. If you comment beginBackgroundTask your app will not print anything after it goes to background.
private var backgroundTaskIdentifier: UIBackgroundTaskIdentifier?
var timer: Timer?
#IBAction func buttontapped(_ sender: Any)
{
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block:
{
(timer) in
NSLog("$$$$$ Time remaining: \(UIApplication.shared.backgroundTimeRemaining)")
})
self.backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler:
{
NSLog("$$$$$ Timer expired: Your app will not be executing code in background anymore.")
if let indentifier = self.backgroundTaskIdentifier
{
UIApplication.shared.endBackgroundTask(indentifier)
}
})
NSLog("$$$$$ start")
DispatchQueue.main.asyncAfter(deadline:.now() + 30)
{
NSLog("$$$$$ end")
if let indentifier = self.backgroundTaskIdentifier
{
UIApplication.shared.endBackgroundTask(indentifier)
}
}
}
From Docs beginBackgroundTask(expirationHandler:)
This method requests additional background execution time for your app. Call this method when leaving a task unfinished might be detrimental to your app’s user experience. For example, call this method before writing data to a file to prevent the system from suspending your app while the operation is in progress. Do not use this method simply to keep your app running after it moves to the background.
Each call to this method must be balanced by a matching call to the endBackgroundTask(_:) method.
My App auto killed after some time if App goes background , is it because of the above code?
no it isn't the above snippet only asks for additional time until task is finished , your app will be terminated anyway

How to execute a task even when the application is in background or lock mode till it finishes

Below code i have used to write certain task in background mode.But this executes only for 3mins after it ends task. So i would like to execute particular task till it finishes.
func registerBackgroundTask() {
backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
self?.endBackgroundTask()
}
assert(backgroundTask != UIBackgroundTaskInvalid)
}
func endBackgroundTask() {
print("Background task ended.")
AppConstants.sharedInstance.scheduleLocalNotification(message: "Background task ended.", title: "status!")
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = UIBackgroundTaskInvalid
}
self.registerBackgroundTask()
self.perform(#selector(self.backgroundTimer(challengeId:)), with: "\(tag)", afterDelay:600)
As found in this doc, only pre-defined (by Apple) tasks (in Table 3-1 of the doc) are allowed to run indefinitely. You have no way to execute your task till it finishes. But if you are targeting jail broken devices, you can do it by writing daemon. This seems not the case for you. So, you have no luck.

swift ios 10 execute code asynchronously or in the background

When user launch the app or finish editing the data I need to update local notifications, basically it takes around 2-3 seconds in async way. I need to make sure that this code executes even if app leave foreground. What I have now:
func buildLocalNotifications()
let dq = DispatchQueue.global(qos: .userInteractive)
dq.async {
//recreate the notifications
}
}
And I can call this method from didFinishLaunchingWithOptions or when user save the form and everything works like a charm while app stays active for more then 3-4 seconds and its not blocking UI of course.. but if user lock the screen or terminate the app - this code won;t finished and notifications won't be created. How to safely execute sensitive code?
What is coming on my mind - show up a loader while performing this action - but it will block the user interaction
Ok I found the solution for the task which requires some time and should not be interrupted when app leaves foreground.
So we need beginBackgroundTask and endBackgroundTask
Small manager which you can use to execute code even when app is not in foreground
class BackgroundTaskManager {
let backgroundDQ = DispatchQueue.global(qos: .background)
var backgroundUpdateTask: UIBackgroundTaskIdentifier!
init(withName: String) {
self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(withName: withName) {}
}
/* Using completion handler to know when code is done*/
func runBackgroundTask(withCode: #escaping (_ cH: #escaping () -> Void) -> Void)
{
backgroundDQ.async {
withCode() {
self.endBackgroungTask()
}
}
}
func endBackgroungTask() {
if backgroundUpdateTask != nil && backgroundUpdateTask != UIBackgroundTaskInvalid {
UIApplication.shared.endBackgroundTask(backgroundUpdateTask)
backgroundUpdateTask = UIBackgroundTaskInvalid
}
}
}
And you can use it like
let taskManager = BackgroundTaskManager(withName: "LocalNotifications")
taskManager.doBackgroundTask() { (cH) in
//Your code goes here
//Send back completion handler so system knows when to finish background task
cH()
}
More information you can find on the Medium
If you want to make sure your code gets executed even if the user closes your app, you need to call your function in applicationWillTerminate. However, you only have ~5 seconds to execute code, before the system closes your app, so asynchronous execution is not encouraged here. It also doesn't matter if you execute code synchronously, since the user already quit your app, so you won't be blocking any UI updates.
Try to excute your code in background
DispatchQueue.global(qos: .background).async {
// your code here
}

Beacon range in background

I'm developing an app which connects to the beacons. I'm able to run the app and also to detect beacons when app is in background (I send local notifications in the didRangeBeacons method and I receive them). I need to run a piece of code in the background when a beacon is detected. How can I do? I tried to write my Alamofire call exactly after sending the local notification, but nothing happens. Some suggestions?
When an app is in the background and it gets a didRangeBeacons callback, it only gets 5 seconds to run by the operating system before it is suspended. This will close any web service connections that are open at that time. You can extend this background running time from 5 seconds to 180 seconds upon request. Below is an example in Swift 3 that shows how to do that.
var threadStarted = false
var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
func extendBackgroundRunningTime() {
if (self.backgroundTask != UIBackgroundTaskInvalid) {
// if we are in here, that means the background task is already running.
// don't restart it.
return
}
print("Attempting to extend background running time")
self.backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "DummyTask", expirationHandler: {
UIApplication.shared.endBackgroundTask(self.backgroundTask)
self.backgroundTask = UIBackgroundTaskInvalid
})
if threadStarted {
print("Background task thread already started.")
}
else {
threadStarted = true
DispatchQueue.global(priority: DispatchQueue.GlobalQueuePriority.default).async {
while (true) {
// A dummy tasks must be running otherwise iOS suspends immediately
Thread.sleep(forTimeInterval: 1);
}
}
}
}
By adding code like this, it is much more likely that your web service call will complete before iOS suspends your app.
You can call the extendBackgroundRunningTime() from your didRangeBeacons or didEnterRegion methods.
You only have a limited time for doing stuff in background when you get the didEnter/didRange callback.
You should checkout background tasks to get more time in background to call your server.

Resources