CLLocationManager startUpdatingLocation() doesn't work when called in background - ios

I have a particular use case where I want to be able to start location updates when the app is already in the background. Specifically, I want to be able to press a button on my Pebble watchapp, and that causes my companion iOS app to begin location updates.
I am able to successfully start the location updates from my Pebble only if the iOS app is either in the foreground or has just entered the background within the past few seconds (like 5?). After a few seconds has passed with the app in the background, I can no longer start the updates. I know that the updates haven't started, since the backgroundTimeRemaining starts counting down to 0 (and if the updates did start, it stays at the max value). I also have the blue bar at the top that shows when location updates are on ("_____ is Using Your Location") and that also fails to show up after this weird ~5 second threshold has passed.
I've looked at almost all of the related questions on SO, and I've already tried basically everything that's been suggested, such as starting a background task right before starting the location updates. I've also tried delaying the start of the location updates by a couple seconds after starting the background task, and that doesn't work either.
The strange thing is, I also have an Apple Watch app that does the same things as the Pebble app, and the Apple Watch is able to start the location updates without fail all the time. Maybe the Apple Watch can do something special where it unsuspends the iOS app?
This is on iOS 9.
Any kind of help is appreciated, I'll try anything that's suggested.
Update: I put together a quick sample app that demonstrates the issue.
The code is posted below, or you can download the project from GitHub:
https://github.com/JessicaYeh/BackgroundLocationTest
If you try it out, you can see that setting the delay to 2 seconds works, but when the delay is longer (like 10 seconds), the updates don't start.
//
// AppDelegate.swift
// BackgroundLocationTest
//
// Created by Jessica Yeh on 2/18/16.
// Copyright © 2016 Yeh. All rights reserved.
//
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let locationManager = CLLocationManager()
var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
var timer: NSTimer?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
debugPrint(__FUNCTION__)
// Setup location manager
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.allowsBackgroundLocationUpdates = true
return true
}
#objc func checkBackgroundTimeRemaining() {
debugPrint(UIApplication.sharedApplication().backgroundTimeRemaining)
}
#objc func endBackgroundTask() {
if self.backgroundTask != UIBackgroundTaskInvalid {
UIApplication.sharedApplication().endBackgroundTask(self.backgroundTask)
self.backgroundTask = UIBackgroundTaskInvalid
}
}
func applicationWillResignActive(application: UIApplication) {
debugPrint(__FUNCTION__)
}
func applicationDidEnterBackground(application: UIApplication) {
debugPrint(__FUNCTION__)
// Cancel old timer and background task
timer?.invalidate()
endBackgroundTask()
// Start a new background task
backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler {
self.endBackgroundTask()
}
// Start updating location
// let delaySec = 2.0 // successfully starts location update
let delaySec = 10.0 // doesn't start location update
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delaySec * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue(), {
debugPrint("Attempting to start updating location")
self.locationManager.startUpdatingLocation()
})
// Keep checking every 3 sec how much background time app has left
timer = NSTimer.scheduledTimerWithTimeInterval(3.0, target: self, selector: Selector("checkBackgroundTimeRemaining"), userInfo: nil, repeats: true)
}
func applicationWillEnterForeground(application: UIApplication) {
debugPrint(__FUNCTION__)
}
func applicationDidBecomeActive(application: UIApplication) {
debugPrint(__FUNCTION__)
}
func applicationWillTerminate(application: UIApplication) {
debugPrint(__FUNCTION__)
}
}
extension AppDelegate: CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
debugPrint(__FUNCTION__)
}
}

Well, I don't exactly what you tried, but make sure you've turned on the Location updates on your target's Capabilities (Capabilities -> Background Modes) and also, for iOS 9.0, add this:
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"9.0")) {
self.locationManager.allowsBackgroundLocationUpdates = YES;
}
and change the line:
locationManager.requestWhenInUseAuthorization()
to:
locationManager.requestAlwaysAuthorization().
Good luck!

Related

iOS - Start Monitoring Significant Location Change when app is terminated - Not Working

has anyone here ever had location services working, even with the app is not running - using Significant Location Change? Basically, I have the app running, gather location data (confirmed) then closed the app entirely. set up code below.
class LocationService: NSObject, CLLocationManagerDelegate {
public static let shared = LocationService()
var delegate: LocationServiceDelegate?
var locationManager: CLLocationManager!
var currentLocation: CLLocation!
private var startTime: Date?
private let apiService = APIService()
private override init() {
super.init()
self.setUpLocationServices()
}
private func setUpLocationServices() {
self.locationManager = CLLocationManager()
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestAlwaysAuthorization()
self.locationManager.allowsBackgroundLocationUpdates = true
self.locationManager.pausesLocationUpdatesAutomatically = false
self.locationManager.delegate = self
self.locationManager.startUpdatingLocation()
//self.locationManager.startMonitoringSignificantLocationChanges()
}
// in app delegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
print("starting launching options")
return true
}
func applicationWillTerminate(_ application: UIApplication) {
self.locationManager.startMonitoringSignificantLocationChanges()
self.locationManager.stopUpdatingLocation()
}
it is my understanding that both the standard and significant services can run in tandem (the docs say that the significant monitoring will ignore the desiredAccuracy and Filters). However, I did try using a few location manager delegate methods from the main AppDelegate to stop and start the standard and significant services separately and it still doesn't seem to work.
I have all the Usage Descriptions Required and have Location Updates checked under Capabilities for background modes. I also make sure that when getting the privacy prompt I select "Always Allow".
I went driving around last night for 10 mins and well over 500m and the app did not relaunch in the background. Also I tried it under the simulator(iOS 12.2) using Freeway mode(on 208) and still didnt work, even after 30 mins I didnt get a new location event to spawn. Any clue to how to get this to work or anything else I am missing?
I know there are a few past posts regarding this but most of them are old. Didn't know if there was something going on since iOS 8/9 that has changed?

How to fix IUnityGraphicsMetal error when I run Xcode project

I work to integrate an amazing AR model from Unity into Swift 4.
I finally did that using Unity 2018 (version 2018.2.4) and Swift 4.1 (Xcode 9.4.1) but when I run the project on my device (iPhone 7 Plus) is crushing and the error is coming from the path MyProjectName/Unity/Classes/Unity/IUnityGraphicsMetal.h
The error is: Thread 1: EXC_BAD_ACCESS (code=1, address=0x8).
I exported the project from Unity selecting Graphics API as Metal and also as OpenGLES3, none of this helped me.
Also in XCode -> Edit Scheme -> Run -> Options Tab -> Metal API Validation -> I set this one to Disabled, and I still get the same error.
I also update ARKit Plugin (in Unity) to the latest version (1.5) hoping that will fix the problem with UnityGraphicsMetal but apparently not.
Can anyone help me please to fix this error or to guide me to the wright way ?
Here I have 3 screenshots with the errors which maybe can help you more.
Thank you if you are reading this !
EDIT:
Here is the source code for my ViewController which is managing the bridge between Swift and Unity:
import UIKit
class ViewController: UIViewController {
#IBOutlet var rotateSwitch: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.startUnity()
NotificationCenter.default.addObserver(self, selector: #selector(handleUnityReady), name: NSNotification.Name("UnityReady"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleUnityToggleRotation(_:)), name: NSNotification.Name("UnityToggleRotation"), object: nil)
}
}
#objc func handleUnityReady() {
showUnitySubView()
}
#objc func handleUnityToggleRotation(_ n: NSNotification) {
if let isOn = n.userInfo?["isOn"] as? NSNumber {
rotateSwitch.isOn = isOn.boolValue
}
}
#IBAction func handleSwitchValueChanged(sender: UISwitch) {
UnityPostMessage("NATIVE_BRIDGE", "PlayHologram", sender.isOn ? "start" : "stop")
}
func showUnitySubView() {
if let unityView = UnityGetGLView() {
// insert subview at index 0 ensures unity view is behind current UI view
view?.insertSubview(unityView, at: 0)
unityView.translatesAutoresizingMaskIntoConstraints = false
let views = ["view": unityView]
let w = NSLayoutConstraint.constraints(withVisualFormat: "|-0-[view]-0-|", options: [], metrics: nil, views: views)
let h = NSLayoutConstraint.constraints(withVisualFormat: "V:|-75-[view]-0-|", options: [], metrics: nil, views: views)
view.addConstraints(w + h)
}
}
}
Here is the AppDelegate file:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var application: UIApplication?
#objc var currentUnityController: UnityAppController!
var isUnityRunning = false
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
self.application = application
unity_init(CommandLine.argc, CommandLine.unsafeArgv)
currentUnityController = UnityAppController()
currentUnityController.application(application, didFinishLaunchingWithOptions: launchOptions)
// first call to startUnity will do some init stuff, so just call it here and directly stop it again
startUnity()
stopUnity()
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
if isUnityRunning {
currentUnityController.applicationWillResignActive(application)
}
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
if isUnityRunning {
currentUnityController.applicationDidEnterBackground(application)
}
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
if isUnityRunning {
currentUnityController.applicationWillEnterForeground(application)
}
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
if isUnityRunning {
currentUnityController.applicationDidBecomeActive(application)
}
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
if isUnityRunning {
currentUnityController.applicationWillTerminate(application)
}
}
func startUnity() {
if !isUnityRunning {
isUnityRunning = true
currentUnityController.applicationDidBecomeActive(application!)
}
}
func stopUnity() {
if isUnityRunning {
currentUnityController.applicationWillResignActive(application!)
isUnityRunning = false
}
}
}
This seems to be a tricky problem, here's a few things which may help, please try them:
In the Unity Editor -> Project Settings -> Player, there is also a checkbox option called "Metal API Validation", you said that you only set the Xcode option to disabled, perhaps this one needs to be disabled as well. Or a combination of on/off for the two.
Instead of selecting Metal and OpenGLES3, try ticking the checkbox "Auto Graphics API". In one of my projects, I left it on manual with Metal selected, but it wouldn't render at all on iOS, fixed it by turning on automatic.
In Color Gamut, have both sRGB and P3 Wide-colour selected. The iPhone 7 and newer all have displays rated for P3 wide-colour.
Hope this helps!

Update location in Background

I want to get user location even when the user does not use the app.now i can get location after press home button and application goes to background state, but after a few second location update stoped. And when I'm killing the app location update against stoped. this is my code in app delegate.
let locationManager = CLLocationManager()
var LatitudeGPS = String()
var LongitudeGPS = String()
var speedGPS = String()
var Course = String()
var Altitude = String()
var bgtimer = Timer()
func applicationDidEnterBackground(_ application: UIApplication) {
self.doBackgroundTask()
}
func beginBackgroundUpdateTask() {
backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
self.endBackgroundUpdateTask()
})
}
func endBackgroundUpdateTask() {
UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskInvalid
}
func doBackgroundTask() {
DispatchQueue.global(qos: .background).async {
self.beginBackgroundUpdateTask()
self.StartupdateLocation()
self.bgtimer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(self.bgtimer(timer:)), userInfo: nil, repeats: true)
RunLoop.current.add(self.bgtimer, forMode: RunLoopMode.defaultRunLoopMode)
RunLoop.current.run()
self.endBackgroundUpdateTask()
}
}
func bgtimer(timer:Timer!){
print("Fired from Background ************************************")
updateLocation()
}
func StartupdateLocation() {
locationManager.delegate = self
locationManager.startUpdatingLocation()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestAlwaysAuthorization()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = false
}
func updateLocation() {
locationManager.startUpdatingLocation()
locationManager.stopUpdatingLocation()
print("Latitude: \(LatitudeGPS)")
print("Longitude: \(LongitudeGPS)")
print("Speed: \(speedGPS)")
print("Heading: \(Course)")
print("Altitude BG: \(Altitude)")
DispatchQueue.main.async {
print(UIApplication.shared.backgroundTimeRemaining)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
LatitudeGPS = String(format: "%.10f", manager.location!.coordinate.latitude)
LongitudeGPS = String(format: "%.10f", manager.location!.coordinate.longitude)
speedGPS = String(format: "%.3f", manager.location!.speed)
Altitude = String(format: "%.3f", manager.location!.altitude)
Course = String(format: "%.3f", manager.location!.course)
}
}
i think after a few second my application terminated and location update stoped.
I want to after 20 min that application terminated (os or user) stop updating location to keep the battery charge.
where is my problem in location update.
Couple of things to be changed.
Step 1:
Make sure you have enabled location updates background mode in capabilities section of your project as shown below
Step 2:
And when I'm killing the app location update against stoped.
Quoting from apple docs
If you start this service and your app is subsequently terminated, the
system automatically relaunches the app into the background if a new
event arrives. In such a case, the options dictionary passed to the
application(:willFinishLaunchingWithOptions:) and
application(:didFinishLaunchingWithOptions:) methods of your app
delegate contains the key location to indicate that your app was
launched because of a location event. Upon relaunch, you must still
configure a location manager object and call this method to continue
receiving location events. When you restart location services, the
current event is delivered to your delegate immediately. In addition,
the location property of your location manager object is populated
with the most recent location object even before you start location
services.
link : https://developer.apple.com/documentation/corelocation/cllocationmanager/1423531-startmonitoringsignificantlocati
important thing to notice in above statement is
Upon relaunch, you must still, configure a location manager object and
call this method to continue receiving location events.
Meaning, your current location manager will not be of much use and you should create a new one and configure the new instance and call startMonitorSignificantLocationChanges again.
So iOS will send location updates to terminated apps only when you use startMonitoringSignificantLocationChanges.
All that are applicable only if your app was terminated and iOS relaunched it on receiving location update. But if your app is simply in background you need not do any thing on using startMonitorSignificantLocationChanges
on the other hand startUpdatingLocation will work only when app is in background/foreground mode. iOS will stop updating location if your app gets suspended or killed.
If you start this service and your app is suspended, the system stops
the delivery of events until your app starts running again (either in
the foreground or background). If your app is terminated, the delivery
of new location events stops altogether. Therefore, if your app needs
to receive location events while in the background, it must include
the UIBackgroundModes key (with the location value) in its Info.plist
file.
link: https://developer.apple.com/documentation/corelocation/cllocationmanager/1423750-startupdatinglocation
So modify your code
locationManager.startMonitoringSignificantLocationChange()
Ok that was about proper usage of startMonitoringSignificantLocationChanges and startupdatinglocation. Now timer for mistakes in your code.
Mistake 1:
self.bgtimer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(self.bgtimer(timer:)), userInfo: nil, repeats: true)
Using timer to get timely updates on location. Thats not how it works!!! You cant run Timer forever. Timer stops as soon as your app suspends or gets terminated. Location Manager informs the app when location changes and you should rely on that only. You cant run timer to timely check location updates. It won't run in suspended or terminated state.
Mistake 2:
func updateLocation() {
locationManager.startUpdatingLocation()
locationManager.stopUpdatingLocation()
Why start and stopping update locations in subsequent statements? That does not make much sense.
Mistake 3:
func StartupdateLocation() {
locationManager.delegate = self
locationManager.startUpdatingLocation()
Your StartupdateLocation gets called multiple time and every time you called this method you are repeatedly calling startUpdatingLocation on same instance of location manager. You need not do that! You can call startUpdatingLocation or startMonitoringSignificantLocationChange only once.

Run a Swift 2.0 app forever in background to update location to server

I have written the below code that has a timer that calls a callback function every minute. When the app goes to the background I have started another timer that calls the same callback method, but the background timer works for only three minutes.
I understand that Apple allows background tasks for only three minutes. My app is more like a tracking app that tracks the location of the user every minute even when the app is in background, so I need to implement this functionality.
I learned that beginBackgroundTaskWithExpirationHandler is to be used but I don't know whether my implementation is correct.
Note: I have Required background modes in plist toApp registers for location updates.
Any working code or links are much appreciated.
override func viewDidLoad() {
super.viewDidLoad()
timeInMinutes = 1 * 60
self.locationManager.requestAlwaysAuthorization()
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
var timer = NSTimer.scheduledTimerWithTimeInterval( timeInMinutes, target: self, selector: "updateLocation", userInfo: nil, repeats: true)
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
self.latitude = locValue.latitude
self.longitude = locValue.longitude
if UIApplication.sharedApplication().applicationState == .Active {
} else {
backgroundTaskIdentifier = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({ () -> Void in
self.backgroundTimer.invalidate()
self.backgroundTimer = NSTimer.scheduledTimerWithTimeInterval( 60.0, target: self, selector: "updateLocation", userInfo: nil, repeats: true)
})
}
}
func updateLocation() {
txtlocationLabel.text = String(n)
self.n = self.n+1
var timeRemaining = UIApplication.sharedApplication().backgroundTimeRemaining
print(timeRemaining)
if timeRemaining > 60.0 {
self.GeoLogLocation(self.latitude,Longitude: self.longitude) {
results in
print("View Controller: \(results)")
self.CheckResult(String(results))
}
} else {
if timeRemaining == 0 {
UIApplication.sharedApplication().endBackgroundTask(backgroundTaskIdentifier)
}
backgroundTaskIdentifier2 = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({ () -> Void in
self.backgroundTimer.invalidate()
self.backgroundTimer = NSTimer.scheduledTimerWithTimeInterval( 60.0, target: self, selector: "updateLocation", userInfo: nil, repeats: true)
})
}
}
Periodic location updates are a bit tricky in IOS.There's a good thread that discusses this, you can read more here
iOS will terminate your app after a few minutes, regardless if your timer is running or not. There is a way around this though, I had to do something similar when writing an ionic app so you can check out the code for this here, that link has a swift class that manages the periodic location updates in iOs.
In order to get periodic locations in the background, and not drain the battery of the device, you need to play with the accuracy of the location records, lower the accuracy of the location manager setting its desiredAccuracy to kCLLocationAccuracyThreeKilometers, then, every 60 seconds you need to change the accuracy to kCLLocationAccuracyBest, this will enable the delegate to get a new, accurate location update, then revert the accuracy back to low. The timer needs to be initialized every time an update is received.
There's also a way to wake up the app in the background after its been killed by the user, use the app delegate to have the app listen for significant changes in location before its killed. This will wake up the app in the background when the user's location makes a big jump (can be around 200ms). When the app wakes up, stop monitoring for significant changes and restart the location services as usual to continue the periodic updates.
Hope this helps.
Update
In Swift 2 you'll also need:
self.locationManager.allowsBackgroundLocationUpdates = true
You can use library for background location tracking, example of use:
var backgroundLocationManager = BackgroundLocationManager()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
backgroundLocationManager.startBackground() { result in
if case let .Success(location) = result {
LocationLogger().writeLocationToFile(location: location)
}
}
return true
}
It's working when application is killed or suspended.

Swift how to use NSTimer background?

class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
func update() {
println("Something cool")
}
}
It's ok for the Simulator ,I will get continuous "Something cool" through I tapped the home button. But it worked out when I debug the app with my iPhone. I did't get anything when I tapped the home button and make my app run background.
Someone told me I can play a long blank music to make my App run in the background.But I don't know how to play music in the background with swift #matt
You can use beginBackgroundTaskWithExpirationHandler to get some background execution time.
class ViewController: UIViewController {
var backgroundTaskIdentifier: UIBackgroundTaskIdentifier?
override func viewDidLoad() {
super.viewDidLoad()
backgroundTaskIdentifier = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({
UIApplication.sharedApplication().endBackgroundTask(self.backgroundTaskIdentifier!)
})
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "update", userInfo: nil, repeats: true)
}
func update() {
println("Something cool")
}
}
Swift 3.0
backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: {
UIApplication.shared.endBackgroundTask(self.backgroundTaskIdentifier!)
})
This code was inspired by this answer, but I ported to swift.
This apparently only runs for 3 minutes on iOS 7+.
I'm willing to bet that you don't need to run a timer in the background, what you need is to know the time that has elapsed while you were suspended. That is a much better use of your resources than trying to play an empty sound file.
Use applicationWillResignActive and applicationDidBecomeActive to determine how much time has elapsed. First save the fireDate of your timer and invalidate it in applicationWillResignActive
func applicationWillResignActive(application: UIApplication) {
guard let t = self.timer else { return }
nextFireDate = t.fireDate
t.invalidate()
}
Next, determine how much time is left for the timer in applicationDidBecomeActive by comparing the time now to the fireDate you saved in the applicationWillResignActive call:
func applicationDidBecomeActive(application: UIApplication) {
guard let n = nextFireDate else { return }
let howMuchLonger = n.timeIntervalSinceDate(NSDate())
if howMuchLonger < 0 {
print("Should have already fired \(howMuchLonger) seconds ago")
target!.performSelector(selector!)
} else {
print("should fire in \(howMuchLonger) seconds")
NSTimer.scheduledTimerWithTimeInterval(howMuchLonger, target: target!, selector: selector!, userInfo: nil, repeats: false)
}
}
If you need a repeating one, just do math to figure out how many times the timer should have fired while in the background
let howManyTimes = abs(howMuchLonger) / repeatInterval
when I tapped the home button and make my app run background
No, that's a false assumption. When you tap the home button, you do not make your app run in the background. You make your app suspend in the background. Your code does not run when you are in the background.
Apps can get special permission to run in the background just in order to perform certain limited activities, like continuing to play music or continuing to use Core Location. But your app does none of those things, so it goes into the background and stops.
add this code in appdelegate for run a task in background , its working finely,
var backgroundUpdateTask: UIBackgroundTaskIdentifier = 0
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
return true
}
func applicationWillResignActive(application: UIApplication) {
self.backgroundUpdateTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({
self.endBackgroundUpdateTask()
})
}
func endBackgroundUpdateTask() {
UIApplication.sharedApplication().endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskInvalid
}
func applicationWillEnterForeground(application: UIApplication) {
self.endBackgroundUpdateTask()
}

Resources