Swift help for a newbie - ios

I am getting the following error message and cannot figure out what I am doing wrong. This is my first program and don't know too much on how to debug it.
Error message:
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
Code:
import UIKit
import AVFoundation
class PlaySoundsViewController: UIViewController {
var audioPlayer:AVAudioPlayer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if var filePath = NSBundle.mainBundle().pathForResource("crankringtone", ofType: "mp3"){
var filePathUrl = NSURL.fileURLWithPath(filePath)
audioPlayer = AVAudioPlayer(contentsOfURL: filePathUrl, error: nil)
} else {
println("file path is empty")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func playSlowAudio(sender: UIButton) {
audioPlayer.stop()
}
}

You need to check if the value of filePathUrl is non-nil before using it.

NSURL.fileURLWithPath(filePath) returns an Optional value which could potentially be nil. You should check before using it:
if let filePath = NSBundle.mainBundle().pathForResource("crankringtone", ofType: "mp3"){
if let filePathUrl = NSURL.fileURLWithPath(filePath) {
audioPlayer = AVAudioPlayer(contentsOfURL: filePathUrl, error: nil)
}
} else {
println("file path is empty")
}

In your code, you've declared the variable audioPlayer var audioPlayer:AVAudioPlayer! The exclamation mark means that it can't be nil when unwrapped.
So, in this code:
if var filePath = NSBundle.mainBundle().pathForResource("crankringtone", ofType: "mp3"){
var filePathUrl = NSURL.fileURLWithPath(filePath)
audioPlayer = AVAudioPlayer(contentsOfURL: filePathUrl, error: nil)
} else {
println("file path is empty")
}
You've got a variable called filePathUrl. That variable is using NSURL.fileURLWithPath(filePath). This can return nil. If it does, then your variable filePathUrl is nil. Then when you use that in audioPlayer, it's nil, so audioPlayer returns nil.
But remember, audioPlayer has a !. It can't be nil. So what you can do (simplest option), is to have an extra if statement:
if let filePath = NSBundle.mainBundle().pathForResource("crankringtone", ofType: "mp3"){
if let filePathUrl = NSURL.fileURLWithPath(filePath) {
audioPlayer = AVAudioPlayer(contentsOfURL: filePathUrl, error: nil)
}
} else {
println("file path is empty")
}
This extra if statement double checks that filePathUrl isn't nil. If it is, it sends the program to the else statement. If it isn't nil, then it allows audioPlayer to do its thing. And, because we know the value isn't nil, audioPlayer won't return an error.

The crash is most likely happening when you tap a button and the playSlowAudio function gets called.
The problem is you are using an implicitly unwrapped optional for audioPlayer. If the initialization of the player failed (for example, file wasn't found), then audioPlayer will be nil. So when the button action is called, audioPlayer.stop() attempt to unwrap audioPlayer before calling stop(), which causes the crash.
To fix, change audioPlayer to be a regular optional:
var audioPlayer:AVAudioPlayer?
And use it with the safer optional chaining:
audioPlayer?.stop()
So the full example will look like this:
import UIKit
import AVFoundation
class PlaySoundsViewController: UIViewController {
var audioPlayer:AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if var filePath = NSBundle.mainBundle().pathForResource("crankringtone", ofType: "mp3"){
var filePathUrl = NSURL.fileURLWithPath(filePath)
audioPlayer = AVAudioPlayer(contentsOfURL: filePathUrl, error: nil)
} else {
println("file path is empty")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func playSlowAudio(sender: UIButton) {
audioPlayer?.stop()
}
}

Related

Using AVAudioPlayer fail to play .m4a by Swift 4

I use Alamofire to download the iTunes search Api's trial music.
And when I download finished, I want to play the music.
I try to fix it, but it also no sounds to play.
How to solve this problem?
Thanks.
import UIKit
import AVFoundation
import Alamofire
import CryptoSwift
class FirstViewController: UIViewController {
let urlString = "https://audio-ssl.itunes.apple.com/apple-assets-us-std-000001/AudioPreview18/v4/9c/db/54/9cdb54b3-5c52-3063-b1ad-abe42955edb5/mzaf_520282131402737225.plus.aac.p.m4a"
override func viewDidLoad() {
super.viewDidLoad()
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileName = self.urlString.md5()
let fileURL = documentsURL.appendingPathComponent("\(fileName).m4a")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
Alamofire.download(urlString, to: destination).response { response in
if response.error == nil {
var audioPlayer: AVAudioPlayer!
do {
audioPlayer = try AVAudioPlayer(contentsOf: response.destinationURL!)
audioPlayer.prepareToPlay()
audioPlayer.play()
} catch {
print("Error:", error.localizedDescription)
}
}
}
}
}
The problem is caused by the fact that audioPlayer is a local variable, therefore it gets deallocated when you leave the scope of completion closure. Because audioPlayer isn't retained anywhere else, the moment you leave the closure's scope, audioPlayer reference count is equal to 0, which causes ARC to deallocate it.
Also, you use force unwrapping operator - ! - a lot, which is:
1) incorrect
2) unsafe
Use either if let construct or guard statement
What you need to to is store the player as instance variable of your FirstViewController class.
class FirstViewController: UIViewController {
let urlString = "https://audio-ssl.itunes.apple.com/apple-assets-us-std-000001/AudioPreview18/v4/9c/db/54/9cdb54b3-5c52-3063-b1ad-abe42955edb5/mzaf_520282131402737225.plus.aac.p.m4a"
var audioPlayer : AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
// (...)
Alamofire.download(urlString, to: destination).response { [weak self] (response) in
if response.error == nil {
guard let url = response.destinationURL else { return }
do {
self?.audioPlayer = try AVAudioPlayer(contentsOf: url)
self?.audioPlayer?.prepareToPlay()
self?.audioPlayer?.play()
} catch {
print("Error:", error.localizedDescription)
}
}
}
}
}
Just move audioPlayer to controller
class FirstViewController: UIViewController {
let urlString = "https://audio-ssl.itunes.apple.com/apple-assets-us-std-000001/AudioPreview18/v4/9c/db/54/9cdb54b3-5c52-3063-b1ad-abe42955edb5/mzaf_520282131402737225.plus.aac.p.m4a"
var audioPlayer: AVAudioPlayer?
//Downloading code......
}

Error: fatal error: unexpectedly found nil while unwrapping an Optional value

I am fairly new to this and trying to figure out the correct format to solve the error in the title. I get on the line: let audioPath = NSBundle.mainBundle().pathForResource("Pugs.m4a", ofType: nil)!
I know I must be missing something just not sure where.
import UIKit
import AVFoundation
class ViewController: UIViewController {
#IBOutlet var playButton: UIButton!
var playPug = 1
var player: AVAudioPlayer!
#IBAction func playPressed(sender: AnyObject) {
let audioPath = NSBundle.mainBundle().pathForResource("Pugs.m4a", ofType: nil)!
let url = NSURL(fileURLWithPath: audioPath)
do {
if playPug == 1 {
let sound = try AVAudioPlayer(contentsOfURL: url)
player = sound
sound.play()
playPug = 2
playButton.setImage(UIImage(named:"pause_Icon.png"),forState:UIControlState.Normal)
} else {
player.pause()
playPug = 1
playButton.setImage(UIImage(named:"play_Icon.png"),forState:UIControlState.Normal)
}
} catch {
print(error)
}
}
The reason you're getting fatal error: unexpectedly found nil while unwrapping an Optional value is because of the ! in this line of code:
let audioPath = NSBundle.mainBundle().pathForResource("Pugs.m4a", ofType: nil)!
It's crashing because you're using ! to force unwrap the value returned by pathForResource(_:ofType:), which is unsafe. If the value is nil, you get the unexpectedly found nil error. You should really only force unwrap things when you know they're not going to be nil.
Try doing something like this instead:
Option 1:
guard let audioPath = NSBundle.mainBundle().pathForResource("Pugs.m4a", ofType: nil) else {
// The resource does not exist, so the path is nil.
// Deal with the problem in here and then exit the method.
}
// The resource exists, so you can use the path.
Option 2:
Use optional binding, like this:
if let audioPath = NSBundle.mainBundle().pathForResource("Pugs.m4a", ofType: nil) {
// The resource exists, and now you have the path, so you can use it.
let url = NSURL(fileURLWithPath: audioPath)
do {
if playPug == 1 {
let sound = try AVAudioPlayer(contentsOfURL: url)
player = sound
sound.play()
playPug = 2
playButton.setImage(UIImage(named:"pause_Icon.png"),forState:UIControlState.Normal)
} else {
player.pause()
playPug = 1
playButton.setImage(UIImage(named:"play_Icon.png"),forState:UIControlState.Normal)
}
} catch {
print(error)
}
} else {
// The path was nil, so deal with the problem here.
}

Playing an embedded sound using swift 2.0 code Xcode 7.1 IOS 9.1

Copied and Pasted this code principally. Compiles and runs, but plays nothing. Using Xcode 7.1 and IOS 9.1. What have I missed... Loaded sound file into main program and AVAssets...
import UIKit
import AVFoundation
class ViewController: UIViewController {
var buttonBeep : AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
buttonBeep = setupAudioPlayerWithFile("hotel_transylvania2", type:"mp3")
//buttonBeep?.volume = 0.9
buttonBeep?.play()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setupAudioPlayerWithFile(file:NSString, type:NSString) -> AVAudioPlayer? {
//1
let path = NSBundle.mainBundle().pathForResource(file as String, ofType: type as String)
let url = NSURL.fileURLWithPath(path!)
//2
var audioPlayer:AVAudioPlayer?
// 3
do {
try audioPlayer? = AVAudioPlayer(contentsOfURL: url)
} catch {
print("Player not available")
}
return audioPlayer
}
}
You've got this line backwards:
try audioPlayer? = AVAudioPlayer(contentsOfURL: url)
It should be:
audioPlayer = try AVAudioPlayer(contentsOfURL: url)
Side note: the conversion to and from NSString is not necessary here, just use String - and you should not force unwrap the result of NSBundle:
func setupAudioPlayerWithFile(file:String, type:String) -> AVAudioPlayer? {
//1
guard let path = NSBundle.mainBundle().pathForResource(file, ofType: type) else {
return nil
}
let url = NSURL.fileURLWithPath(path)
//2
var audioPlayer:AVAudioPlayer?
// 3
do {
audioPlayer = try AVAudioPlayer(contentsOfURL: url)
} catch {
print("Player not available")
}
return audioPlayer
}

AVAudioPlayer wont play sound when called in other swift file

I have two swift files - my ViewController:UIViewController and AudioPlayer:AVAudioPlayer.
My AudioPlayer file has this function
func seaGullSound() {
var tmp = AVAudioPlayer()
var seaGullSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Gulls", ofType: "mp3")!)
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
var error:NSError?
tmp = AVAudioPlayer(contentsOfURL: seaGullSound, error: &error)
tmp.prepareToPlay()
tmp.play()
println("This function got called!")
}
I'm trying to call that function in my ViewController thru tapping a button, using this code:
#IBAction func playSound(sender: AnyObject) {
var audio = AudioPlayer()
audio.seaGullSound()
}
The sound is not played when I click the button. However, the print statement works. I can get the audio to play if I move seaGullSound() to the ViewController file, so I know the mp3 does work. I haven't moved the audio to ViewController because I want to develop the habit of not crowding all my code into one file. Thanks in advance for the help.
edit:
class HighScore: UIViewController {
var audioInitializer = AudioPlayer()
func updateHighScore(score:Int) -> String {
NSUserDefaults.standardUserDefaults().integerForKey("highscore")
//Check if score is higher than NSUserDefaults stored value and change NSUserDefaults stored value if it's true
if score > NSUserDefaults.standardUserDefaults().integerForKey("highscore") {
//call applause sound
audioInitializer.applauseSound()
//set score
NSUserDefaults.standardUserDefaults().setInteger(score, forKey: "highscore")
NSUserDefaults.standardUserDefaults().synchronize()
}
NSUserDefaults.standardUserDefaults().integerForKey("highscore")
//use below line to reset high score for testing
//NSUserDefaults.standardUserDefaults().removeObjectForKey("highscore")
return String(NSUserDefaults.standardUserDefaults().integerForKey("highscore"))
}}
here is the file with the sounds:
class AudioPlayer: AVAudioPlayer {
var soundMaster = AVAudioPlayer()
func tappingSound() {
var tapSoundURL = NSBundle.mainBundle().URLForResource("tapSound", withExtension: "mp3")
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
var error:NSError?
soundMaster = AVAudioPlayer(contentsOfURL: tapSoundURL, error: &error)
soundMaster.prepareToPlay()
soundMaster.play()
}
//need to call in highscore.swift
func applauseSound() {
var tapSoundURL = NSBundle.mainBundle().URLForResource("applause", withExtension: "mp3")
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
var error:NSError?
soundMaster = AVAudioPlayer(contentsOfURL: tapSoundURL, error: &error)
soundMaster.prepareToPlay()
soundMaster.play()
println("did this get called?")
}}
You just have move the declaration of your tmp AVAudioPlayer out of your method. Declare it as class variable.
You should also use URLForResource instead of pathForResource:
let seaGullSoundURL = NSBundle.mainBundle().URLForResource("Gulls", withExtension: "mp3")!
Try like this:
import UIKit
import AVFoundation
class HighScore: UIViewController {
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func updateHighScore(score:Int) -> String {
//Check if score is higher than NSUserDefaults stored value and change NSUserDefaults stored value if it's true
if score > NSUserDefaults().integerForKey("highscore") {
//call applause sound
playAudio("applause")
//set score
NSUserDefaults().setInteger(score, forKey: "highscore")
}
//use below line to reset high score for testing
//NSUserDefaults.standardUserDefaults().removeObjectForKey("highscore")
return NSUserDefaults().integerForKey("highscore").description
}
func playAudio(audioName: String ) {
var error:NSError?
if let audioURL = NSBundle.mainBundle().URLForResource(audioName, withExtension: "mp3") {
audioPlayer = AVAudioPlayer(contentsOfURL: audioURL, error: &error)
audioPlayer.prepareToPlay()
audioPlayer.play()
} else if let error = error {
println(error.description)
}
}
#IBAction func playSound(sender: UIButton) {
playAudio("Gulls")
}
}

AVAudioPlayer not playing audio in Swift

I have this code in a very simple, single view Swift application in my ViewController:
var audioPlayer = AVAudioPlayer()
#IBAction func playMyFile(sender: AnyObject) {
let fileString = NSBundle.mainBundle().pathForResource("audioFile", ofType: "m4a")
let url = NSURL(fileURLWithPath: fileString)
var error : NSError?
audioPlayer = AVAudioPlayer(contentsOfURL: url, error: &error)
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
if (audioPlayer.isEqual(nil)) {
println("There was an error: (er)")
} else {
audioPlayer.play()
NSLog("working")
}
I have added import AVFoundation and audioPlayer is a global variable. When I execute the code, it does print "working", so it makes it through without errors but no sound is played. The device is not in silent.
There's so much wrong with your code that Socratic method breaks down; it will probably be easiest just to throw it out and show you:
var player : AVAudioPlayer! = nil // will be Optional, must supply initializer
#IBAction func playMyFile(sender: AnyObject?) {
let path = NSBundle.mainBundle().pathForResource("audioFile", ofType:"m4a")
let fileURL = NSURL(fileURLWithPath: path)
player = AVAudioPlayer(contentsOfURL: fileURL, error: nil)
player.prepareToPlay()
player.delegate = self
player.play()
}
I have not bothered to do any error checking, but the upside is you'll crash if there's a problem.
One final point, which may or may not be relevant: not every m4a file is playable. A highly compressed file, for example, can fail silently (pun intended).
Important that AvPlayer is class member and not in the given function, else it goes out of scope... :)
I had to declare a global player variable
var player: AVAudioPlayer!
and set it in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
player = AVAudioPlayer()
}
Then I could play the audio file wherever like this:
func playAudioFile(){
do {
if audioFileUrl == nil{
return
}
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
/* The following line is required for the player to work on iOS 11. Change the file type accordingly*/
player = try AVAudioPlayer(contentsOf: audioFileUrl, fileTypeHint: AVFileType.m4a.rawValue)
/* iOS 10 and earlier require the following line:
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3) */
guard let player = player else { return }
player.play()
print("PLAYING::::: \(audioFileUrl)")
}
catch let error {
print(error.localizedDescription)
}
}
}
Here is a working snippet from my swift project. Replace "audiofile" by your file name.
var audioPlayer = AVAudioPlayer()
let audioPath = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("audiofile", ofType: "mp3"))
audioPlayer = AVAudioPlayer(contentsOfURL: audioPath, error: nil)
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
audioPlayer.play()
You can download fully functional Swift Audio Player application source code from here https://github.com/bpolat/Music-Player
for some reason (probably a bug) Xcode can't play certain music files in the .m4a and the .mp3 format I would recommend changing them all to .wav files to get it to play
//top of your class
var audioPlayer = AVAudioPlayer
//where you want to play your sound
let Sound = NSURL(fileURLWithPath: Bundle.main.path(forResource: "sound", ofType: "wav")!)
do {
audioPlayer = try AVAudioPlayer(contentsOf: Sound as URL)
audioPlayer.prepareToPlay()
} catch {
print("Problem in getting File")
}
audioPlayer.play()
var audioPlayer = AVAudioPlayer()
var alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("KiepRongBuon", ofType: "mp3")!)
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
var error:NSError?
audioPlayer = AVAudioPlayer(contentsOfURL: alertSound, error: &error)
audioPlayer.prepareToPlay()
audioPlayer.play()
I used the below code in my app and it works. Hope that is helpful.
var audioPlayer: AVAudioPlayer!
if var filePath = NSBundle.mainBundle().pathForResource("audioFile", ofType:"mp3"){
var filePathUrl = NSURL.fileURLWithPath(filePath)
audioPlayer = AVAudioPlayer(contentsOfURL: filePathUrl, error: nil)
audioPlayer.play()
}else {
println("Path for audio file not found")
}
In Swift Coding using Try catch, this issues will solve and play audio for me and my code below,
var playerVal = AVAudioPlayer()
#IBAction func btnPlayAction(sender: AnyObject) {
let fileURL: NSURL = NSURL(string: url)!
let soundData = NSData(contentsOfURL: fileURL)
do {
playerVal = try AVAudioPlayer(data: soundData!)
}
catch {
print("Something bad happened. Try catching specific errors to narrow things down",error)
}
playerVal.delegate = self
playerVal.prepareToPlay()
playerVal.play()
}
Based on #matt answer but little bit detailed 'cause original answer did not completely satisfied me.
import AVFoundation
class YourController: UIViewController {
private var player : AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
prepareAudioPlayer()
}
#IBAction func playAudio() {
player?.play()
}
}
extension YourController: AVAudioPlayerDelegate {}
private extension YourController {
func prepareAudioPlayer() {
guard let path = Bundle.main.path(forResource: "you-audio", ofType:"mp3") else {
return
}
let fileURL = URL(fileURLWithPath: path)
do {
player = try AVAudioPlayer(contentsOf: fileURL)
} catch let ex {
print(ex.localizedDescription)
}
player?.prepareToPlay()
player?.delegate = self
}
}
swift 3.0:
import UIKit
import AVFoundation
class ViewController: UIViewController
{
var audioplayer = AVAudioPlayer()
#IBAction func Play(_ sender: Any)
{
audioplayer.play()
}
#IBAction func Pause(_ sender: Any)
{
if audioplayer.isPlaying
{
audioplayer.pause()
}
else
{
}
}
#IBAction func Restart(_ sender: Any)
{
if audioplayer.isPlaying
{
audioplayer.currentTime = 0
audioplayer.play()
}
else
{
audioplayer.play()
}
}
override func viewDidLoad()
{
super.viewDidLoad()
do
{
audioplayer = try AVAudioPlayer(contentsOf:URL.init(fileURLWithPath:Bundle.main.path(forResource:"bahubali", ofType: "mp3")!))
audioplayer.prepareToPlay()
var audioSession = AVAudioSession.sharedInstance()
do
{
try audioSession.setCategory(AVAudioSessionCategoryPlayback)
}
catch
{
}
}
catch
{
print (error)
}
}
}

Resources