Optional enum initializer not working with where clause - swift

I'm having trouble composing a guard with a where clause and want to verify whether I'm doing this correctly or if the compiler is being buggy.
I have this enum:
enum Command: String {
case Init
case Update
}
And then this guard statement
let cmdStr = "Init"
guard let command = Command(rawValue: cmdStr) where command != nil else {
print("invalid command: \(cmdStr)") // Error: Value of type Command can never be nil, comparison isn't allowed
return nil
}
The error I get is strange, because the rawValue initializer is an optional initializer. Introspecting command shows that it is type Command though, even though the initializer results in an optional.
However, if I do this outside the guard statement first and rewrite like this:
let cmdStr = "Init"
let cmd = Command(rawValue: cmdStr)
guard cmd != nil else {
print("invalid command: \(cmdStr)")
return nil
}
It works and introspection of cmd shows the expected type of Command?
Does anyone know why this is happening? Or is this a compiler bug I should be submitting?

Please, read Apple Documentation about guard statement:
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID525
In your case there should be
let cmdStr = "Init"
guard let command = Command(rawValue: cmdStr) else {
print("invalid command: \(cmdStr)") // Error: Value of type Command can never be nil, comparison isn't allowed
return nil
}

Related

Swift 4 - Catch Error if Value not in Plist

I have this code
let path = self.userDesktopDirectory + "/Library/Preferences/.GlobalPreferences.plist"
let dictRoot = NSDictionary(contentsOfFile: path)
if let dict = dictRoot{
try print(dict["AppleLocale"] as! String)
}
If the Value "AppleLocale" didnt exists the script crashes. What I must add to "catch" the Error and avoid the crash?
If the Value "AppleLocale" didnt exists the script crashes. What I
must add to "catch" the Error and avoid the crash?
depends on what's the reason for causing the crash. Mentioning that "If the Value AppleLocale didnt exists" means the the reason for the crash would be the force casting:
dict["AppleLocale"] as! String
probably, it has nothing to do with the try, it would be:
Unexpectedly found nil while unwrapping an Optional value
Means that at some point dict["AppleLocale"] could be nil or even if it contains a value as not a string it will crash (optional). You have to make sure that dict["AppleLocale"] is a valid (not nil) string, there are more than just one approach to follow for doing it, for instance you could do optional binding, like this:
let path = self.userDesktopDirectory + "/Library/Preferences/.GlobalPreferences.plist"
let dictRoot = NSDictionary(contentsOfFile: path)
if let dict = dictRoot{
if let appleLocale = dict["AppleLocale"] as? String {
print(appleLocale)
} else {
// `dict["AppleLocale"]` is nil OR contains not string value
}
}
Actually, I would assume that you don't have to deal with try for such a case.

Trying to do deal with errors and optionals the right way

I am attempting to use SwiftSoup to scrape some HTML. This example, based on the SwiftSoup github documentation, works fine…
func scrape() throws {
do {
let htmlFromSomeSource = "<html><body><p class="nerp">HerpDerp</p><p class="narf">HoopDoop</p>"
let doc = try! SwiftSoup.parse(htmlFromSomeSource)
let tag = try! doc.select("p").first()!
let tagClass = try! tag.attr("class")
} catch {
print("oh dang")
throw Abort(.notFound)
}
print(tagClass)
}
… Up until I mess with the selector or attribute targets, at which point everything crashes thanks to the implicitly unwrapped optionals (which I assume was just quick-and-dirty code to get smarter people started). That do/catch doesn't seem to help at all.
So what's the Right way? This compiles...
print("is there a doc?")
guard let doc = try? SwiftSoup.parse(response.body.description) else {
print("no doc")
throw Abort(.notFound)
}
print("should halt because there's no img")
guard let tag = try? doc.select("img").first()! else {
print("no paragraph tag")
throw Abort(.notFound)
}
print("should halt because there's no src")
guard let tagClass = try? tag.attr("src") else {
print("no src")
throw Abort(.notFound)
}
... but again if I mess with the selector or attribute it crashes out, "Unexpectedly found nil while unwrapping an Optional value" (after "is there a doc?"). I thought guard would halt the process when it encountered a nil? (If I convert "try?" to "try" the compiler complains that "initializer for conditional binding must have Optional type"…)
If you declare the function as throws you don't need a do - catch block inside the function. Just remove the block and the exclamation marks after try to pass through the errors to the caller function.
func scrape() throws { // add a return type
let htmlFromSomeSource = "<html><body><p class="nerp">HerpDerp</p><p class="narf">HoopDoop</p>"
let doc = try SwiftSoup.parse(htmlFromSomeSource)
guard let tag = try doc.select("p").first() else { throw Abort(.notFound) }
let tagClass = try tag.attr("class")
// return something
}

Should we do nil check for non-optional variables?

I have a function (abc) as follows and I should throw an error when the arguments passed are empty or nil, should I check for nil too or only empty is enough?
public func abc(forURL serviceUrl:String,serviceID:String, error:inout Error? )throws ->[AnyHashable : Any]{
guard serviceUrl != nil, !serviceUrl.isEmpty else {
let argError:Error = MapError.emptyArgumentUrl.error()
error = argError
throw argError
}
guard !serviceID.isEmpty else {
let argError:Error = MapError.emptyArgumentServiceId.error()
error = argError
throw argError
}
serviceID is not an optional.
That means it can't be nil.
So no, there is no need for that check.

How to check conditions of two parameters in my case

Say I have a closure with two optional parameters:
(data: MyData?, error: Error?) in
// I want to safely unwrap data & make sure error is nil
if let data = data, let error== nil {
}
The if condition above gives me error: Variable binding in a condition requires an initializer.
I understand I might have used wrong syntax with the let error==nil part. But, what is the correct way to do the condition check ?
For Swift 3 just drop let before error
if let data = data, error == nil {
// do stuff
}
Before Swift 3 the syntax was a little different
if let data = data where error == nil {
}
In your case it can be a good idea to use guard :
(data: MyData?, error: Error?) in
// I want to safely unwrap data & make sure error is nil
guard let data = data, error == nil else {
return
}
// code here
Another option is to use if case with pattern matching:
if case let (data?, nil) = (data, error) {
// ...
}
Here data? is the "optional pattern" and a shortcut for .some(data).

Optional try for function with no return value

Could it be that when using try? (optional try) for the call of a throwing function with no return value the errors are just ignored?
func throwingVoidFunction() throws { . . . }
try? throwingVoidFunction()
I expected that the compiler does not allow a try? in front of a throwing function with return type void, but the compiler doesn't complain.
So is using try? in front of a void function a way to absorb errors? (like when using an empty default catch: catch {})
There is no reason for the compiler to complain. The return type
of
func throwingVoidFunction() throws { ... }
is Void and therefore the type of the expression
try? throwingVoidFunction()
is Optional<Void>, and its value is nil (== Optional<Void>.none) if an error was thrown while evaluating the expression,
and Optional<Void>.some() otherwise.
You can ignore the return value or test it against nil. An
example is given in An elegant way to ignore any errors thrown by a method:
let fileURL = URL(fileURLWithPath: "/path/to/file")
let fm = FileManager.default
try? fm.removeItem(at: fileURL)