Swift: Guard Let and Where - The Priority

Swift: guard let and where - the priority

You are right, in your Swift 2 code

guard let check = person.check() where dont else { }

the conditional binding let check = ... is checked first, and only if that succeeds, the boolean condition dont is checked. You can use

guard dont, let check = person.check() else { }

to check the boolean condition first.

This "asymmetry" in the syntax has been removed in Swift 3:
All guard clauses are separated by commas and the
where keyword is not used anymore. So you have

guard let check = person.check(), dont else { }
// or
guard dont, let check = person.check() else { }

The conditions are checked from left to right with short-circuiting,
i.e. if one condition fails then the else-block is executed without
checking the remaining conditions.

swift : shortcut for guard let self = ...?

I know that question is old but I have to admit that I didn't understand very well the in-built Decodable/Decoder system in Swift (especially this notion of "Container", can't figure out what it represents exactly)

Anyway, I made my own decoder which enable to handle this situation in quite the same way as Android does (to decode JSONObject). I made an extension of Dictionary like this :

protocol Decodable {
init(from raw:[String:Any]) throws
}

extension Dictionary where Key == String
{

enum DecodableError : Error {
case unknownKey(key:String)
case keyWrongType(key:String, expectedType:String, actualValue:String)
case nullValueAtKey(key:String)
}

func getSafe<T>(_ key:String, forType t:T.Type) throws -> T
{
if(self[key] != nil)
{
if(self[key] is NSNull) // corresponds to the JSON null value (by experience)
{
throw DecodableError.nullValueAtKey(key:key)
}
else if(self[key] is T) // for raw type
{
return self[key] as! T
}

// try to parse self[key] to provided type if it's decodable
else if(self[key] is [String:Any] && t is Decodable.Type)
{
return try (t as! Decodable.Type).init(from: self[key] as! [String:Any]) as! T
}

throw DecodableError.keyWrongType(key: key,
expectedType: String(describing: T.self),
actualValue: String(describing:self[key]!))

}

throw DecodableError.unknownKey(key:key)

}

func getSafeOpt<T>(_ key:String, forType t:T.Type) throws -> T?
{
if(self[key] != nil)
{
if(self[key] is NSNull)
{
return nil
}
return try getSafe(key, forType: t)

}

throw DecodableError.unknownKey(key:key)
}

}

I use it like that :

struct Position : Decodable {


let latitude:Double
let longitude:Double
let accuracy:Int?
let member:Member

init(from raw:[String:Any]) throws
{

// getSafe throw exception whenever node are json<null> or if node doesn't exist
latitude = try raw.getSafe("lat", forType: Double.self)
longitude = try raw.getSafe("lng", forType: Double.self)

// getSafeOpt returns nil if the JSON value of node acc is null,
// but it still throw an exception if there is no "acc" node
accuracy = try raw.getSafeOpt("acc", forType: Int.self)

// you can use it to decode other objects that implement my Decodable protocol too :
member = try raw.getSafeOpt("member", forType: Member.self)
}
}

do {
try app.position = Position(from: rawDic)
}
catch {
print("Unable to parse Position : \(error) ")
return
}

This does not handle yet the JSON arrays, I'll do it later, or feel free to update my answer if you wish to add a JSON array handling mechanism.

Usage of where in if let assignment in Swift

Example with two conditions

if let x = y, let a = b, a == x && !x.isEmpty {

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

Can I use the range operator in a guard statement in Swift?

As you like:

guard
let statusCode = (response as? HTTPURLResponse)?.statusCode,
(200...299).contains(statusCode) else {return}

or:

guard
let statusCode = (response as? HTTPURLResponse)?.statusCode,
case 200...299 = statusCode else {return}

or:

guard
let statusCode = (response as? HTTPURLResponse)?.statusCode,
200...299 ~= statusCode else {return}

Using guard with a non-optional value assignment

If you throw a case in there, it'll work. So as follows:

guard case let c = parts.count where c > 1 else { return }

Change variables in guard statement in Swift?

This is an inappropriate use of guard. guard exits the current scope, and you don't want to do that; you want to keep on going. So just use good old if. (Also, never compare a Bool to true as a condition; a Bool is a condition.)

if animal.isDefault! {
// it's true, do one thing
} else {
// it's false, do a different thing
}

In your case, you probably just need the if:

var animalName = (String(format: "%03d", self.animal.speciesId!))
if !(animal.isDefault!) {
animalName += "-merg"
}
let gif = UIImage(gifName: animalName)

Swift @IBInspectables with priority choose the order are they executed in

You should write the setters in a way that the order of calls won't matter. This is not only about the order of calls in Interface Builder, this is also about the order when called programatically.

It shouldn't matter whether you call:

view.placeholder = 
view.placeholderColor =

or

view.placeholderColor = 
view.placeholder =

A sample implementation:

@IBInspectable
var placeholder: String? {
didSet {
updatePlaceholder()
}
}

@IBInspectable
var placeholderColor: UIColor? {
didSet {
updatePlaceholder()
}
}

private func updatePlaceholder() {
textField.attributedPlaceholder = NSAttributedString(
string: placeholder ?? "",
attributes: [.foregroundColor: placeholderColor ?? UIColor.red]
)
}

Unwrapping optionals to a pre-defined variable in the guard condition without creating a new constant

I would just test for nil and then force unwrap when I know it's not:

var someString: String? = "hello"
let nonOptionalString: String // note, you don't have to initialize this with some bogus value

guard someString != nil else { return }
nonOptionalString = someString!

Or if someString was a parameter to some method or closure, you can unwrap in the guard statement using the same variable name, simplifying life even more:

func foo(someString: String?) {
guard let someString = someString else { return }

// now I can just use local `someString`, which is not optional anymore
}

If you're desperate to unwrap and exit-if-nil in a single statement, you could theoretically write a function to unwrap if it can or throw an error if it can't:

extension Optional {
enum OptionalError: Error {
case unwrapFailed
}

func unwrap<T>() throws -> T {
if self == nil { throw OptionalError.unwrapFailed }
return self as! T
}
}

Then you can do:

do {
firstNonOptional = try firstOptional.unwrap()
secondNonOptional = try secondOptional.unwrap()
thirdNonOptional = try thirdOptional.unwrap()
} catch {
return
}

I think that's horrible overkill, but if you're desperate to distill it down to one line per unwrap, that's one way to do it.



Related Topics



Leave a reply



Submit