How to throw and handle an error in swift? - swift

here is my code (Swift):
import UIKit
import AVFoundation
class PlaySoundViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if var filePath = NSBundle.mainBundle().pathForResource("movie_quote",ofType: "mp3"){
var filePathUrl = NSURL.fileURLWithPath(filePath)
AVAUdioPlayer audioPlayer = AVAudioPlayer(contentsOfURL:filePathUrl) throws
}
else{
print("filePath is empty")
}
}
#IBAction func playSlowAudio(sender: UIButton) {
}
func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
this is the method I found on my "Documentation and API References" to play audio:
``
initWithContentsOfURL:error:
init(contentsOfURL url: NSURL) throws
So, I return a String as source path, then conver it to NSURL. Now i want to play the audio but the method I am using needs to throw the error and handle it. How should I throw and handle the error ?

Swift 2.0
AVAudioPlayer will throw an exception if its initializer fails. Catch the error by wrapping its initialization in a do/catch clause.
do {
let audioPlayer = try AVAudioPlayer(contentsOfURL: filePathUrl)
// use audioPlayer
} catch {
// handle error
}
As you can see, the keyword try is inserted before any method call that can throw exceptions. As long as the try statement doesn't throw, you can continue your code as normal. If the try statement does throw, you program will jump to the catch clause.
Examining the error
If you'd like to examine the error, you can convert it to an NSError by writing your catch statement like so (as seen in Apple's Objective-C/Swift Interoperability Docs):
do {
let audioPlayer = try AVAudioPlayer(contentsOfURL: filePathUrl)
// use audioPlayer
} catch let error as NSError {
// error is now an NSError instance; do what you will
}
Converting to NSError is only necessary if you want to examine an error thrown by one of Apple's Cocoa objects. Native Swift code, throwing native ErrorType errors, require no conversion.
I recommend you read Apple's new docs on error handling in Swift.
Swift 1.2
If you are using Swift 1.2, then there is no error handling available. Instead, AVAudioPlayer's initialization method will fail and return nil.
If you are using Swift 1.2, I would recommend initializing the audio player like this:
var initError: NSError?
if let audioPlayer = AVAudioPlayer(contentsOfURL: filePathUrl, error: &initError) {
// use audioPlayer
} else {
println(initError) // handle error
}

Since Swift 1.2, you cannot throw/handle exceptions. While it is available in swift2 (need XCode7 support) which is still in beta. See this article for detail (https://www.hackingwithswift.com/new-syntax-swift-2-error-handling-try-catch).

Related

SWIFT : Incorrect argument label in call Xcode 7.3.1

import SpriteKit
import AVFoundation
class HGBackgroundMusic: SKScene {
var audioPlayer = AVAudioPlayer()
func getSoundReady() {
var plop = NSURL(fileURLWithPath:NSBundle.mainBundle().pathForResource("Plop", ofType: "mp3")!)
var audioPlayer = AVAudioPlayer()
audioPlayer = AVAudioPlayer(contentsOfURL: plop, error: nil)
audioPlayer.prepareToPlay()
}
func playSound() {
audioPlayer.play()
}
}
In the
error:nil
line, an error is coming up stating: Incorrect argument label in call(have 'contentsOfURL:error:', expected'contentsOfURL:fileTypeHint:'). So I replace "error" with "fileTypeHint", and that brings up another error stating: Call can throw, but is not marked with 'try' and the error is not handled. I cannot fix this though I have spent hours trying to find what to do to solve it.
Replace your arc4random call with arc4random_uniform.

Incorrect argument label in call?

Help me, please!!! I've only recently started programming in Swift. This is my first project. I get the errormessage: "Incorrect argument label in call". This is my 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("psl",ofType: "mp3"){
var filePathUrl = NSURL.fileURLWithPath(filePath)
audioPlayer = AVAudioPlayer(contentsOfURL: filePathUrl, error: nil)
}else {
print("the filepath is empty")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func playSoundSlow(sender: UIButton) {
audioPlayer.play()
I'm not sure what went wrong here is another picture of my code so you can also see the errormessage.My Code
I'm trying to get it to play a mp3 called psl.mp3.
Please, help me!!! I just started and don't know what to do.
P.S. Not a native Englishspeaker, so sorry for mistakes.
Swift 2 adds new error handling, meaning you don't even have to pass in an error parameter:
audioPlayer = try! AVAudioPlayer(contentsOfURL: filePathUrl)
This initializer throws, meaning if there is an error, you can catch it in a do-catch statement. However, since you passed nil for the error parameter, I'm assuming you are sure the player is there. That's why I used try! without a do-catch instead of try in a do-catch.
Read about the new error handling here.
Try this
do {
audioPlayer = try AVAudioPlayer(contentsOfURL: NSURL.fileURLWithPath(path))
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
audioPlayer.play()
} catch {
print("Catch error in playUsingAudioPlayer")
}

Cannot convert value of type 'inout NSError?'

The errors i'm getting :
1)Extra argument 'error' in call
2) Cannot convert value of type 'inout NSError?' (aka 'inout Optional') to expected argument type '()'
My Code:
func initUdpSocket(){
var error : NSError?
mUdpSocket = GCDAsyncUdpSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
mUdpSocket.enableBroadcast(true,error: &error)
mUdpSocket.beginReceiving(&error)
}
func udpSocket(sock: GCDAsyncUdpSocket!, didReceiveData data: NSData!, fromAddress address: NSData!, withFilterContext filterContext: AnyObject!) {
print("\(__FUNCTION__),\(__LINE__),\(data)");
}
func udpSocket(sock: GCDAsyncUdpSocket!, didSendDataWithTag tag: Int) {
print("\(__FUNCTION__),\(__LINE__),\(tag)");
}
func udpSocket(sock: GCDAsyncUdpSocket!, didConnectToAddress address: NSData!) {
print("\(__FUNCTION__),\(__LINE__),\(address)");
}
func udpSocket(sock: GCDAsyncUdpSocket!, didNotConnect error: NSError!) {
print("\(__FUNCTION__),\(__LINE__),\(error)");
}
}
Can i know what is wrong with my codes?? I haven had any errors before updating my xcode.
Swift 2.2 and Objective-C Interoperability in Error Handling
This appears to be happening because, in Swift 2.2, Objective-C methods that take a NSError ** for their last parameter are converted to the Swift throws method equivalent.
You can find more information on this topic on this page, using the 3rd and 4th sections, excerpted below.
Error Handling (excerpt)
In Cocoa, methods that produce errors take an NSError pointer parameter as their last parameter, which populates its argument with an NSError object if an error occurs. Swift automatically translates Objective-C methods that produce errors into methods that throw an error according to Swift’s native error handling functionality.
For example, consider the following Objective-C method from NSFileManager:
- (BOOL)removeItemAtURL:(NSURL *)URL
error:(NSError **)error;
In Swift, it’s imported like this:
func removeItemAtURL(URL: NSURL) throws
Catching and Handling an Error (excerpt)
In Objective-C, error handling is opt-in, meaning that errors produced by calling a method are ignored unless an error pointer is provided. In Swift, calling a method that throws requires explicit error handling.
Here’s an example of how to handle an error when calling a method in Objective-C:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *fromURL = [NSURL fileURLWithPath:#"/path/to/old"];
NSURL *toURL = [NSURL fileURLWithPath:#"/path/to/new"];
NSError *error = nil;
BOOL success = [fileManager moveItemAtURL:URL toURL:toURL error:&error];
if (!success) {
NSLog(#"Error: %#", error.domain);
}
And here’s the equivalent code in Swift:
let fileManager = NSFileManager.defaultManager()
let fromURL = NSURL(fileURLWithPath: "/path/to/old")
let toURL = NSURL(fileURLWithPath: "/path/to/new")
do {
try fileManager.moveItemAtURL(fromURL, toURL: toURL)
} catch let error as NSError {
print("Error: \(error.domain)")
}
Additionally, you can use catch clauses to match on particular error codes as a convenient way to differentiate possible failure conditions:
do {
try fileManager.moveItemAtURL(fromURL, toURL: toURL)
} catch NSCocoaError.FileNoSuchFileError {
print("Error: no such file exists")
} catch NSCocoaError.FileReadUnsupportedSchemeError {
print("Error: unsupported scheme (should be 'file://')")
}
Question-specific Code
In this code example, this means that, in Swift 2.2, the enableBroadcast(error:) method of GCDAsyncUdpSocket would be enableBroadcast() throws and would need to use an explicit do-catch.
Similarly, the beginReceiving() method of GCDAsyncUdpSocket would turn into beginReceiving() throws and would also require the use of a do-catch.

Using Xcode 7 for 1st time and I'm getting an extra argument error call with the following code -

Getting extra argument error call in relation to the 'items' constant below. Has this changed following Xcode 7.0?
override func viewDidLoad() {
super.viewDidLoad()
let fm = NSFileManager.defaultManager()
let path = NSBundle.mainBundle().resourcePath!
let items = fm.contentsOfDirectoryAtPath(path, error: nil)
for item in items as! [String] {
if item.hasPrefix("nssl") {
objects.append(item)
}
}
According to Apple for contentsOfDirectoryAtPath:
In Swift, this method returns a nonoptional result and is marked with
the throws keyword to indicate that it throws an error in cases of
failure.
You call this method in a try expression and handle any errors in the
catch clauses of a do statement, as described in Error Handling
in The Swift Programming Language (Swift 2).
The new/correct way to do this in Swift 2.0 is this:
let fm = NSFileManager.defaultManager()
let path = NSBundle.mainBundle().resourcePath!
do {
let items = try fm.contentsOfDirectoryAtPath(path)
for item in items as! [String] {
if item.hasPrefix("nssl") {
objects.append(item)
}
}
}
catch let error as NSError {
error.description
}
You need to adopt to new error handling techniques added in Swift 2.0. You need to use do..try..catch for fixing this issue (error handling)
Change your code like:
override func viewDidLoad()
{
super.viewDidLoad()
let fm = NSFileManager.defaultManager()
let path = NSBundle.mainBundle().resourcePath!
do
{
let items = try fm.contentsOfDirectoryAtPath(path)
for item in items
{
if item.hasPrefix("nssl")
{
objects.append(item)
}
}
}
catch
{
// Handle error here
}
}
Also you don't need to use items as! [String], because fm.contentsOfDirectoryAtPath returns an array of strings [String]
Refer Swift Error Handling for more details. Also check contentsOfDirectoryAtPath:error: for checking that new method syntax.
My bet is, it's because in Swift 2 error handling changed. The method you are calling no longer takes the second "error" parameter, and instead "throws".
Read the Swift 2 docs.
You need to convert to the new do-try-catch construct. Xcode should have suggested it for you...?

Swift 2 migration saveContext() in appDelegate

I have just downloaded the new Xcode 7.0 beta and did a migration from Swift 1.2 to Swift 2. The migration apparently did not change the whole code, in fact a method saveContext() which was fine until throws 2 errors for the line:
if moc.hasChanges && !moc.save() {
Binary operator '&&' cannot be applied to two Bool operands
and
Call can throw, but it is not marked with 'try' and the error is not handled
The method looks like this:
// MARK: - Core Data Saving support
func saveContext () {
if let moc = self.managedObjectContext {
var error: NSError? = nil
if moc.hasChanges && !moc.save() {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(error), \(error!.userInfo)")
abort()
}
}
}
Any ideas on how to get it working?
The first of the two errors you provided is misleading, but the second is spot on. The problem is in !moc.save() which as of Swift 2, no longer returns Bool and is instead annotated throws. This means that you you have to try this method and catch any exceptions that it may emit, instead of just checking wether its return value is true or false.
To reflect this, a new project created in Xcode 7 using Core Data will produce the following boilerplate code which can replace the code you're using.
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
}
}
The answer by 0x7fffffff is correct, but to improve upon Apple's boilerplate code, you can catch the specific error in the catch block using catch let error as NSError like so:
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch let error as NSError {
NSLog("Unresolved error \(error), \(error.userInfo)")
// Handle Error
}
}
}
The best practice is to use the var error witch will still be available if you just use it this way :
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
NSLog("Unresolved error \(error), \(error.userInfo)")
// Handle Error
}
}
}
In the same way, if you are sure that managedObjectContext.save() will not throw an exception, the code is slimmed down to:
func saveContext () {
if managedObjectContext.hasChanges {
try! managedObjectContext.save()
}
}
And to extrapolate on why managedObjectContext is not optional in the Swift 2 code, it is because the NSManagedObject(concurrencyType:) is an initializer that does not fail. In Xcode 6, the boilerplate code returned an optional context if the NSPersistentStoreCoordinator is nil, but you can handle this easily by checking.
lazy var managedObjectContext: NSManagedObjectContext = {
let coordinator = self.persistentStoreCoordinator
var moc = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
moc.persistentStoreCoordinator = coordinator
return moc
}()