I am trying to learn Swift, and wondered about Apple's expected answer to the experiment below which is provided in the Error Handling section of Apple's GuidedTour playground as follows;
Experiment: Add code to throw an error inside the do block. What kind of error do you need to throw so that the error is handled by the first catch block? What about the second and third blocks?
enum PrinterError: ErrorType {
case OutOfPaper
case NoToner
case OnFire
}
func sendToPrinter(printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.NoToner
}
return "Job sent"
}
do {
let printerResponse = try sendToPrinter("Gutenberg")
print(printerResponse)
} catch PrinterError.OnFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
Below are my answers to the questions asked in this experiment, and they all seem to work...
What kind of error do you need to throw so that the error is handled by the first catch block?
By adding throw PrinterError.OnFire as below;
do {
throw PrinterError.OnFire
let printerResponse = try sendToPrinter("Gutenberg")
print(printerResponse)
} catch PrinterError.OnFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
What about the second...
By sending "Never Has Toner" to sendToPrinter.
do {
let printerResponse = try sendToPrinter("Never Has Toner")
print(printerResponse)
} catch PrinterError.OnFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
...and third blocks?
The only way I can trigger the third block -and the expected answer in my opinion in this case- is to create a different enum conforming to ErrorType and to throw it, as below;
enum anotherEnum: ErrorType {
case someCase
}
do {
throw anotherEnum.someCase
let printerResponse = try sendToPrinter("Gutenberg")
print(printerResponse)
} catch PrinterError.OnFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
However, as the first sentence for this experiment starts with "Add code to throw an error inside the do block.", I wondered if there was another expected answer that I was supposed to find out while staying in the do block.
I'm asking because I don't want to miss something else that I was expected to find out. Do you think these answers are valid from Apple's perspective?
Thank you!
There are four ways to handle errors in Swift. You can propagate the error from a function to the code that calls that function, handle the error using a do - catch statement, handle the error as an optional value, or assert that the error will not occur.
The try/catch syntax was added in Swift 2.0 to make exception handling clearer and safer. It's made up of three parts: do starts a block of code that might fail, catch is where execution gets transferred if any errors occur, and any function calls that might fail need to be called using try .
Swift provides a default initializer for any structure or class that provides default values for all of its properties and doesn't provide at least one initializer itself. The default initializer simply creates a new instance with all of its properties set to their default values.
Information about an error condition including a domain, a domain-specific error code, and application-specific information.
A more practical example of that third scenario might be something like checking to see if the network was available before even bothering to try sending something to the network printer:
enum NetworkError: ErrorType {
case Unavailable
}
enum PrinterError: ErrorType {
case OutOfPaper
case NoToner
case OnFire
}
func sendToPrinter(printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.NoToner
}
return "Job sent"
}
var networkAvailable = false // some other process will update this when the network is available
Then you could do:
do {
if !networkAvailable {
throw NetworkError.Unavailable
}
let printerResponse = try sendToPrinter("Gutenberg")
print(printerResponse)
} catch PrinterError.OnFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
This will result in the third catch
block being caught.
Or a more realistic, Cocoa example might be:
do {
let documents = try NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
let fileURL = documents.URLByAppendingPathComponent("document.txt")
let contents = try String(contentsOfURL: fileURL)
let printerResponse = try sendToPrinter(contents)
print(printerResponse)
} catch PrinterError.OnFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
Note, this isn't actually manually throwing an error, but relying upon errors thrown by URLForDirectory
and String(contentsOfURL:)
. But if the document.txt
was not found in the Documents
folder, String(contentsOfURL:)
would throw an error which would be caught by your third catch
block.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With