I'm working on an app with a piano keyboard. I want that the sound after key-pressing is only played, when "Ring/Silent" switch is on ring. Then it should stop background music from other apps . If the "Ring/Silent" switch is on silent, it shouldn't do anything and let the background music from other apps play (like Spotify).
What I tried (with Spotify as background music):
Option 1 [AVAudioSessionCategoryAmbient]:
"Ring/Silent" switch is on ring: background music doesn't stop and it plays in-app sound together with background music (-> What I don't want)
"Ring/Silent" switch is on silent: background music doesn't stop and no in-app sounds are played (-> What I want)
Option 2 [AVAudioSessionCategorySoloAmbient]:
"Ring/Silent" switch is on ring: background music stops and it plays in-app sound (-> What I want)
"Ring/Silent" switch is on silent: background music stops, but no in-app sounds are played (-> What I want, it shouldn't stop background music)
I think that Option 2 is the closest option I can get. The iPhone shouldn't stop background music when "Ring/Silent" switch is on silent and I'd be happy.
Option 3 [AVAudioSessionCategoryPlayback]:
"Ring/Silent" switch is on ring: background music stops and it plays in-app sound (-> What I want)
"Ring/Silent" switch is on silent: background music stops and it plays in-app sound (-> What I don't want)
My Code:
let NoteSound = NSURL(fileURLWithPath: Bundle.main.path(forResource: currentNote.sound, ofType: "m4a")!)
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
try AVAudioSession.sharedInstance().setActive(true)
audioPlayer = try AVAudioPlayer(contentsOf: NoteSound as URL)
audioPlayer.prepareToPlay()
audioPlayer.play()
} catch {
print("Problem in getting File")
}
In the code example I only changed .setCategory() .
There is no native API to check if the phone is on Silent or not, and you are discouraged from basing your app's functionality on such user choices (On low battery mode, Charging or not, ...).
There is a hack to check whether the device is muted or not: Play a short sound of 0.5s minimum duration, and detect the time it took to play. For more details on this workaround, have a look here.
Related
I am working on app that can alert users for some critical things. I use local notifications to alert the user. On iOS, I find that the notifications will not ring if the phone is on vibrate. This is a deal-breaker for many users of the app but I have been putting that question off till now since I thought that iOS doesn't allow an app to play a sound if the app is in the background.
Music apps are able to play songs even when the phone is on vibrate by enabling the audio background mode but it doesn't allow you to schedule a song to be played at a certain time.
Lately I have seen that some apps are able to play a sound at a certain time even though the app is in the background. One such app is Alarmy alarm app. I don't think that they are playing the music via the local notification when the alarm expires because the music continues to play even after I clear the notification. From the local notification documentation, I understood that I am can't run any code when local notification fires till the user clicks on the notification. So, I can't start an audio player which may be able to play the sound in vibrate.
How are such apps able to play a sound even though the phone is on vibate and the app is in background in iOS?
There are few methods to implement this kind of functionality.For reference I recommend this link.
For actually playing the sound while the device’s ringer switch is set to vibrate
First off make sure to include the audio background mode in the capabilities, in order to play audio in the background.
Then,
Swift 4
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, with: [.duckOthers, .defaultToSpeaker])
try AVAudioSession.sharedInstance().setActive(true)
UIApplication.shared.beginReceivingRemoteControlEvents()
} catch {
NSLog("Audio Session error: \(error)")
}
Here we set the shared audio session’s category to AVAudioSessionCategoryPlayAndRecord, so that we can play sound, while the device’s ringer switch is set to vibrate.
The .duckOthers is specified to make other audio quieter, if there’s any mixable audio playing, so that our alarm can be heard. You can leave that out or use another option, if you prefer a different behavior.
The .defaultToSpeaker is specified, so that the volume would go to the speaker, where it’ll be much louder and should wake up our user with ease.
beginReceivingRemoteControlEvents makes it so that the app handles the remote control options, like the play/pause buttons on the lock screen, in order to make it easier for our user to mute their alarm, once they wake up.
The way this can be done (I have implemented this in my app) is by starting an AVAudioPlayer and specifying a specific time to play. So:
Enable background audio in the app capabilities.
Start and audio session with .playback mode, and start a player at the time you like it to play:
do {
//set up audio session
try AVAudioSession.sharedInstance().setCategory(.playback, options: [.defaultToSpeaker, .duckOthers])
try AVAudioSession.sharedInstance().setActive(true)
//Start AVAudioPlayer
player.play(at: time) //time is a TimeInterval after which the audio will start
}
catch {
...
}
This does not play silence in the background, which violates Apple's rules. It actually starts the player, but the audio will only start at the right time. I think this is probably how Alarmy implemented their alarm, given that it's not a remote notification that triggers the audio nor is the audio played by a local notification (as its not limited to 30 seconds or silenced by the ringer switch).
I have game made with SpriteKit. When I open the app and I change the scene, while music is playing (for example from Music app on iPhone), the music stops. How can I continue to play music when I open the app and when I change scenes?
You need to configure your app's capabilities, and set the category on the Audio Session.
I think you're looking for:
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
There are other categories as well, but here are the docs on AVAudioSessionCategoryPlayback.
When using this category, your app audio continues with the Silent
switch set to silent or when the screen locks. (The switch is called
the Ring/Silent switch on iPhone.) To continue playing audio when your
app transitions to the background (for example, when the screen
locks), add the audio value to the UIBackgroundModes key in your
information property list file.
Here's a capture of the XCode Capabilities Panel:
Many alarm apps managed to play sound with locked screen after several hours (examples: Rise, Wave alarm clock)
All you need to do is set the alarm and lock your screen while the app is open and an alarm will go off hours after the screen has been locked.
I managed to figure out how to play audio with AVPlayer while my device is locked:
Set background capabilities to audio
Set 'Application does not run in background' to YES
Code:
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
print("AVAudioSession Category Playback OK")
do {
try AVAudioSession.sharedInstance().setActive(true)
print("AVAudioSession is Active")
} catch let error as NSError {
print(error.localizedDescription)
}
} catch let error as NSError {
print(error.localizedDescription)
}
When I try to set a NSTimer or use dispatch_after to run the code that plays my audio file and lock my iPhone the Timer stops pretty quickly because apparently the app gets inactive after just a few seconds
(But only when I start the App from the home screen and doesn't when I run the App from xcode, btw)
How do those alarm clock apps manage to delay audio?
These apps achieve this by opting out of the multi-tasking mode. If an app opts out of multi-tasking mode, the app is not suspended if the app was in the foreground when the device got locked and hence they are able to play the alarm sound when the time arrives. You can read more here
(Posted answer on behalf of the question author to move it to the answer space).
I figured out how they do it. I set an alarm and listened to the speakers. Those apps play silent sound to stay active. I don't know how they passed the checks by Apple but apparently many alarm clock apps do this and pass.
I want to play sound in background for a feature but I want to play sound only in foreground for another feature.
Also, I want to play sound even when the silent switch is turned on.
To play sound in background, the following 2 code is needed:
[Info.plist] UIBackgroundModes = audio
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, withOptions: [])
To play sound only in foreground, I think I have to set UIBackgroundModes to empty in the Info.plist.
But it is not possible while the app is running.
How can I play sound only in foreground for a feature and sound in background for another feature?
* playing sound in foreground means that sound is paused when the user push the home button of the iPhone and it is resumed when the user open the app again.
How can I play sound only in foreground for a feature and sound in background for another feature
Detect when the app goes into the background, and stop playing the foreground sound and switch to the background sound.
In the game I'm building there are sound effects and music. Players are able to turn off the in-game music and sound effects. I assume that when a player turns off the in-game music it's probably because they want to listen to their own music. However, when I run a Sound.play() in order to play a sound effect the background music being played from the music app is stopped. Is there a way around this?
Thanks in advance.