How to Extending 'backgroundTimeRemaining' more time? - ios

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.

Related

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

can't start beginBackgroundTask swift 3

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.

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.

Local Notifications in iOS without running the app

My application needs to have more than 10 local notifications at different time (not recurring) on daily basis. According to iOS official docs, i can only schedule 64 notifications. I have tried solutions from this and several others articles on the web but found no working solution.
Is there any way i can schedule the Local notifications at different times even if my app is not running for several days (or killed)?
There is no direct way For doing this.
If you want to do it anyhow(not proper solution, just a patch), then just go via following way.
wake up the app in background - which can be done by using starting location manager, which will wake up your app in background when location get updated, at that time you can do whatever you like with local notification or any other things.
Before applying this method - make sure that - this is too much battery consuming way + not proper way. Your app might get rejected from apple if it is using too much battery.
Read following details(copied from other question from stackoverflow):
An app can be woken by a significant location change, if the app has indicated that it wants to monitor such events.
See: [CLLocationManager Docs][1]
Look for a method called startMonitoringSignificantLocationChanges. If a significant location change occurs while your app is not in the foreground or isn't running at all, your application will be launched in the background, allowing the app to perform background-only operations (e.g. no view code will run).
When you want your app to work in background even when it is killed then you have to enable 'Background Modes' from your Project's Capabilities and fire your Local Notifications method with specific times through that.
Here is a little code snippet to get you started:
var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
override public func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.reinstateBackgroundTask), name: UIApplicationDidBecomeActiveNotification, object: nil)
}
//MARK: Background Task / Local Notifications / Checkin
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func reinstateBackgroundTask() {
if backgroundTask == UIBackgroundTaskInvalid {
registerBackgroundTask()
}
}
func registerBackgroundTask() {
backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler {
[unowned self] in
self.endBackgroundTask()
}
assert(backgroundTask != UIBackgroundTaskInvalid)
}
func endBackgroundTask() {
NSLog("Background task ended.")
UIApplication.sharedApplication().endBackgroundTask(backgroundTask)
backgroundTask = UIBackgroundTaskInvalid
}
func dosomething() {
registerBackgroundTask()
//Fire Local Notifications accordingly…
//Use NSTimer if you want it with specific time intervals
}
Have a look at this wonderful Raywenderlich Tutorial about Background Modes in iOS.
Apple's Documentation

Resources