Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Start playing sound in background on iOS?

I'm trying to do something similar to Tile.app. When it shows a notification, it plays a sound. That seems simple enough-- use UILocalNotification and include the sound file name. But local notifications limit sounds to no more than 30 seconds, and Tile's sound keeps playing for a whole lot longer than that. I expect it's looping, and it continues until I can't stand the noise any more.

The sound also plays even if the phone's mute switch is on, which doesn't happen with local notifications. For these reasons, UILocalNotification appears to be out.

I thought maybe I could post a text-only local notification and have my app play the sound. But using AVAudioPlayer, the play method returns NO and the sound doesn't play.

Other questions have suggested that this is by design, that apps aren't supposed to be able to play sounds from the background-- only continue sound that's already playing. I'd accept that reasoning except that Tile does it, so it's obviously possible. One suggested workaround violates Apple's guidelines in a way that I'm pretty sure Apple checks for now.

Some details that may be relevant:

  • I have the audio background mode in Info.plist
  • I use AVAudioSessionCategoryPlayback on the audio session, and the session should be active (at least, setActive:error: claims to succeed).
  • This app uses Bluetooth but does not currently use the bluetooth-central background mode.
like image 897
Tom Harrington Avatar asked May 20 '16 16:05

Tom Harrington


People also ask

How do you play music in the background on Apple?

To play it while the phone is locked, just lock your phone and you should see a music widget control on the lock screen. Tap play on that and music continues in the background.


1 Answers

Actually you can use one of AudioServices... functions

  • AudioServicesPlaySystemSound
  • AudioServicesPlayAlertSound
  • AudioServicesPlaySystemSoundWithCompletion

to start playing sound in background.

I'm not sure about length of sound that can be submitted to AudioServicesCreateSystemSoundID because I'm checked only with short looped sound for ringing but this API not tied to notification so possible can overpass 30 second limit.

EDIT: App still requires audio in UIBackgroundModes to play in background

Cut from my test project appDelegate, iOS 9.3.1

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // test sound in foreground.
    registerAndPlaySound()

    return true
}

func applicationDidEnterBackground(application: UIApplication) {
    var task =  UIBackgroundTaskInvalid
    task = application.beginBackgroundTaskWithName("time for music") {
        application.endBackgroundTask(task)
    }
    // dispatch not required, it's just to simulate "deeper" background  
    let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(5 * Double(NSEC_PER_SEC)))

    dispatch_after(delayTime, dispatch_get_main_queue()) {
        self.registerAndPlaySound()
    }
}

func registerAndPlaySound() {
    var soundId: SystemSoundID = 0

    let bundle = NSBundle.mainBundle()
    guard let soundUrl = bundle.URLForResource("InboundCallRing", withExtension: "wav") else {
        return
    }
    AudioServicesCreateSystemSoundID(soundUrl, &soundId)

    AudioServicesPlayAlertSound(soundId)
}

Update

There I used beginBackgroundTask to start sound only as simplest way to execute code in background in another project similar code to registerAndPlaySound was called from PushKit callback without background task.

like image 55
sage444 Avatar answered Nov 10 '22 15:11

sage444