4

Currently I am creating an alarm app that plays custom audio clips from the server. My plan on implementing this is by saving all the audio clips locally and then setting the soundName accordingly.

But I am having a few issues. Currently I am having troubles saving the audio files in the bundle directory and only able to save the files in the document directory. Would it be possible to set the soundName from the document directory instead of the bundle directory?
OR
would it be possible for me to save the audio file from the server to the bundle directory?

var localNotification = UILocalNotification()
    localNotification.fireDate = self.timePicker.date
    localNotification.alertBody = "Alert Fired"
    localNotification.soundName = "fakesound.caf" // File saved in Document Directory
    UIApplication.sharedApplication().scheduleLocalNotification(localNotification)

Thanks, please let me know if you have any confusion with my question. Or if you can think of another solution on how I could solve this problem.

AustinT
  • 1,998
  • 8
  • 40
  • 63

3 Answers3

3

The answer, unfortunately, is no and no.

You can only play sounds from the bundle in a local notification, and the bundle is read-only.

The only sounds you can play from a local notification must be shipped with your app. No other option.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Good confirmation. I've been trying to figure this out myself. Does anyone know the justification from Apple for preventing downloaded sounds from playing in UILocalNotification? Is this a security/sandbox issue to prevent non-permitted audio from suddenly playing unexpectedly? That's the only thing I can think of but it's pretty specious. – Justin Whitney Mar 09 '16 at 14:46
  • 1
    This is possible. After some research (trial & error), I've found that if the sound file is stored in the 'Library/Sounds' directory, it will be played successfully as a local notification. I've written the full answer on a different SO question, here: http://stackoverflow.com/a/39454122/1280479 – Andy Shephard Sep 12 '16 at 15:44
  • @AndyShephard, that's good news. It contradicts what the docs say however. You're talking about the sandboxed library/sounds directory? – Duncan C Sep 12 '16 at 23:10
  • To quote the docs on the soundName property of `UILocalNotification`: "...For this property, specify the filename (including extension) of a sound resource in the app’s main bundle or UILocalNotificationDefaultSoundName to request the default system sound." – Duncan C Sep 12 '16 at 23:12
  • @DuncanC the `UILocalNotification` documentation states this: "To play a sound, assign a sound to the soundName property. You can assign the filename of a nonlocalized custom sound in the app’s main bundle (or data container) or you can assign UILocalNotificationDefaultSoundName to get the default system sound." The data container consists of the Documents, Library & Temp directories. As the `UIRemoteNotification` states that sounds should be stored in `Library/Sounds`, I tried it for a local notification and it worked. – Andy Shephard Sep 13 '16 at 07:11
  • I'll be testing it out more today, but it worked by storing the file ONLY in this `Library/Sounds` directory. The filetype I used happened to be a .CAF file, though the Apple documentation states it can also be an .AIFF or .WAV file. Link here: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/IPhoneOSClientImp.html#//apple_ref/doc/uid/TP40008194-CH103-SW6 – Andy Shephard Sep 13 '16 at 07:18
  • Well, I tried it by copying file from bundle to Library/Sounds directory and still can't get this to work . Here is the path of file : file:///private/var/mobile/Containers/Data/Application/37FA5D9B-F603-44CE-B386-DAB9D614ED68/Library/Sounds/notification.caf – IsPha Mar 20 '17 at 19:45
  • 1
    @IsPha It is possible, I have done it, remove the "file://" from your path; well in my case the path was file:///var/mobile/Containers/Data/Application/37FA5D9B-F603-44CE-B386-DAB9D614ED68/Library/Sounds/notification.caf and I removed the "file://" from the path and it worked. – Dheeraj Gupta May 18 '18 at 14:09
  • Yes @DheerajGupta you’re totally right and this is so no longer issue , i had to update that it worked that time with me :D , thanks . – IsPha May 18 '18 at 14:53
1

Fortunately, it is YES. Vist this link : How do I add a file to the main bundle's /Library/Sounds directory?

Here I have copied the system ringtone to Library/Sounds, likewise you have to copy from your data directory by putting the path as source path and destination as given below, by creating a directory with Sounds name.

// get the list of system sounds, there are other sounds in folders beside /New 
let soundPath = "/System/Library/Audio/UISounds/New" 
var arrayOFSoundNames = [String] () 

// MARK: - scene setup 
func doAnyAdditionalSetup() 
{ 
   arrayOFSoundNames = getSoundList() 
} 
// MARK: - File manager methods 
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)") 
    } 
} 

// MARK: - tableview methods 
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
   return arrayOFSoundNames.count 
} 
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
    copyFileToDirectory(fromPath:soundPath, fileName:arrayOFSoundNames[indexPath.row]) 

}

You will get your answer, also check the apple document

Dheeraj Gupta
  • 372
  • 5
  • 20
0

There is one more way, that you can directly save your custom audio file in Library/Sounds rather than saving in the document directory. And then just put the name of the file with extension in the notification payload, it will play your custom audio on local/push notification; provided its not more than 30 seconds.

Refer code if needed:

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

Here, you get the path of Library/Sounds in directoryPath variable, using this you can save or do other operations.

Dheeraj Gupta
  • 372
  • 5
  • 20