Send the location to the server every n minutes in the background in swift - ios

i want to sent the location to the server when the app is background, i tried with many way but can't get the solution
I tried with the timer function using the timer and using function
self.bgtimer = Timer.scheduledTimer(timeInterval:60, target: self, selector: #selector(AppDelegate.bgtimer(_:)), userInfo: nil, repeats: true)
RunLoop.current.add(self.bgtimer, forMode: RunLoopMode.defaultRunLoopMode)
RunLoop.current.run()
but i when i stop the timer when the app becomes active
if UIApplication.shared.applicationState == .active {
RunLoop.current.cancelPerformSelectors(withTarget: self.bgtimer)
timer.invalidate()
}
but the app not function properly collection view and back function everything is not working properly

In DidBackground:
stop normalTimer.
start New backgroundTaskTimer
In WillForeground:
stop backgroundTaskTimer.
start normalTimer
Find below sample code:
In Appdelegate:
Declaration:
var backgroundUpdateTask: UIBackgroundTaskIdentifier!
var backgroundTaskTimer:Timer! = Timer()
Implementation:
func doBackgroundTask() {
DispatchQueue.global(qos: .default).async {
self.beginBackgroundTask()
if self.backgroundTaskTimer != nil {
self.backgroundTaskTimer.invalidate()
self.backgroundTaskTimer = nil
}
//Making the app to run in background forever by calling the API
self.backgroundTaskTimer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(self.startTracking), userInfo: nil, repeats: true)
RunLoop.current.add(self.backgroundTaskTimer, forMode: RunLoopMode.defaultRunLoopMode)
RunLoop.current.run()
// End the background task.
self.endBackgroundTask()
}
}
func beginBackgroundTask() {
self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(withName: "Track trip", expirationHandler: {
self.endBackgroundTask()
})
}
func endBackgroundTask() {
UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskInvalid
}
In ApplicationDidEnterBackground:
func applicationDidEnterBackground(_ application: UIApplication) {
//Start the background fetch
self.doBackgroundTask()
}
In ApplicationWillEnterForeground:
func applicationWillEnterForeground(_ application: UIApplication) {
if self.backgroundTaskTimer != nil {
self.backgroundTaskTimer.invalidate()
self.backgroundTaskTimer = nil
}
}

Related

AudioKit Background Audio Battery Usage

I just switched from AVAudio to AudioKit. To get things working I had to enable background audio and since then battery usage is woeful.
What is the best way to clean up and come out of background audio once a sequence or player has come to and end?
Any help would be greatly appreciated.
In your app delegate:
func applicationDidEnterBackground(_ application: UIApplication) {
conductor.checkIAAConnectionsEnterBackground()
}
func applicationWillEnterForeground(_ application: UIApplication) {
conductor.checkIAAConnectionsEnterForeground()
}
In your audio engine, or conductor (as per other AudioKit examples) do:
var iaaTimer: Timer = Timer()
func checkIAAConnectionsEnterBackground() {
if let audiobusClient = Audiobus.client {
if !audiobusClient.isConnected && !audiobusClient.isConnectedToInput {
deactivateSession()
AKLog("disconnected without timer")
} else {
iaaTimer.invalidate()
iaaTimer = Timer.scheduledTimer(timeInterval: 20 * 60,
target: self,
selector: #selector(self.handleConnectionTimer),
userInfo: nil, repeats: true)
}
}
}
func checkIAAConnectionsEnterForeground() {
iaaTimer.invalidate()
startEngine()
}
func deactivateSession() {
stopEngine()
do {
try AKSettings.session.setActive(false)
} catch let error as NSError {
AKLog("error setting session: " + error.description)
}
iaaTimer.invalidate()
AKLog("disconnected with timer")
}
#objc func handleConnectionTimer() {
AKLog("should disconnect with timer")
deactivateSession()
}

call timeout function after duration Callkit

I tried to add a timeout Timer 12 secs after receiving a call with CallKit but it doesn't get fired in Appdelegate when the app is in background. My code:
self.callBackgroundHandlerIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: {
UIApplication.shared.endBackgroundTask(self.callBackgroundHandlerIdentifier!)
self.callBackgroundHandlerIdentifier = UIBackgroundTaskInvalid
})
DispatchQueue.global(qos: .userInitiated).async {
AppDelegate.callAnswerTimer = Timer.scheduledTimer(timeInterval: 12, target: self, selector: #selector(AppDelegate.hangUpOnCallTimeOut), userInfo: nil, repeats: false)
self.callBackgroundHandlerIdentifier = UIBackgroundTaskInvalid
}
func hangUpOnCallTimeOut(){
AppDelegate.timeOutTimer?.invalidate()
AppDelegate.timeOutTimer = nil
if #available(iOS 10.0, *) {
ProviderDelegate.sharedInstance.endCall(uuids: ApplicationDelegate.activeCalls!) { (uuid) in }
}
if self.callBackgroundHandlerIdentifier != UIBackgroundTaskInvalid {
UIApplication.shared.endBackgroundTask(self.callBackgroundHandlerIdentifier!)
}
}
Any ideas what I'm doing wrong?
a) dont add a timer in a block on the background. run the timer on the main queue!
b) dont reset self.callBackgroundHandlerIdentifier too early.
:) try below code
func ... {
self.callBackgroundHandlerIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: {
print("expired!")
UIApplication.shared.endBackgroundTask(self.callBackgroundHandlerIdentifier!)
self.callBackgroundHandlerIdentifier = UIBackgroundTaskInvalid
})
AppDelegate.callAnswerTimer = Timer.scheduledTimer(timeInterval: 12, target: self, selector: #selector(AppDelegate.hangUpOnCallTimeOut), userInfo: nil, repeats: false)
}
}
func hangUpOnCallTimeOut(){
AppDelegate.timeOutTimer?.invalidate()
AppDelegate.timeOutTimer = nil
if #available(iOS 10.0, *) {
ProviderDelegate.sharedInstance.endCall(uuids: ApplicationDelegate.activeCalls!) { (uuid) in }
}
if self.callBackgroundHandlerIdentifier != UIBackgroundTaskInvalid {
UIApplication.shared.endBackgroundTask(self.callBackgroundHandlerIdentifier!)
self.callBackgroundHandlerIdentifier = UIBackgroundTaskInvalid
}
}

Background location update every n minutes

I'm writing an application that requires location updates every n minutes and sends data to a server even when the application is in background mode. I have gone through so many links about this task. As I found the proper solution is to use a timer and make it as a background task.
Concerning to this I have two questions:
How can I implement these regular background location updates? I understand that Apple allows background tasks for an only certain time. So how can I make long-running background timer?
Will apple reject the application with such kind of logics?
inside appdelegate create method like this
func getLocation()
{
locationManager.allowsBackgroundLocationUpdates = true
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
inside didFinishLaunchingWithOptions method in appdelegate
var n = 10 //seconds
var timer = Timer.scheduledTimer(timeInterval: n, target: self, selector: #selector(getLocation), userInfo: nil, repeats: true)
set the delegate method for cllocationmanager
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
locationManager.stopUpdatingLocation()
locationManager.delegate = nil
//get the coordinates or do some code
}
Use following code.This will work for 3 minutes
Call this didenterbackground or foreground
var time = 180
func applicationDidEnterBackground(_ application: UIApplication)
{
self.doBackgroundTask()
}
var timerBack = Timer()
func doBackgroundTask() {
DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async {
self.beginBackgroundUpdateTask()
print("Background time remaining = \(UIApplication.shared.backgroundTimeRemaining) seconds")
self.timerBack = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(AppDelegate.displayAlert), userInfo: nil, repeats: true)
RunLoop.current.add(self.timerBack, forMode: RunLoopMode.defaultRunLoopMode)
RunLoop.current.run()
self.endBackgroundUpdateTask()
}
}
var backgroundUpdateTask: UIBackgroundTaskIdentifier!
func beginBackgroundUpdateTask() {
self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
self.endBackgroundUpdateTask()
})
}
func endBackgroundUpdateTask() {
UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskInvalid
}
//Do whatever you want
func displayAlert{
}

NSTimer invalidate not work

I can not stop the timer NSTimer created to perform secondary operations.
I have read and tried all sorts of possible Web guides but none have worked and the timer continues to run even when the Controller is destroyed. Below is the code in question:
/* Copy banner in a temp array and change order to random value */
private func getBanners(){
let realm = try! Realm()
let banners = self.synchronizer.getAllBanners()?.sorted("ordine")
if let banners = banners {
for banner in banners {
let random = Double.random(0.0, 1.0)
let currentOrderValue = banner.ordine
self.banners.append(banner)
do {
try realm.write({
self.banners.last!.ordine = currentOrderValue + random
})
} catch let error as NSError {
print(error)
}
}
}
}
/*Update index of banner to show */
func updateIndex(timer : NSTimer) {
if self.index+1 < self.banners.count {
for banner in self.banners{
print(banner.name + " \(banner.ordine)")
}
self.index+=1
} else {
self.index = 0
}
self.setImageBanner()
}
/* Set image of banner to show*/
private func setImageBanner() {
if self.banners.count > 0 {
self.bannerImage.hidden = false
let banner = self.banners[self.index]
let file = banner.images[0]
self.synchronizer.loadImageURL(file.link, imageView: self.bannerImage)
} else {
self.bannerImage.hidden = true
}
}
/* Start Timer */
func startTimerForBanners() {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.getBanners()
})
self.timer = NSTimer(timeInterval: 5, target: self, selector: #selector(self.updateIndex(_:)), userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(self.timer!, forMode: NSRunLoopCommonModes)
}
/* Open link on banner click */
func openLink(sender : UITapGestureRecognizer){
if self.index >= 0 && self.index < self.banners.count {
let banner = self.banners[self.index]
print(banner.name)
self.synchronizer.openLink(banner.url)
}
}
func trialLesson(sender : UITapGestureRecognizer){
performSegueWithIdentifier("trial_lesson_segue", sender: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if let timer = self.timer {
timer.invalidate()
}
}
you need to invalidate the timer before starting a new timer. Currently I believe you are calling "startTimerForBanners" many times may be, so many timer thread has been initiated. One approach is to invlidate all timer one by one or just invalidate before starting new one like this
/* Start Timer */
func startTimerForBanners() {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.getBanners()
})
**self.timer.invalidate()**
self.timer = NSTimer(timeInterval: 5, target: self, selector: #selector(self.updateIndex(_:)), userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(self.timer!, forMode: NSRunLoopCommonModes)
}
Are you sure that your controller is really deallocated?
There is one thing you should be always aware about NSTimer: it retains it's target.
I would suggest to:
Start your timer in viewWillAppear
Invalidate and set timer to nil in viewDidDisappear
Also try replacing your start timer code with this (Swift 3):
timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.updateIndex(_:)), userInfo: nil, repeats: true)

Singleton with NSTimer not firing from AppDelegate

I'm trying to get a singleton with an NSTimer to fire from within my AppDelegate so that it fires for the duration of whilst the app is running. Whilst I can make it work in Objective-C, I can't seem to make the link as to why it's not working in Swift. Every 30 seconds I want it to poll a server and download any messages that are waiting.
The code I have in my AppDelegate is:
var myGetMessages:GetMessages!
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
..lots of other things ..
myGetMessages = GetMessages.sharedInstance
myGetMessages.downloadMessages()
}
My Singleton has:
class GetMessages {
static let sharedInstance = GetMessages()
var messageDownloadTimer: NSTimer?
.. other init code ....
func downloadMessages() {
if myMainUser != nil {
if messageDownloadTimer == nil {
self.getMessagesFromServer(false, completionHandler: nil)
messageDownloadTimer = NSTimer(timeInterval: 30, target: self, selector: "downloadNewMessages", userInfo: nil, repeats: true)
}
} else {
println("There is no MainUser set up, so can't download Messages")
}
}
It fires once from the AppDelegate and then never again.
Any clue as to what I'm missing would be great.
Change your download function;
func downloadMessages() {
if messageDownloadTimer == nil {
//?? messageDownloadTimer = NSTimer(timeInterval: 30, target: self, selector: "downloadNewMessages", userInfo: nil, repeats: true)
messageDownloadTimer = NSTimer.scheduledTimerWithTimeInterval(30, target: self, selector: "downloadNewMessages", userInfo: nil, repeats: true)
}
//--
if myMainUser != nil {
self.getMessagesFromServer(false, completionHandler: nil)
} else {
println("There is no MainUser set up, so can't download Messages")
}
}

Resources