How do I tell which guard statement failed?
Erica Sadun just wrote a good blog post on this exact topic.
Her solution was to hi-jack the where
clause and use it to keep track of which guard statements pass. Each successful guard condition using the diagnose
method will print the file name and the line number to the console. The guard condition following the last diagnose
print statement is the one that failed. The solution looked like this:
func diagnose(file: String = #file, line: Int = #line) -> Bool {
print("Testing \(file):\(line)")
return true
}
// ...
let dictionary: [String : AnyObject] = [
"one" : "one"
"two" : "two"
"three" : 3
]
guard
// This line will print the file and line number
let one = dictionary["one"] as? String where diagnose(),
// This line will print the file and line number
let two = dictionary["two"] as? String where diagnose(),
// This line will NOT be printed. So it is the one that failed.
let three = dictionary["three"] as? String where diagnose()
else {
// ...
}
Erica's write-up on this topic can be found here
Guard statement in swift for error handling
You could use the where
clause to check for non-nil and non-empty
guard let num1 = num1Input.text where !num1.isEmpty else {
show("No input in first box")
return
}
How can I make sure my guard statement doesn't proceed with a nil value?
Your code is correct and probably never will enter the guard clause. Here you are setting values of variables base on, I think, UIText fields. This fields when empty are not null. It's important to understand that an empty string is not the same thing as a null value. An empty string, represented as "" is still a string. A null value is, like JavaScript coders like to talk: has an undefined type. Your guard clause will fail only when you try to set a non nil property to nil value.
To solve this, you just change your guard clause to something like:
guard emailField.text.isEmpty == false, passwordField.text.isEmpty == false
Or
guard !emailField.text.isEmpty, !passwordField.text.isEmpty
Guard statement in Swift has error if using return
The error is saying that your guard
statement is within a func()
that has an expected return value of some type
For example in the greet()
function a String
is returned… so the guard
statement must return a String
value. The type
of the value you have to return from your guard
statement will depend on the function that contains it.
func greet(person: String, day: String) -> String {
guard person != "Homer" else {
return "Sorry, no Homer's allowed"
}
return "Hello \(person), have a great \(day)"
}
greet(person: "Homer", day: "Monday")
greet(person: "Douglas", day: "Thursday")
If the String
"Sorry, no Homer's allowed" isn't returned in the example greet()
function you will see the same problem:
A guard
statement simply protects against an unusable state for the function it's enclosed in. So the return
statement of a guard
is just a form of early return
for the function, as such, it must return the same type as the functions definition.
In the greet()
function above the definition specifies that a String
is returned (-> String
) , so the return
inside the guard
's else block must also return a String
.
How to use guard statement to detect nil after an assignment?
The other answers show you how to solve your issue, but doesn't really explain why this error occurs, so I thought I'd pitch in on that.
The guard let ... else
statement, much like if let ...
, attempts to bind the unwrapped value of an optional---generally as long as this is not nil
---to a non-optional immutable of the same underlying type; using optional binding
var a: Int? = 5
if let b = a {
// b unwrapped, type inferred to non-optional type Int
print(b) // "5"
}
The above binding would fail if a
had the value nil
, as b
, as per default (by type inference), is of type Int
which cannot hold nil
.
In this context, it makes no sense to explicitly declare b
to be an implicitly unwrapped optional, as this will allow a successful binding even if a
is nil. An equivalently non-sense block would be to explicitly declare b
to be an optional, whereafter the "attempted optional binding" of optional a
(Int?
) to optional b
(Int?
) would naturally always succeed, and the if let ...
block reduces to a completely redundant block-local assignment.
a = nil
if let b: Int! = a {
print(b) // "nil"
// wups, we managed to bind a to b even though a is nil ...
// note also this peculiarity
print(b.dynamicType) // Optional<Int>
let c: Int! = nil
print(c.dynamicType) // ImplicitlyUnwrappedOptional<Int>
}
if let b: Int? = a {
print(b) // nil
// wups, we managed to bind a to b even though a is nil ...
}
Note the peculiarity that no matter if we explicitly specify b
to be of type Int?
(optional) or type Int!
(implicitly unwrapped optional), the binded immutable b
passing into the if let
block is, for both cases, just a regular optional (of type Int?
). This explains why you need to unwrap event
(event!.iconImgData
) after the guard let
clause, even though we declared it to be of an implicitly unwrapped type.
Hence, in your example, your guard let ... else
statement will not catch cases where eventsImagesLoading.removeValueForKey(location)
is nil
, as the binding to event
(which is of implicitly unwrapped optional type Event!
) will success even for nil
cases.
func foo() {
let bar : Int? = nil
guard let _: Int! = bar else {
print("this is surely nil")
return
}
print("but an implicitly unwrapped optional will fall through")
}
foo() // "but an implicitly unwrapped optional will fall through"
You should generally only use implicitly unwrapped optionals for immutables where you need delayed initialization (value nil
until initialized). After initialization of an implicitly unwrapped optional, its value should never be nil
(whereas in the example above, it's value is, after initialization by optional binding, just that; nil
).
Also, you should generally let the compiler infer the non-optional type of the immutable to which you are trying to bind in the guard let INFER_THIS = ... else
or if let INFER_THIS = ...
clauses.
We could ponder over whether it should even be allowed to use optional binding to optional types (guaranteed success), but that is another discussion.
Force error in guard statement?
An NSError
object encapsulates information about an error condition in an extendable, object-oriented manner. It consists of a predefined error domain, a domain-specific error code, and a user info dictionary containing application-specific information. more check Apple Doc
guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else {
print("Your request returned a status code other than 2xx!")
//How can I force an error?
let error = NSError(domain: "statusCode",
code: (response as? HTTPURLResponse)?.statusCode,
userInfo: [NSLocalizedDescriptionKey: "Your request returned a status code other than 2xx!"])
self.delegate.errorWeather(error: error )
return
}
Related Topics
Programmatically Disabling Screenshot in App
Why Swift Closure Not Capture Self
Lazy Property Initialization in Swift
Add Skreferencenode/Skscene to Another Skscene in Spritekit
Fatal Error: Array Index Out of Range in Swift Xcode6
How to Request a Desktop Version of a Webpage Using Uiwebview in Swift 3.0
Declaring and Using Custom Attributes in Swift
Swift 2: !, ? -" Value of Optional Type "..." Not Unwrapped"
How to Get Iobluetoothdevice's Battery Level, Using Swift and Appkit (Xcode for MACos)
Binding in a Foreach in Swiftui
What Is the Type of the Logical Operators
Subtle Cast Warning When Using SQLite.Swift ... Binding? to Any
In Swift,There's No Way to Get the Returned Function's Argument Names