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 thewhere
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
What Is The Advantage of Closure Stored Property Initialisation
Parsing JSON from Url Ends Up with an Error - Swift 5
Flattened Objects in JSON File to Nested Object Structure in Swift
Testing a Class Which Preserves Its State in Private Variables
Swift: Get The Compile Time Name of Variable (Referencing to a Class)
Getting The Parameterised Type of a Generic in Swift
Bar Button Item Tint Color Not Working
Calculating Distance in Vapor4
How to Set a Custom Annotations for All Points Except for User Location
How to Delay a Return-Statement in Swift
Swift: Trunc a Floating Number to Show It in a Label
List Is Not Conforming to Encodable
How to Display an Alert Controller When Nsdate == to a Time (For Example 12:00 Am) in Swift