How to run operations in background while an iOS application is in foreground
Asked Answered
E

5

12

I have a JSON file populated with strings data in Documents Directory. In user Interface of application there is a UIButton. On button press, a new string appends into the JSON file.

Now I am looking for any iOS Service that helps me to send these strings (from JSON file) to the server using swift. And this service should be totally independent of my code. The point is when I press a UIButton, the first step is a string is saved to the JSON file then service should take this string and send it to server if Internet is available.

When a string sent successfully, it should be removed from the JSON file. This service should track after every 30 seconds if there is any string saved into JSON file, then send it to server.

I Googled and found background fetch but It triggers performFetchWithCompletionHandler function automatically and I cannot know when iOS triggers it. I want to trigger this kind of service my self after every 30 seconds.

Elanorelapid answered 1/8, 2016 at 14:10 Comment(6)
Did you found some solution for your request? Thanks!Udine
how / why would it be independent of your app?Comminate
If you're looking for a daemon/service, then you're out of luck, iOS doesn't support custom apps that indefinitely run.Both
Thank you every body I have solved my problem by just using NSTimer with time interval function, which calls my own created syncService class methods after every 30 secsElanorelapid
@xikitidistant did my answer help you? It looks like it helped the OP.Atomicity
yes @xikitidistant your answer provides a same approach That I have used. One more Thing is that I had solved my problem early than you with the same functions application.beginBackgroundTask application.endBackgroundTask you provided. But you posted an Answer early than me This is why I have Tic marked your Answer. You can also see my Answer posted below. Thank you again jalElanorelapid
A
13

Review the Background Execution portion of Apple's App Programming Guide for iOS.

UIApplication provides an interface to start and end background tasks with UIBackgroundTaskIdentifier.

In the top level of your AppDelegate, create a class-level task identifier:

var backgroundTask = UIBackgroundTaskInvalid

Now, create your task with the operation you wish to complete, and implement the error case where your task did not complete before it expired:

backgroundTask = application.beginBackgroundTaskWithName("MyBackgroundTask") {
    // This expirationHandler is called when your task expired
    // Cleanup the task here, remove objects from memory, etc

    application.endBackgroundTask(self.backgroundTask)
    self.backgroundTask = UIBackgroundTaskInvalid
}

// Implement the operation of your task as background task
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    // Begin your upload and clean up JSON
    // NSURLSession, AlamoFire, etc

    // On completion, end your task
    application.endBackgroundTask(self.backgroundTask)
    self.backgroundTask = UIBackgroundTaskInvalid
}
Atomicity answered 15/9, 2016 at 14:18 Comment(3)
Note that this will execute in the foreground if the app is not backgrounded until the work finishes.Both
Great point @Cristik. I think this is the closes solution to what the OP is asking for due to Apple restrictions.Atomicity
Thanks friend I have already solved my problem by the same approach you provided.Elanorelapid
E
4

What I have done is I just uses the approach discussed by JAL above.

these were the three methods which I used

    func reinstateBackgroundTask() {
        if updateTimer != nil && (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
    }

where updateTimer is of type NSTIMER class The above functions are in my own created class named "syncService" This class has an initialiser which is

init(){
  NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.reinstateBackgroundTask), name: UIApplicationDidBecomeActiveNotification, object: nil)

        updateTimer = NSTimer.scheduledTimerWithTimeInterval(30.0, target: self, selector: #selector(self.syncAudit), userInfo: nil, repeats: true)
        registerBackgroundTask()

 }

Then I just called this class and the whole problem is solved.

Elanorelapid answered 20/9, 2016 at 11:20 Comment(0)
P
3
 DispatchQueue.global(qos: .background).async { // sends registration to background queue

 }
Pistole answered 5/1, 2018 at 19:32 Comment(0)
M
1

Please refer NSURLSessionUploadTask might it help you.

Merl answered 20/9, 2016 at 7:4 Comment(0)
I
0

Here is the swift 4 version of the answer by JAL

  extension UIApplication {
  /// Run a block in background after app resigns activity
   public func runInBackground(_ closure: @escaping () -> Void, expirationHandler: (() -> Void)? = nil) {
       DispatchQueue.main.async {
        let taskID: UIBackgroundTaskIdentifier
        if let expirationHandler = expirationHandler {
            taskID = self.beginBackgroundTask(expirationHandler: expirationHandler)
        } else {
            taskID = self.beginBackgroundTask(expirationHandler: { })
        }
        closure()
        self.endBackgroundTask(taskID)
    }
  }

  }

Usage Example

UIApplication.shared.runInBackground({
        //do task here
    }) {
       // task after expiration.
    }
Insomuch answered 22/6, 2018 at 6:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.