How to avoid returning an optional after throwing an exception? - swift

I'm writing a utility function which takes a parameter and always returns a valid non-nil result (notice the returned value is not optional because the possible parameters are all actually hardcoded and valid, so I know the function cannot fail):
func myFunction(param: String) -> NonTrivialObject {...}
Now, during development I want to experiment with possible parameters and, should I make a mistake, I want the function to throw an exception and just crash. I don't want or need to throw Swift errors or catch them, I want to hard-crash and fix the parameter immediately. In Objective C I would just use NSParameterAssert() or do something along these lines:
guard let validatedParam = param where param != nil else {
NSException(...).raise()
return nil
}
// do the actual work and return a non-optional result
However, I cannot return nil because the result is not an optional. Is there a way to somehow tell the compiler that it doesn't need to bother returning anything from the function after an exception is thrown? Or am I doomed to litter my code with unwrapping optionals or try! statements or to return a dummy object just to make the compiler pleased?

You can use Swift assert(_:_file:line:) function as follows
assert(some condition, "Message to display if condition is false (optional)" )
If the condition is verified the app will continue running, otherwise it will terminate

An optional may contain nil, but Swift syntax forces you to safely deal with it using the ? syntax to indicate to the compiler you understand the behavior and will handle it safely.

You can define the function to be a throwing function:
func foo(param: String) throws -> NonTrivialObject {
guard param != nil else {
throw SomeErrorEnum.NilFound
}
doStuff(...)
}
The error enum needs to conform to the ErrorType protocol. The function call is now able to catch errors like this:
do {
try foo(param)
} catch SomeErrorEnum.NilFound {
print("Found nil")
}
In this way the function returns a non-optional but can also throw errors. For more information see: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html

Related

Swift: Error ignores try statement

In my code I'm deserialising some XML using the SWXMLHash library. Its .value() method has the throws keyword in its declaration, as do any custom deserialise functions.
I have the following line of code:
let myValue : UInt8 = try? xml["Root"]["ValueNode"].value()
Since the library doesn't include a deserialiser for UInt8, I've defined my own:
extension UInt8: XMLElementDeserializable {
public static func deserialize(_ element: XMLElement) throws -> UInt8 {
return UInt8(element.text)!
}
}
This works when the node has a value. However, when the node doesn't exist or is nil, an error occurs on the following line:
return UInt8(element.text)! // Fatal error: Unexpectedly found nil while unwrapping an Optional value
This is supposed to happen, obviously. What I don't understand is why this error is not being caught by my try? statement and returning nil instead of throwing that error.
Can anyone help?
Not all errors can be caught in Swift. If you mark a function using the throws keyword it indicates that the function might throw a recoverable error. However, your custom implementation doesn't actually throw any errors. Only errors thrown from functions marked with the throws keyword and thrown using the code throw Error can be caught by a do-catch block.
try? is a way to convert a throwable function's return value to an optional. If the function would throw an error, the value after the try? will be nil, otherwise it will be an optional value.
When you use the !, you specifically tell the compiler that you know what you are doing and if the operation on which you used the ! fails, your app shouldn't fail gracefully.
You'll need to change your deserialize method to handle the optional unwrapping gracefully or throw and error.
extension UInt8: XMLElementDeserializable {
public static func deserialize(_ element: XMLElement) throws -> UInt8 {
if let val = UInt8(element.text) {
return val
} else {
throw NSError(domain: "Couldn't deserialize value to UInt8", code: 1)
}
}
}
return UInt8(element.text)!
There's no try in this line. Therefore, no errors are going to be thrown here. If UInt8 can't convert the string it's given, it just returns nil. And then, of course, your ! turns that nil into a crash.
Instead of that, do something like this instead:
guard let retVal = UInt8(element.text) else { throw SomeError }
return retVal
In general: When there's a way to do something using !, and another way to do the same thing without using !, go for the second one unless you've got a really good reason.

if statements and optionals in Swift

Is there a difference between something like
if let error = error {
print(error.localizedDescription)
}
and just checking if it is = nil
if error != nil {
print(error.localizedDescription)
}
if I want to check if error has a value? Think of firebase's creating user function.
Yes.
The if let statement allows you to bind the value of error to a variable if it is non-nil and use it in the block. If maybeError is of type Error?, when you do:
if let error = maybeError {
/* block contents */
}
the type of error will be Error within the block - ie: It won't be an optional anymore. If you just do a nil-check using if, error will still be of type Error? within the block. So the code that would actually be equivalent to your first snippet would be:
if error != nil {
print(error!.localizedDescription)
}
(Your second snippet, as it is, won't compile, as you're trying to get the localizedDescription variable of an Error? object, which has no such property)
By the way, in case you haven't seen it before, the !. thing is the unwrap operator. It runs the method on the object if the object is non-nil, but it crashes in case the object is nil. In this case, you generally know it won't crash. (But this might not actually be safe depending on where and how you use it - check #rmaddy's comment)
In the first, error is now a non-optional type, so you can use .. This is also idiomatic -- it more clearly shows what you are trying to do.
In the second, you would need to use ?. to look at error's properties (your code won't compile because you haven't done that).

Can I restrict the type that a function throws in Swift?

When calling a function in Swift 3 that throws, you have to be exhaustive in catching all possible errors, which often means you have an unnecessary extra catch {} at the end to handle errors that won't happen.
Is it possible to say throws MyErrorType so that the compiler can know you have indeed been exhaustive when you handle all cases from that enumeration?
There's no simple way to be type-safe with thrown errors. Consider this, if the compiler allowed you to specify throws MyErrorType, then it would also have to ensure within that function body that you're not trying a function that could potentially throw a different type outside of a do/catch block. (Well there is but it would add layers of unnecessary complexity). The Swift compiler can already be slow and get stuck in loops when inferring types, inferring Thrown types all the way up a chain of throwing functions could be a nightmare.
The running idea is that for most errors you're going to handle them in a small subset of ways anyhow.
That being said, there's no need for you to add extra catch let error as MyErrorType clauses, you can simply use a switch in a catch block like so:
do {
try something()
} catch let e {
switch e {
case let m as MyErrorType: handleMyError(m)
case let o as OtherErrorType: handleOther(o)
case is ThirdErrorType: print("error \(e)")
default: handleElse(e)
}
}
My suggestion for this problem is instead of throwing an error return a Result type in your function. It would be something like this.
enum MyCustomError: Error {
case genericError
}
func operationThatFails() -> Result<Response, MyCustomError> {
guard requiredConsition() else {
return .failure(.genericError)
}
return Response()
}
Then you can handle the error like this:
let result = operationThatFails()
switch result {
case .success(let value):
// handle success
case .failure(let error):
// handle error
}
This way your error is always type safe

Error message: initialiser for conditional binding must have optional type, not '()'

I am getting error:
initialiser for conditional binding must have optional type, not '()'.
Using Swift language, below is the code:
if let result = brain.performOperation(operation)
I think I can answer your question, but I don't know how much it will help you.
The way if let someVar = someOptional { } is used is to check the value of a variable (someOptional) to see if it is nil or a "value". If it is not nil then someVar will be assigned the non-nil value of someOptional and execute the code between { } which can safely reference someVar, knowing it is not nil. If someOptional is nil then the code within { } is bypassed and not executed.
The comment you posted above indicates that the performOperation() method is this:
func performOperation(symbol:String) {
if let operation = knownOps[symbol] {
opStack.append(operation)
}
}
This method does not return anything, or more formally it returns void aka (). void, or () is not a value, nor is it nil.
So when you have this statement
if let result = brain.performOperation(operation) { }
the compiler complains because it expects brain.performOperation(operation)
to return either nil or a value, but not void aka () which is exactly what the method returns.
If you are still confused about optionals, be sure to read as much as you can of the Swift Language Reference. Optionals are a big part of the language and once you get used to them you will find them very invaluable.

How do I handle runtime error in swift?

I do not know how do I handle runtime error in swift. I need to check for a parsing error and do something about it. Can anyone help me please?
I have code something like this:
var:SomeObject = parse("some string")
I need to handle any generic error that occurs in runtime.
Thanks!
If the function is yours, then in case of a failure, you can make that function return a nil value.
That way your line of code
var anObj:SomeObject = parse("some string")
would become something like this
var:SomeObject? = parse("some string")
Notice the ? sign. It means that the value in it is Optional. In simple words, it could be some actual value, or it could be nil.
After this function, you should perform a check like
If anObj != nil
{
//do something
}
else
{
//the parse didn't go right, handle the erorr here.
}
Swift 2 adds additional safety to your error checking. You use the throws keyword to specify which functions and methods could throw an error. Then you have the do, try, and catch keywords for when you call something that could throw:
// 1
enum ParseError: ErrorType {
case InvalidValue
}
// 2
func parseWithError(value:String) throws {
if value.count > 0 {
// yeaaa!
} else {
// 3
throw ParseError.InvalidValue
}
}
func parse(value:String) {
// 4
do {
try parseWithError(value)
} catch {
print("Could not parse! :[")
return
}
}
There are a few things to highlight here:
To create an error to throw, simply create an enum that derives from ErrorType.
You need to use the throws keyword to mark any function that can throw an error.
This throws an error, which will be caught in section 4.
Instead of try blocks, which might be familiar from other languages, you wrap any code that can throw an error in a do block. Then, you add the try keyword to each function call that could throw an error.
For more read here or this is the official documentation of Error Handling