I'm trying to have a custom sound to be played when my application receives a notification, as part of the Apple Docs this should be done in the following way:
You can package the audio data in an aiff, wav, or caf file. Then, in Xcode, add the sound file to your project as a nonlocalized resource of the app bundle
My question is how do you create a nonlocalized resource? Is it automatically created when you drag/drop the audio file into the Xcode project? Is it specified in the Info.plist?
I'm able to play the sound file using the following code:
if let buttonBeep = self.setupAudioPlayerWithFile("0897", type:"aiff") {
self.buttonBeep = buttonBeep
}
buttonBeep?.volume = 1.0
buttonBeep?.play()
...
func setupAudioPlayerWithFile(file:NSString, type:NSString) -> AVAudioPlayer? {
let path = NSBundle.mainBundle().pathForResource(file as String, ofType: type as String)
let url = NSURL.fileURLWithPath(path!)
var audioPlayer:AVAudioPlayer?
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: url)
} catch {
print("Player not available")
}
return audioPlayer
}
But no sound plays when a push notification is received.
When you drag the sound file into Xcode, make sure that you check the box to "Add To Targets".
Related
I saw UNNotificationAttachment does provide support on both image and audio.
Based on https://developer.apple.com/documentation/usernotifications/unnotificationattachment
Create a UNNotificationAttachment object when you want to include
audio, image, or video content together in an alert-based
notification.
Hence, I try to create 2 local notification (not push notification), which triggered by time. 1 notification has an image, another notification has an audio file (m4a)
However, I notice the image attachment does work well. It is showing as thumbnail on right.
But, there is nothing shown for the audio file attachment. I expect there will be an audio play button on right.
Here's how they look like
Am I doing something incorrect? Or, my expectation is wrong for the local notification's UNNotificationAttachment?
Here's my code which is using UNNotificationAttachment
if let attachment = litePlainNote.attachmentables.first {
source = attachment.url
identifier = "\(timestamp)-\(attachment.name)"
} else if let recording = litePlainNote.recordingables.first {
source = recording.url
identifier = "\(timestamp)-\(recording.name)"
}
if let source = source, let identifier = identifier {
let destination = UserDataDirectory.notification.url.appendingPathComponent(identifier)
do {
try FileManager.default.copyItem(at: source, to: destination)
let notificationAttachment = try UNNotificationAttachment(identifier: identifier, url: destination)
content.attachments.append(notificationAttachment)
} catch {
error_log(error)
}
}
May I know are you able to make audio file (m4a) workable with UNNotificationAttachment (Local notification)?
I have an app being used by people to receive orders with it needing to make a continuous sound until staff attend to it. It was working for two months then just started crashing a lot. For whatever reason, it runs fine on an iPad but not on iPhones running a recent operating system.
When this bit of code gets called it crashes:
guard let path = Bundle.main.path(forResource: "alert.mp3", ofType: nil) else { return }
let url = URL(fileURLWithPath: path)
do {
self.alertSoundEffect = try AVAudioPlayer(contentsOf: url)
} catch let err {
print("err: \(err)")
}
DispatchQueue.main.async {
self.alertSoundEffect.numberOfLoops = -1
self.alertSoundEffect.prepareToPlay()
self.alertSoundEffect.play()
}
The fix online to declare the alertSoundEffect variable like this:
private var alertSoundEffect : AVAudioPlayer!
has not worked at all.
I tried moving everything but the line:
self.alertSoundEffect.play()
to viewDidLoad as I thought maybe that code couldn't get called more than once, but it didn't help.
Specifically, the compiler highlights this line when it crashes:
self.alertSoundEffect = try AVAudioPlayer(contentsOf: url)
I tried using try AVAudioPlayer where it takes a Data object as a parameter or with including the type of audio file to be played, but that did not change anything.
When I try the AVAudioPlayer's delegate and declare it like this:
self.alertSoundEffect.delegate = self
right before the first lines of code I shared above Xcode highlights this line instead when it reliably crashes.
What else should I try?
I suppose your path is wrong.
Try this:
guard let path = Bundle.main.path(forResource: "alert", ofType: "mp3") else { return }
Also, if your audio file is short, like less than 30s, then try not to call self.alertSoundEffect.prepareToPlay(). Just call self.alertSoundEffect.play() right away.
Since iOS 13, this was causing a bug in my app, since I have notification sounds which are 3-10 seconds long.
If you initialise your AVAudioPlayer like var wrongMusicPlayer: AVAudioPlayer = AVAudioPlayer() OR wrongMusicPlayer = AVAudioPlayer() in any method then please remove it and just Declare like var wrongMusicPlayer: AVAudioPlayer!.
iOS 13.1 Crash in AVAudio Player
i'm thinking about an iOS alarm app. At the time of the alarm i want to play custom music from Apple Music or another source.
Unfortunately app background operations are really restrictive and notifications only allow bundled music files to be played. Is there any way to achieve my goal by using background tasks or something else?
There're no other choices except using notifications, seealso: Background Execution
Change the sound of notification.
1. Import audio files.
Visit the iPod music library
let mediaquery = MPMediaQuery()
// MPMusicPlayerControllerNowPlayingItemDidChangeNotification
if let musics = mediaquery.items {
for music in musics {
let title = music.valueForProperty(MPMediaItemPropertyTitle) as? String
if let url = music.assetURL {
saveNotificationSound(url,name: title,isLast: music == musics.last)
}
}
}
The important param is assetURL, you can get audio file by it. NOTICE: If the music is download from Apple Music or in the iCloud, it's assertURL is nil.
Use file sharing
How to enable file sharing for my App
2. Cut the audio to 30s and specified format for notification
Because the notification is limited: 1. The duration is not more than 30s; 2. The format is limited, we cut it to m4a.
/**
Cut the duration and convert to m4a, than save it.
- parameter audioPath: Source file path
- parameter startTime: Cut start time
- parameter endTime: Cut end time
- parameter saveDirect: ...
- parameter handler: ...
*/
func cutoffAudio(audioPath: NSURL, startTime: Int64, endTime: Int64, saveDirect:NSURL, handler: (succeed: Bool) -> Void){
let audioAsset = AVURLAsset(URL: audioPath, options: nil)
if let exportSession = AVAssetExportSession(asset: audioAsset, presetName: AVAssetExportPresetAppleM4A){
let startTime = CMTimeMake(startTime, 1)
let stopTime = CMTimeMake(endTime, 1)
exportSession.outputURL = saveDirect
// Output is m4a
exportSession.outputFileType = AVFileTypeAppleM4A
exportSession.timeRange = CMTimeRangeFromTimeToTime(startTime, stopTime)
exportSession.exportAsynchronouslyWithCompletionHandler({
handler(succeed: exportSession.status == .Completed)
})
}
}
3. Set as the sound of notification
Attention: The custom audio files can only be placed in /Library/Sounds in App's Sandbox, than the soundName only needs to provide the file name (including the extension), the audio files are just like in the main bundle.
Here is a demo from github.com/ToFind1991
The code is not compatible with the current Swift version, you need to adjust it.
All of my other audio files play perfectly through the audio player. Most of the files are less than 5 seconds long, and the longest file that still plays is 23 seconds long. For some reason my 1:15 long file "sortSong" is completely silent when it is called.
Here is the code for my audio player:
import Foundation
import AVFoundation
class AudioPlayer {
static let sharedInstance = AudioPlayer()
enum Sound: String {
case sortSongPlay = "sortSong"
case centerButtonRelease = "centerButtonRelease"
case buttonTap = "tapSound"
static var all: [Sound] {
return [.centerButtonRelease, .buttonTap, sortSongPlay]
}
var url: URL {
return URL.init(fileURLWithPath: Bundle.main.path(forResource: self.rawValue, ofType: "mp3")!)
}
}
private var soundMap = [String: SystemSoundID]()
init() {
for sound in Sound.all {
let soundUrl = sound.url
var soundId: SystemSoundID = 0
AudioServicesCreateSystemSoundID(soundUrl as CFURL, &soundId);
soundMap[sound.rawValue] = soundId
}
}
func play(sound: Sound) {
AudioServicesPlaySystemSound(soundMap[sound.rawValue]!)
}
}
The sound is played when this function is called in my view controller.
func successfulSort() {
AudioPlayer.sharedInstance.play(sound: .sortSongPlay)
rotateSortImageOne()
rotateSortImageTwo()
}
This is the action that calls the successfulSort func
if inputText == "hello" && seedArray == [1, 4, 1] {
successfulSort()
}
If I simply change the case sortSongPlay to = "shortSortSong" (the 23 second version) it plays just fine.
All of my sound files have their target memberships checked for this project file, and all of the files have the correct path. The audio will play in the interface builder if I press the play button. There are no compiler errors or warnings, the app never crashes, the audio for sortSong simply isn't playing when it is called in the app.
This is a link containing examples I have tried on my project. The first two sounds play silently in the app while the shorter sounds all play perfectly. https://soundcloud.com/spiffystache
What is causing this to be silent?
You should put a correct title on your question.
Your code does not use AVAudioPlayer, but uses System Sound Services.
The documentation clearly states its limitation:
You can use System Sound Services to play short (30 seconds or
shorter) sounds.
I have never checked if its limitation is exactly 30 seconds, but it matches the description in your question.
Consider using AVAudioPlayer (as in the title) to play longer sound.
Yes I have looked around.
var congratzSound = NSBundle.mainBundle().URLForResource("Sweg.aiff", withExtension: "aiff")
var congratzPlayer:AVAudioPlayer? = AVAudioPlayer()
congratzPlayer = AVAudioPlayer(contentsOfURL: congratzSound, error: nil)
if let congratzPlayer = audioPlayer {
congratzPlayer.play()
}
This code complies fine, the file is in the Supporting Files section AND being coppied to the device.
What am I doing wrong? Is the file extension not playable? Is the congratzSound set up wrong?
Thanks in advance.
Using the following should solve your problem:
var congratzSound = NSBundle.mainBundle().URLForResource("Sweg", withExtension: "aiff")
If that doesn't work, try:
var congratzSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Sweg", ofType: "aiff")!)
Also, please check whether the file name is as-it-is (Sweg.aiff) in your code project and it is present in your app folder.
It is likely that your device is looking for Sweg.aiff.aiff while trying to play the sound, which obviously doesn't exist