I\'m finding a number of conflicting data about playing sounds in iOS. What is a recommended way to play just a simple \"ping\" sound bite every time the user touches the sc
Here's an updated answer for Swift (4):
import AudioToolbox
func playSound() {
var soundId: SystemSoundID = 0
guard let soundPath = Bundle.main.url(forResource: "Success7", withExtension: "wav") else {
print("Error finding file")
return
}
let error = AudioServicesCreateSystemSoundID(soundPath as CFURL, &soundId)
if error != kAudioServicesNoError {
print("Error loading sound")
return
}
AudioServicesPlaySystemSoundWithCompletion(soundId) {
AudioServicesDisposeSystemSoundID(soundId)
}
}
If you have a sound effect that you want to play multiple times in a view, then you can be a little more smart about loading and disposing of the audio:
class YourViewController: UIViewController {
fileprivate lazy var soundId: SystemSoundID? = {
guard let soundPath = Bundle.main.url(forResource: "Success7", withExtension: "wav") else {
return nil
}
var soundId: SystemSoundID = 0
let error = AudioServicesCreateSystemSoundID(soundPath as CFURL, &soundId)
if error != kAudioServicesNoError {
return nil
}
return soundId
}()
func playScannedSound() {
guard let soundId = self.soundId else {
return
}
AudioServicesPlaySystemSoundWithCompletion(soundId, nil)
}
deinit {
guard let soundId = self.soundId else {
return
}
AudioServicesDisposeSystemSoundID(soundId)
}
}
This is the best way of playing a simple sound in iOS (no more than 30 seconds):
//Retrieve audio file
NSString *path = [[NSBundle mainBundle] pathForResource:@"soundeffect" ofType:@"m4a"];
NSURL *pathURL = [NSURL fileURLWithPath : path];
SystemSoundID audioEffect;
AudioServicesCreateSystemSoundID((__bridge CFURLRef) pathURL, &audioEffect);
AudioServicesPlaySystemSound(audioEffect);
// call the following function when the sound is no longer used
// (must be done AFTER the sound is done playing)
AudioServicesDisposeSystemSoundID(audioEffect);
You can use AVFoundation
or AudioToolbox
.
Here're two examples which use the libraries separately.
I use this:
Header file:
#import <AudioToolbox/AudioServices.h>
@interface SoundEffect : NSObject
{
SystemSoundID soundID;
}
- (id)initWithSoundNamed:(NSString *)filename;
- (void)play;
@end
Source file:
#import "SoundEffect.h"
@implementation SoundEffect
- (id)initWithSoundNamed:(NSString *)filename
{
if ((self = [super init]))
{
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
if (fileURL != nil)
{
SystemSoundID theSoundID;
OSStatus error = AudioServicesCreateSystemSoundID((__bridge CFURLRef)fileURL, &theSoundID);
if (error == kAudioServicesNoError)
soundID = theSoundID;
}
}
return self;
}
- (void)dealloc
{
AudioServicesDisposeSystemSoundID(soundID);
}
- (void)play
{
AudioServicesPlaySystemSound(soundID);
}
@end
You will need to create an instance of SoundEffect and direct call the method play on it.
(Small amendment to the correct answer to take care of the disposing of the audio)
NSString *path = [[NSBundle mainBundle] pathForResource:@"soundeffect" ofType:@"m4a"];
NSURL *pathURL = [NSURL fileURLWithPath : path];
SystemSoundID audioEffect;
AudioServicesCreateSystemSoundID((__bridge CFURLRef) pathURL, &audioEffect);
AudioServicesPlaySystemSound(audioEffect);
// Using GCD, we can use a block to dispose of the audio effect without using a NSTimer or something else to figure out when it'll be finished playing.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(30 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
AudioServicesDisposeSystemSoundID(audioEffect);
});