MBN
MBN

Reputation: 304

Playing sound in silent mode, app killed state

I need to play a custom sound on receiving a push notification in my ios app.

I know, apple don't support the sound alert in silent mode. I have tried sending push notification with custom sound. Its playing when the device is not silent, and vibrates in silent mode.

But, recently I found an app - Chipolo where a custom sound is displayed in silent mode, even when the app is in killed state.

What technology used in playing a sound alert?

Any help is really appreciated!

Upvotes: 2

Views: 1996

Answers (2)

Daniel E. Salinas
Daniel E. Salinas

Reputation: 441

As for playing a sound when the device is on silent mode you simply can't do it right away. Only Apple can override the silent switch for an emergency situation like finding the device. You would need a special permission from Apple for your app to do it too.

Ganesh's answer is the best thing you can do without it.

Upvotes: 0

Ganesh Manickam
Ganesh Manickam

Reputation: 2139

We can't change the system sound when receiving push notification but we can play audio by using MediaPlayer and AVFoundation. We need to use Notification Service extension to process foreground and background notification

In NotificationService Extension

import UserNotifications
import AVFoundation
import MediaPlayer

class NotificationService: UNNotificationServiceExtension {


    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        // Properties
        var error:NSError?
        var audioPlayer = AVAudioPlayer()

        let tempInfo = request.content.userInfo as! [String:NSObject]
        var songName = ""
        if tempInfo.index(forKey: "SoundFileName") != nil {
            songName = (tempInfo["SoundFileName"] as! String)
        }

        var opecode = ""
        if tempInfo.index(forKey: "Opcode") != nil {
            opecode = (tempInfo["Opcode"] as! String)
        }

        if opecode == "RingDevice" {
            var songTitle = ""
            var songExtension = ""
            if songName == "" {
                songTitle = "Input"
                songExtension = "caf"
            } else  {
                let testStr = songName.components(separatedBy: ".")
                songTitle = "\(testStr[0])"
                songExtension = "\(testStr[1])"
            }

                if let url = Bundle.main.url(forResource: "\(songTitle)", withExtension: "\(songExtension)")  {

                    let volumeView = MPVolumeView()
                    if let view = volumeView.subviews.first as? UISlider{
                        view.value = 1.0 //---0 t0 1.0---

                    }

                    do {
                        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)

                        try AVAudioSession.sharedInstance().setActive(true)

                        audioPlayer = try AVAudioPlayer(contentsOf: url,  fileTypeHint: AVFileType.caf.rawValue)
                        audioPlayer.volume = 1.0
                        DispatchQueue.main.asyncAfter(deadline: .now() + 5) { // Give 5 sec delay to play audio or else audio will not hearable
                             audioPlayer.play()
                        }
                    } catch _ as NSError {
                        print("[SoundPlayer] There was an error: \(String(describing: error))")
                    }

                    if (audioPlayer.isPlaying) {
                        audioPlayer.stop()
                    }
                    audioPlayer.play()
                }
        }


        if let bestAttemptContent = bestAttemptContent {
            // Modify the notification content here...            
            bestAttemptContent.title = "\(bestAttemptContent.title)"

            contentHandler(bestAttemptContent)
        }
    }

    override func serviceExtensionTimeWillExpire() {
        // Called just before the extension will be terminated by the system.
        // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }

}

In my case i need to check two parameters in pushnotification SoundFileName and Opcode. if Opcode is RingDevice means i'm playing sound which is mentioned in PushNotification

Don't forget to enable background mode

enter image description here

Hope this will help you :)

Upvotes: 2

Related Questions