Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Local notification sound not playing while app in background - Swift

I'm making an alarm clock app, and I have confirmed that my alarm notifications are being triggered correctly, but the sound does not always play as I expect.

For instance, when the phone is not in silent mode, the notification plays the sound sucessfully (ok, great!). However, when the phone is in silent mode, the sound does not play, even though the app is still running in the background (not so great...).

I understand that silent mode is supposed to silence all notification sounds, BUT I've downloaded other alarm clock apps from the App Store (like Alarmy), and they are somehow able to get their notification sounds to play even if the phone is on silent mode, as long as the app is still running in the background. Only when the app is fully exited will the silent mode take effect.

Does anyone know how to achieve this result? Is there some setting or option that I need to declare either in my code or plist file? I've scoured the internet but haven't found anything for this particular issue...

My code to set the AVAudioSession category:

private func setAudioCategory() {
    do {
        // Enable sound (even while in silent mode) as long as app is in foreground.
        try AVAudioSession.sharedInstance().setCategory(.playback)
    }
    catch {
        print(error.localizedDescription)
    }
}

My code to set a notification:

/// Sets a local user notification for the provided `Alarm` object.
static func set(_ alarm: Alarm) {

    // Configure the notification's content.
    let content = UNMutableNotificationContent()
    content.title = NSString.localizedUserNotificationString(forKey: K.Keys.notificationTitle, arguments: nil)
    content.body = NSString.localizedUserNotificationString(forKey: K.Keys.notificationBody, arguments: nil)

    // Get sound name
    let soundName: String = UserDefaultsManager.getAlarmSound().fileNameFull
        
    // Set sound
    content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: soundName))
    content.categoryIdentifier = "Alarm"

    // Configure the time the notification should occur.
    var date = DateComponents()
    date.hour = alarm.hour
    date.minute = alarm.minute

    // Create the trigger & request
    let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: false)
    let request = UNNotificationRequest(identifier: alarm.notifID, content: content, trigger: trigger)

    // Schedule the request with the system.
    let notificationCenter = UNUserNotificationCenter.current()
    notificationCenter.add(request, withCompletionHandler: { error in
        if error != nil {
            // TODO: Show Alert for Error
            return
        }
    })
}
like image 373
Eric Avatar asked Aug 06 '21 16:08

Eric


People also ask

Why is my app notification sound not working?

Enable Allow Notifications. Try changing it to another and see whether it fixes the problem. To change notification tone on Android: Go to Settings > Notifications. Tap on Notification sound and select the tone you prefer.

Can each app have its own notification sound?

Some Android apps offer a built-in setting to change the default notification tones. So you can customize the notification tone inside the app as well. You can even customize notification tones for individual contacts if your app allows that.

Can I send notifications when the app is in the background?

You should now be able to send local notifications when the app is in the foreground and in the background.

How do notifications work in Android?

When a notification appears, the observer code executes special code for that event. Another type of notification notifies a user that something has happened, even if they are not in the app the sent the notification – or the device is sleeping. That kind of user notification has two varieties: push notifications and local notifications.

How do I create a swift local notification demo?

Set Up a New Project. Start a new single view project called SwiftLocalNotificationDemo with Swift as the language and a Universal device. Save the file, and then go to the story board.

Why are my notifications not showing up when I run the app?

Before you run the app there is something you need to know about the notifications. They will not show up if you app is in the foreground. In order to see this notification, you will need to run the app and then background the app immediately. This will allow the notification to show up.


1 Answers

So I've discovered something.

As the question is stated, it is not currently possible to play sound in a local notification when the phone is on silent mode.

However, great news!

There is actually a different way to achieve the same result; and it's how apps like Alarmy do it.

Note: I (FINALLY) discovered this solution from this wonderful SO answer, but I'll summarize it here for reference.

In short, the local notification will not be playing the sound, but instead, the app will play it (while in the background).

STEPS

  1. You must enable the app to play sound in the background. To do this, navigate to your .plist file and add the String value App plays audio or streams audio/video using AirPlay to the array key Required background modes. (This can also be achieved in your app's Capabilities - it does the same thing).

  2. In your App Delegate, set your AVAudioSession's category to .playBack so sound will still play even when the phone is locked or in the background.

do {
    try AVAudioSession.sharedInstance().setCategory(.playAndRecord)
    try AVAudioSession.sharedInstance().setActive(true)
} catch {
    print(error.localizedDescription)
}
  1. Start your AVAudioPlayer at the time you would like it to play (in my case, at the time of my local notification).
let timeInterval = 60.0 // 60.0 would represent 1 minute from now
let timeOffset = audioPlayer.deviceCurrentTime + timeInverval
audioPlayer.play(atTime: timeOffset) // This is the magic function

// Note: the `timeInterval` must be added to the audio player's 
// `.deviceCurrentTime` to calculate a correct `timeOffset` value.

In conclusion, as the SO answer I linked to above so aptly summarizes:

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).

like image 146
Eric Avatar answered Oct 19 '22 05:10

Eric