2

I'm hoping I'm wrong but I don't think it's possible. In the Local and Push Notification Programming Guide, under "Preparing Custom Alert Sounds" it says that "The sound files must be in the main bundle of the client application".

If you can't write to the main bundle, then how can you get a user generated recording (using, say, AVAudioRecorder) to play as the alert sound?

On the one hand it seems impossible, but on the other I think there are apps out there that do it (and I'll look for those).

ari gold
  • 2,074
  • 3
  • 25
  • 51
  • Well, I found an app that does it: VoCal XL. Any ideas what's going on there? – ari gold Jul 31 '12 at 03:19
  • Maybe the docs are wrong and the sound files don't have to be in the app bundle but in the app sandbox. –  Sep 05 '12 at 20:45
  • Hmm. I have tested it and it doesn't work. However, if I use sounds that I loaded in with Xcode (i.e. in the main bundle) then it does work, leading me to believe that the docs are right. Any ideas on how I can test things? – ari gold Sep 05 '12 at 22:27

1 Answers1

8

I solved this by copying a system sound file to ~/Library/Sounds directory and name it as notification.caf. The server alert payload specify this as the name of the sound to be played. Whenever the user selects another sound, that sound will be copied to the same folder and overwrites the old sound.

Payload:

{
"aps": {
    "sound": "notification.caf"
}

}

// get the list of system sounds, there are other sounds in folders beside /New
let soundPath = "/System/Library/Audio/UISounds/New"
func getSoundList() -> [String] {
    var result:[String] = []
    let fileManager = NSFileManager.defaultManager()
    let enumerator:NSDirectoryEnumerator = fileManager.enumeratorAtPath(soundPath)!
    for url in enumerator.allObjects {
        let string = url as! String
        let newString = string.stringByReplacingOccurrencesOfString(".caf", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
        result.append(newString)
    }
    return result
}

// copy sound file to /Library/Sounds directory, it will be auto detect and played when a push notification arrive
class func copyFileToDirectory(fromPath:String, fileName:String) {
    let fileManager = NSFileManager.defaultManager()

    do {
        let libraryDir = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.LibraryDirectory, NSSearchPathDomainMask.UserDomainMask, true)
        let directoryPath = "\(libraryDir.first!)/Sounds"
        try fileManager.createDirectoryAtPath(directoryPath, withIntermediateDirectories: true, attributes: nil)

        let systemSoundPath = "\(fromPath)/\(fileName)"
        let notificationSoundPath = "\(directoryPath)/notification.caf"

        let fileExist = fileManager.fileExistsAtPath(notificationSoundPath)
        if (fileExist) {
            try fileManager.removeItemAtPath(notificationSoundPath)
        }
        try fileManager.copyItemAtPath(systemSoundPath, toPath: notificationSoundPath)
    }
    catch let error as NSError {
        print("Error: \(error)")
    }
}

The push notification sound can be buggy though, I have to reboot my phone before the sound will play reliably for every notification.

Cymric
  • 1,063
  • 1
  • 11
  • 18
  • how did you solve this problem? I did the same process like you, but whenever i sent a push notification, if app is active or in background, the whole sound system of the iphone crashes, if app is not running then notification sound plays without a problem, i didnt understand the reason but if ios wants to play a sound file in the library folder when app is alive, then you cant even set the volume of the iphone until you play a song in another app like spotify etc, then it backs to normal – woheras Apr 10 '20 at 13:35