Swift 3 enum in functions causes app to crash - swift

I'm trying to play a sound based on file name. I created an enum with all file names. Everything works, except this case, where I check for the soundType.click
func playSound(type: soundType) {
var soundUrl = URL.init(fileURLWithPath: Bundle.main.path(forResource: type.rawValue, ofType: "aiff")!)
if type.rawValue == soundType.click.rawValue {
soundUrl = URL.init(fileURLWithPath: Bundle.main.path(forResource: type.rawValue, ofType: "wav")!)
}
do {
audioPlayer = try AVAudioPlayer(contentsOf: soundUrl)
audioPlayer.play()
} catch _ { }
}
And here is my enum
enum soundType: String {
case selectAnswer = "answerSelected"
case correctAnswer = "correctAnswer"
case wrongAnswer = "wrongAnswer"
case click = "click"
}
The problem is here where I check for "type.rawValue == soundType.click.rawValue"
Here is the error
fatal error: unexpectedly found nil while unwrapping an Optional value

You should take a look at this line of code first.
var soundUrl = URL.init(fileURLWithPath: Bundle.main.path(forResource: type.rawValue, ofType: "aiff")!)
soundUrl = URL.init(fileURLWithPath: Bundle.main.path(forResource: type.rawValue, ofType: "wav")!)
Here, you are force unwrapping a failable initializer. You should check if Bundle.main.path(forResource: type.rawValue, ofType: "aiff")!) exists first by doing something like this...
if let soundUrl = URL.init(fileURLWithPath: Bundle.main.path(forResource: type.rawValue, ofType: "aiff")){
if type.rawValue == soundType.click.rawValue {
...
}
or you could also use a guard statement..
Check this blog post by Natashtherobot to learn more about how to unwrap stuff well. https://www.natashatherobot.com/swift-guard-better-than-if/

Related

Playing music in Swift: Unexpectedly found nil while unwrapping an Optional value

Working on a project, I decided to add some music to play when a certain action is triggered. Here's what I've got:
var player : AVAudioPlayer?
several lines later...
func playSound(){
let alertSound = URL(fileURLWithPath: Bundle.main.path(forResource: "Kalimba", ofType: "wav")!)
print(alertSound)
try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try! AVAudioSession.sharedInstance().setActive(true)
try! player = AVAudioPlayer(contentsOf: alertSound)
player!.prepareToPlay()
player!.play()
}
Whenever I try this, xcode throws me a fatal error when the action is triggered, saying:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
You should check with your music file is attached with your target? -
let path = Bundle.main.path(forResource: "Kalimba", ofType: "wav")
if path != nil {
let url = URL(fileURLWithPath: path!)
print(url)
let player = AVPlayer(url: url)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
self.view.layer.addSublayer(playerLayer)
player.play()
} else {
print("File Not exist")
}

Why do I keep getting this error??? fatal error: unexpectedly found nil while unwrapping an Optional value

I am trying to create an app with sound built in. Whenever I try to build my program I get this error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Here is my code:
var magicSound: AVAudioPlayer = AVAudioPlayer()
#IBOutlet var Answer: UILabel!
var AnswerArray = ["Yes", "No", "Maybe", "Try Again", "Not Now", "No Doubt", "Yes Indeed", "Of course", "Definetley Not"]
var chosenAnswer = 0
override func viewDidLoad() {
super.viewDidLoad()
let magicFile = Bundle.main.path(forResource: "MagicSound", ofType: ".wav")
do {
try magicSound = AVAudioPlayer(contentsOf: URL (fileURLWithPath: magicFile!))
}
catch {
print(error)
}
}
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
if event?.subtype == motion {
printAnswer()
randomAnswer()
animation()
showingAnswerAnimation()
magicSound.play()
}
}
The console throws the error at the line,
try magicSound = AVAudioPlayer(contentsOf: URL (fileURLWithPath: magicFile!))
If anyone could help me fix my code that would be great.
I believe the line:
let magicFile = Bundle.main.path(forResource: "MagicSound", ofType: ".wav")
should be:
let magicFile = Bundle.main.path(forResource: "MagicSound", ofType: "wav")
The dot character is implicit in to the type parameter.
This is how I would write it:
if let magicFile = Bundle.main.path(forResource: "MagicSound", ofType: "wav") {
let magicSound = try? AVAudioPlayer(contentsOf: URL (fileURLWithPath: magicFile))
}
else {
print( "MagicSound.wav does not exist in main bundle" )
}
Make sure that the file name is an exact case-sensitive match.Also make sure the resource file is at the top level of the bundle (i.e. in the same folder as the .xcodeproj and .xcworkspace file).
Okay this unexpected found nil while unwrapping an Optional value is pretty common errors in SO. You should search more before posting the question.
This magicFile! is the main culprit here. The use of ! means you are unwrapping such a value that you are cent percent sure that the value does contain something other than nil. But if the value is nil your code is supposed to crash and give you a hint that you're assuming something wrong about the value. So change it.
Now comes the rescue. You have multiple options to safely unwrap an Optional value. Such as optional-binding. Having said that you should change your implementation like:
if let magicFile = Bundle.main.url(forResource:"MagicSound" withExtension:"wav") { //As you are supposed to use url, no need to have the path as String
do {
try magicSound = AVAudioPlayer(contentsOf: magicFile)
}
catch {
print(error)
}
}
Try using below Code hope it helps
func playSound() {
guard let url = Bundle.main.url(forResource: "MagicSound", withExtension: "wav") else { return }
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
player = try AVAudioPlayer(contentsOf: url)
guard let player = magicSound else { return }
player.play()
} catch let error {
print(error.localizedDescription)
}
}
and make sure to import AVFoundation use this

decode morse code by character swift

I am trying to make a morse code converter in a swift playground. I got the conversion to work, but I need to make the code "speak" with AVFoundation. How can I decode the morse code string to play the short beep for every '.' and the long beep for every '-'?
Here's my code so far:
func speakTheCode(message: String) {
var speaker = AVAudioPlayer()
let longBeep = URL(fileURLWithPath: Bundle.main.path(forResource: "beep_long", ofType: "mp3")!)
let shortBeep = URL(fileURLWithPath: Bundle.main.path(forResource: "beep_short", ofType: "mp3")!)
try! speaker = AVAudioPlayer(contentsOf: longBeep)
try! speaker = AVAudioPlayer(contentsOf: shortBeep)
speaker.prepareToPlay()
}
Just try to decode the string to the correspondingly audio.
func speakTheCode(message: String) {
var audioItems: [AVPlayerItem] = []
guard let longPath = Bundle.main.path(forResource: "beep_long", ofType: "mp3"),
let shortPath = Bundle.main.path(forResource: "beep_short", ofType: "mp3") else {
print("Path is not availabel")
return
}
let longBeep = AVPlayerItem(url: URL(fileURLWithPath: longPath))
let shortBeep = AVPlayerItem(url: URL(fileURLWithPath: shortPath))
for character in message.characters {
if character == Character("-") {
audioItems.append(longBeep)
} else if character == Character(".") {
audioItems.append(shortBeep)
}
}
let player = AVQueuePlayer(items: audioItems)
player.play()
}
speakTheCode(message: "..--..")

Looking to understand Swift Functions a bit better

I'm new to swift and have a lot of repeating code. For example how would you change the following code into different functions:
Example 1
if let button = view.viewWithTag(12) as? UIButton {
// change any properties of the button as you would normally do
button.isHidden = true
}
Example 2
var oneAPlayer = AVAudioPlayer()
var oneBPlayer = AVAudioPlayer()
var oneCPlayer = AVAudioPlayer()
var oneDPlayer = AVAudioPlayer()
var twoAPlayer = AVAudioPlayer()
var threeAPlayer = AVAudioPlayer()
var fourAPlayer = AVAudioPlayer()
let oneASound = Bundle.main.path(forResource: "1-a", ofType: "mp3")
let oneBSound = Bundle.main.path(forResource: "1-b", ofType: "mp3")
let oneCSound = Bundle.main.path(forResource: "1-c", ofType: "mp3")
let oneDSound = Bundle.main.path(forResource: "1-d", ofType: "mp3")
let twoASound = Bundle.main.path(forResource: "2-a", ofType: "mp3")
let threeASound = Bundle.main.path(forResource: "3-a", ofType: "mp3")
let fourASound = Bundle.main.path(forResource: "4-a", ofType: "mp3")
I wouldn't necessarily use a new function for this. I would write it like so:
let soundNames = ["1-a", "1-b", "1-c", "1-d", "2-a", "3-a", "4-a"]
let sounds = soundNames.map{ Bundle.main.path(forResource: $0, ofType: "mp3") }
Alex answer is good, but it's pretty complicated for someone new. I would suggest learning the basics of functions before attempting functional programming (usage of .map, .filter, and other fun stuff).
Here is a simple example that you can modify in numerous ways to be more flexible if desired:
var sounds: [String:String] = [:]
func addSound(_ num: Int,_ letter: Character) {
let key = String(num) + "-" + String(letter)
sounds[key] = Bundle.main.path(forResource: key, ofType: "mp3")
}
// Usage:
addSound(1,"a")
print(sounds["1-a"])
What we are doing is using just one variable to hold the sounds in... You access your different sounds by typing in the string, as demonstrated in the usage section.
This is a dictionary, and they are very powerful. They work based on Key and Value.
So here we have key of "1-a" and the value will be the bundle that we added in the function.
The function basically just converts the input parameters, an integer and a Character, and turns it into a string that we can use with our Bundle and Dictionary.
If you want to add many sounds at one time, you can convert this to an array:
func addSounds(_ keys: [String]) {
for key in keys {
sounds[key] = Bundle.main.path(forResource: key, ofType: "mp3")
}
}
// Usage:
addSounds( ["1-a", "1-b"])
print( sounds["1-b"])
This second example is exactly what Alex did, his version used .map which is actually a function that does basically the same thing as the for loop. It's a great feature, but it focuses on what's called declarative programming --which is great--but generally difficult to learn when just starting out.

What can I do to fix this error?

I get an error every time I try to run stating
fatal error: unexpectedly found nil while unwrapping an Optional value (lldb).
Could someone explain why? heres the code
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player: AVAudioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
let audioPath = NSBundle.mainBundle().pathForResource("Belly - Might Not", ofType: "mp3")!
do {
try player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: audioPath))
player.play()
} catch {
// Process error here
}
}
}
This error is almost always caused by force unwrapping an object, i.e. the "!"operator. for your code, it's likely this line:
let audioPath = NSBundle.mainBundle().pathForResource("Belly - Might Not", ofType: "mp3")!
Chances are t can't find that file. To be safe with it and handle this error case, use this:
if let audioPath = NSBundle.mainBundle().pathForResource("Belly - Might Not", ofType: "mp3") {
/* do what you need to with the path*/
}
else{
/* handle error case */
}
You're force-unwrapping the optional in your line of code:
let audioPath = NSBundle.mainBundle().pathForResource("Belly - Might Not", ofType: "mp3")!
This file can returns an optional in case of not exist the resource, avoid the force unwrapping of the optional instead use optional-binding or a guard statement like in the following way. It's always recommend not make force-unwrapping of an optional because you're telling to the compiler that you know that always be different of nil and if it's happen you get an runtime error.
if let audioPath = NSBundle.mainBundle().pathForResource("Belly - Might Not", ofType: "mp3") {
do {
try player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: audioPath))
player.play()
} catch {
// Process error here
}
}
Or with guard:
guard let audioPath = NSBundle.mainBundle().pathForResource("Belly - Might Not", ofType: "mp3") else { return }
do {
try player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: audioPath))
player.play()
} catch {
// Process error here
}
I hope this help you.
May be audio file not found. Try like this
if let audioPath = NSBundle.mainBundle().pathForResource("Belly - Might Not", ofType: "mp3"){
do {
try player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: audioPath))
player.play()
} catch {
// Process error here
}
}else{
print("path not found")
}