AudioKit Background Audio Battery Usage - ios

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()
}

Related

Vibrate indefinitely in Swift

How can I make an iPhone vibrate indefinitely? I know I can do this:
while true {
AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
}
Is there any way to play the vibration for a certain number of seconds, or something with a similar result that is cleaner? Also, is there a way to make the vibration more intense?
I think you should use Timer.
var timer: Timer?
var numberRepeat = 0
var maxNumberRepeat = 20
func startTimer() {
//
timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(self.timerHandler), userInfo: nil, repeats: true)
}
func stopTimer() {
timer?.invalidate()
timer = nil
}
#objc func timerHandler() {
if numberRepeat <= maxNumberRepeat {
numberRepeat += 1
vibrateDevice()
} else {
stopTimer()
}
}
func vibrateDevice() {
AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
}

Loop vibration in Swift

I'm playing a sound using AVPlayer. I'm trying to let the iPhone vibrate during this sound.
I'm able to create a vibration with:
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)
But I'm having problems trying to loop this vibration if I use:
vibrationTimer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(ViewController.loopVibration), userInfo: nil, repeats: true)
#objc func loopVibration() {
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
}
The func loopVibration() gets called every two seconds but it doesn't vibrate.
Not sure why this isn't working for you--the following sample works for me on my test device (iPhone X, iOS 11.2).
The ViewController sample below includes outlets for playing single sounds, playing single vibrations, looping sounds and looping vibrations. The "Chirp" sound is a 1s wav file.
Note that since this is a sample, the code below doesn't dispose of the system sounds, nor invalidate timers if the ViewController is hidden. Make sure to manage your system resources appropriately if you adapt this.
//
// ViewController.swift
//
import UIKit
import AudioToolbox
class ViewController: UIViewController {
static let soundId: SystemSoundID? = {
guard let soundURL = Bundle.main.url(forResource: "Chirp", withExtension: "wav") else {
return nil
}
var soundId: SystemSoundID = 0
AudioServicesCreateSystemSoundID(soundURL as CFURL, &soundId)
return soundId
}()
static let timerInterval: TimeInterval = 2
var soundTimer: Timer?
var vibrationTimer: Timer?
var playCount = 0 {
didSet {
playCountLabel.text = String(playCount)
}
}
func invalidateTimers() {
if let vibrationTimer = vibrationTimer {
vibrationTimer.invalidate()
}
if let soundTimer = soundTimer {
soundTimer.invalidate()
}
}
#IBOutlet weak var playCountLabel: UILabel!
#IBAction func playSound(_ sender: Any) {
if let soundId = ViewController.soundId {
AudioServicesPlaySystemSound(soundId)
playCount += 1
}
}
#IBAction func loopSound(_ sender: Any) {
invalidateTimers()
soundTimer = Timer.scheduledTimer(timeInterval: ViewController.timerInterval, target: self, selector: #selector(playSound(_:)), userInfo: nil, repeats: true)
}
#IBAction func vibrate(_ sender: Any) {
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
playCount += 1
}
#IBAction func loopVibrate(_ sender: Any) {
invalidateTimers()
soundTimer = Timer.scheduledTimer(timeInterval: ViewController.timerInterval, target: self, selector: #selector(vibrate(_:)), userInfo: nil, repeats: true)
}
#IBAction func cancelAll(_ sender: Any) {
invalidateTimers()
}
}
It's not permitted to use continuous vibration, your app might be rejected. If you still want to do it you can try something like this:
func vibrate() {
for i in 0...13 {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(Double(i) * 0.1 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: {
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)
self.vibrate()
})
}
}

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

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
}
}

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)

AppDelegate Issues When Running App on Device

I have an app that has a simple timer that runs on an animated viewcontroller using SpriteKit. I want the timer to continue so I used the Notification Center and posted notifications in the AppDelegate as follows:
func applicationWillResignActive(application: UIApplication) {
NSNotificationCenter.defaultCenter().postNotificationName("stopTimerNotification", object: nil)
}
func applicationDidEnterBackground(application: UIApplication) {
NSNotificationCenter.defaultCenter().postNotificationName("stopTimerNotification", object: nil)
}
func applicationWillEnterForeground(application: UIApplication) {
NSNotificationCenter.defaultCenter().postNotificationName("startTimerNotification", object: nil)
}
func applicationDidBecomeActive(application: UIApplication) {
NSNotificationCenter.defaultCenter().postNotificationName("startTimerNotification", object: nil)
}
My timer runs fine on the device when connected to Xcode and also runs fine on device when the "Sleep Time" app is running on device (and not connected to Xcode). I've searched but cannot fathom this one out. Any suggestions?
The code that executes when both NSNotifications are called is:
func stopTimerInBackground(note: NSNotification) {
if(isRunningStopwatch == true) {
stopPressed()
stopTimerInBackgroundDate = NSDate()
}
}
func startTimerInBackground(note: NSNotification) {
var restartTimerInBackgroundDate:NSDate! = NSDate()
if (isStopwatch == true) {
previousElapsedTime = restartTimerInBackgroundDate.timeIntervalSinceDate(stopTimerInBackgroundDate)
previousElapsedTime = 0
startPressed()
}
}
I just wonder if it's a plist value I have to change?

Resources