2
votes

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!

2
Have you find solution?Ganesh Manickam
Not yet. I contacted the apple for technical support, regarding this and they replied like by design, it is supposed to like that. There is no way to play a sound in silent mode and app killed state. Words from apple - " The behavior and resulting limitations you describe are by design. If you believe an alternative approach should be considered by Apple, we encourage you to file an enhancement request with information on how this design decision impacts you, and what you’d like to see done differently. "MBN
But i wonder, how the apps like Chipolo are doing that. No idea regarding that.MBN

2 Answers

2
votes

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

0
votes

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.