I have sounds stored for different events within the struct that the user will be able to change, and was hoping i could send the function a string to select a song for a specific event.
my function call would look like this:
func playSound(audio: Audio, soundSelect: String = "startSound"){
if let path = Bundle.main.path(forResource: audio.\(soundSelect), ofType: audio.soundType){
do {
audioPlayer = try! AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
audioPlayer?.play()
}catch{
print("ERROR: Could not find and play the sound file!")
}
and my struct would look something like this:
struct Audio {
var startSound: String = "happyMusic"
var endSound: String = "sadMusic"
var soundType: String = "mp3"
}
as above i tried string interpolation which didn't seem to work I got the error
"Expected member name following '.'
What I expected was for "audio.\(soundSelect)"
to be read like "audio.startSound"
You cannot build variable names at runtime because all variable names are evaluated at compile time.
But if you define the type of the parameter as a KeyPath
you are able to address different properties in the struct
func playSound(audio: Audio, soundSelect: KeyPath<Audio,String> = \.startSound) {
if let url = Bundle.main.url(forResource: audio[keyPath: soundSelect], withExtension: audio.soundType) {
do {
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer?.play()
} catch {
print("ERROR: Could not find and play the sound file!", error)
}
}
}
Notes:
If you implement a do - catch
block handle (removing the question mark in try?
) and print
the error rather than just a meaningless string literal.
Bundle
provides an URL related API which avoids the extra URL(fileURLWithPath
call
A still swiftier syntax is to make the function throw
and hand over the error(s) to the caller
func playSound(audio: Audio, soundSelect: KeyPath<Audio,String> = \.startSound) throws {
if let url = Bundle.main.url(forResource: audio[keyPath: soundSelect], withExtension: audio.soundType) {
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer?.play()
} else {
throw URLError(.badURL)
}
}