If I’ve got a bunch of chained guard let statements, how can I diagnose which condition failed, short of breaking apart my guard let into multiple statements?
Given this example:
guard let keypath = dictionary["field"] as? String,
let rule = dictionary["rule"] as? String,
let comparator = FormFieldDisplayRuleComparator(rawValue: rule),
let value = dictionary["value"]
else
{
return nil
}
How can I tell which of the 4 let statements was the one that failed and invoked the else block?
The simplest thing I can think of is to break out the statements into 4 sequential guard else statements, but that feels wrong.
guard let keypath = dictionary["field"] as? String
else
{
print("Keypath failed to load.")
self.init()
return nil
}
guard let rule = dictionary["rule"] as? String else
{
print("Rule failed to load.")
self.init()
return nil
}
guard let comparator = FormFieldDisplayRuleComparator(rawValue: rule) else
{
print("Comparator failed to load for rawValue: \(rule)")
self.init()
return nil
}
guard let value = dictionary["value"] else
{
print("Value failed to load.")
self.init()
return nil
}
If I wanted to keep them all in one guard statement, I can think of another option. Checking for nils inside the guard statement might work:
guard let keypath = dictionary["field"] as? String,
let rule = dictionary["rule"] as? String,
let comparator = FormFieldDisplayRuleComparator(rawValue: rule),
let value = dictionary["value"]
else
{
if let keypath = keypath {} else {
print("Keypath failed to load.")
}
// ... Repeat for each let...
return nil
}
I don't even know if that will compile, but then I might as well have used a bunch of if let statements or guards to begin with.
What's the idiomatic Swift way?
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
Normally, a guard statement doesn't let you distinguish which of its conditions wasn't satisfied. Its purpose is that when the program executes past the guard statement, you know all the variables are non-nil. But it doesn't provide any values inside the guard/else body (you just know that the conditions weren't all satisfied).
That said, if all you want to do is print something when one of the steps returns nil, you could make use of the coalescing operator ?? to perform an extra action.
Make a generic function that prints a message and returns nil:
/// Prints a message and returns `nil`. Use this with `??`, e.g.:
///
/// guard let x = optionalValue ?? printAndFail("missing x") else {
/// // ...
/// }
func printAndFail<T>(message: String) -> T? {
print(message)
return nil
}
Then use this function as a "fallback" for each case. Since the ?? operator employs short-circuit evaluation, the right-hand side won't be executed unless the left-hand side has already returned nil.
guard
let keypath = dictionary["field"] as? String ?? printAndFail("missing keypath"),
let rule = dictionary["rule"] as? String ?? printAndFail("missing rule"),
let comparator = FormFieldDisplayRuleComparator(rawValue: rule) ?? printAndFail("missing comparator"),
let value = dictionary["value"] ?? printAndFail("missing value")
else
{
// ...
return
}
Very good question
I wish I had a good answer for that but I have not.
Let's begin
However let's take a look at the problem together. This is a simplified version of your function
func foo(dictionary:[String:AnyObject]) -> AnyObject? {
guard let
a = dictionary["a"] as? String,
b = dictionary[a] as? String,
c = dictionary[b] else {
return nil // I want to know more ☹️ !!
}
return c
}
Inside the else we don't know what did go wrong
First of all inside the else block we do NOT have access to the constants defined in the guard statement. This because the compiler doesn't know which one of the clauses did fail. So it does assume the worst case scenario where the first clause did fail.
Conclusion: we cannot write a "simple" check inside the else statement to understand what did not work.
Writing a complex check inside the else
Of course we could replicate inside the else the logic we put insito the guard statement to find out the clause which did fail but this boilerplate code is very ugly and not easy to maintain.
Beyond nil: throwing errors
So yes, we need to split the guard statement. However if we want a more detailed information about what did go wrong our foo function should no longer return a nil value to signal an error, it should throw an error instead.
So
enum AppError: ErrorType {
case MissingValueForKey(String)
}
func foo(dictionary:[String:AnyObject]) throws -> AnyObject {
guard let a = dictionary["a"] as? String else { throw AppError.MissingValueForKey("a") }
guard let b = dictionary[a] as? String else { throw AppError.MissingValueForKey(a) }
guard let c = dictionary[b] else { throw AppError.MissingValueForKey(b) }
return c
}
I am curious about what the community thinks about this.
One possible (non-idiomatic) workaround: make use of the where clause to track the success of each subsequent optional binding in the guard block
I see nothing wrong with splitting up your guard statements in separate guard blocks, in case you're interested in which guard statement that fails.
Out of a technical perspective, however, one alternative to separate guard blocks is to make use of a where clause (to each optional binding) to increment a counter each time an optional binding is successful. In case a binding fails, the value of the counter can be used to track for which binding this was. E.g.:
func foo(a: Int?, _ b: Int?) {
var i: Int = 1
guard let a = a where (i+=1) is (),
let b = b where (i+=1) is () else {
print("Failed at condition #\(i)")
return
}
}
foo(nil,1) // Failed at condition #1
foo(1,nil) // Failed at condition #2
Above we make use of the fact that the result of an assignment is the empty tuple (), whereas the side effect is the assignment to the lhs of the expression.
If you'd like to avoid introducing the mutable counter i prior the scope of guard clause, you could place the counter and the incrementing of it as a static class member, e.g.
class Foo {
static var i: Int = 1
static func reset() -> Bool { i = 1; return true }
static func success() -> Bool { i += 1; return true }
}
func foo(a: Int?, _ b: Int?) {
guard Foo.reset(),
let a = a where Foo.success(),
let b = b where Foo.success() else {
print("Failed at condition #\(Foo.i)")
return
}
}
foo(nil,1) // Failed at condition #1
foo(1,nil) // Failed at condition #2
Possibly a more natural approach is to propagate the value of the counter by letting the function throw an error:
class Foo { /* as above */ }
enum Bar: ErrorType {
case Baz(Int)
}
func foo(a: Int?, _ b: Int?) throws {
guard Foo.reset(),
let a = a where Foo.success(),
let b = b where Foo.success() else {
throw Bar.Baz(Foo.i)
}
// ...
}
do {
try foo(nil,1) // Baz error: failed at condition #1
// try foo(1,nil) // Baz error: failed at condition #2
} catch Bar.Baz(let num) {
print("Baz error: failed at condition #\(num)")
}
I should probably point out, however, that the above is probably closer to be categorized as a "hacky" construct, rather than an idiomatic one.
The simplest thing I can think of is to break out the statements into 4 sequential guard else statements, but that feels wrong.
In my personal opinion, the Swift way shouldn't require you to check whether the values are nil or not.
However, you could extend Optional to suit your needs:
extension Optional
{
public func testingForNil<T>(#noescape f: (Void -> T)) -> Optional
{
if self == nil
{
f()
}
return self
}
}
Allowing for:
guard let keypath = (dictionary["field"] as? String).testingForNil({ /* or else */ }),
let rule = (dictionary["rule"] as? String).testingForNil({ /* or else */ }),
let comparator = FormFieldDisplayRuleComparator(rawValue: rule).testingForNil({ /* or else */ }),
let value = dictionary["value"].testingForNil({ /* or else */ })
else
{
return nil
}
My two cents:
Since Swift doesn't let me add the where in the guard let, I came up with this solution instead:
func validate<T>(_ input: T?, file: String = #file, line: Int = #line) -> T? {
guard let input = input else {
print("Nil argument at \(file), line: \(line)")
return nil
}
return input
}
class Model {
let id: Int
let name: String
init?(id: Int?, name: String?) {
guard let id = validate(id),
let name = validate(name) else {
return nil
}
self.id = id
self.name = name
}
}
let t = Model(id: 0, name: "ok") // Not nil
let t2 = Model(id: 0, name: nil) // Nil
let t3 = Model(id: nil, name: "ok") // Nil
I think other answers here are better, but another approach is to define functions like this:
func checkAll<T1, T2, T3>(clauses: (T1?, T2?, T3?)) -> (T1, T2, T3)? {
guard let one = clauses.0 else {
print("1st clause is nil")
return nil
}
guard let two = clauses.1 else {
print("2nd clause is nil")
return nil
}
guard let three = clauses.2 else {
print("3rd clause is nil")
return nil
}
return (one, two, three)
}
And then use it like this
let a: Int? = 0
let b: Int? = nil
let c: Int? = 3
guard let (d, e, f) = checkAll((a, b, c)) else {
fatalError()
}
print("a: \(d)")
print("b: \(e)")
print("c: \(f)")
You could extend it to print the file & line number of the guard statement like other answers.
On the plus side, there isn't too much clutter at the call site, and you only get output for the failing cases. But since it uses tuples and you can't write a function that operates on arbitrary tuples, you would have to define a similar method for one parameter, two parameters etc up to some arity. It also breaks the visual relation between the clause and the variable it's being bound to, especially if the unwrapped clauses are long.
This code can be used for all guard and if logic tests like optional, bool and case tests. It prints a line of a logic test which failed.
class GuardLogger {
var lastGoodLine: Int
var lineWithError: Int { lastGoodLine + 1 }
var file: String
var function: String
init(file: String = #file, function: String = #function, line: Int = #line) {
self.lastGoodLine = line
self.file = file
self.function = function
}
func log(line: Int = #line) -> Bool {
lastGoodLine = line
return true
}
func print() {
Swift.print([file, function, String(lineWithError)].joined(separator: " "))
}
}
let testBoolTrue = true
let testBoolFalse = false
let guardLogger = GuardLogger()
guard
testBoolTrue, guardLogger.log(),
let testOptionalBoolTrue = Optional(testBoolTrue), guardLogger.log(),
let selfIsViewController = self as? UIViewController, guardLogger.log(),
testBoolTrue == false, guardLogger.log() // this fails
else {
print(guardLogger.lastGoodLine)
fatalError()
}
Related
Is there a way to combine type matching and unwrapping optional values within a case?
In the following playground code there are three loops:
Loop A is using if let to match the type and unwrap the optional values at the same time.
In Loop B I replaced the if let's with a switch statement. However I'm using where and check against nil, and then force unwrap my values from this point forward.
I'd like to know if there is a way similar to Loop C to unwrap the values so that I can use those new non optional variables the way I do in Loop A.
import Foundation
// Setup
protocol MyObjects {}
struct MyTopic: MyObjects {
let name: String?
}
struct MyQuestion: MyObjects {
let text: String?
let topic: String?
}
let topicOrQuestions: [MyObjects] = [
MyQuestion(text: "questionA", topic: "topicA"),
MyTopic(name: "topicA"),
MyTopic(name: "topicB"),
MyTopic(name: nil)
]
// Loop A:
for topicOrQuestion in topicOrQuestions {
if let name = (topicOrQuestion as? MyTopic)?.name {
print(name)
} else if let text = (topicOrQuestion as? MyQuestion)?.text, let topic = (topicOrQuestion as? MyQuestion)?.topic {
print(text, topic)
} else {
print("Error: wrong type or nil value")
}
}
// Loop B:
for topicOrQuestion in topicOrQuestions {
switch topicOrQuestion {
case let topic as MyTopic where topic.name != nil:
print(topic.name!)
case let question as MyQuestion where question.text != nil && question.topic != nil:
print(question.text!, question.topic!)
default:
print("Error: wrong type or nil value")
}
}
// Loop C (doesn't work):
for topicOrQuestion in topicOrQuestions {
switch topicOrQuestion {
case let name as MyTopic.name: // <-- Is it possible to unwrap the values here directly similar to the if let's in Loop A?
print(name)
case let text as MyQuestion.text, let topic as MyQuestion.topic:
print(text, topic)
default:
print("Error: wrong type or nil value")
}
}
/*
Current output from Loop A and B and expected output from Loop C:
questionA topicA
topicA
topicB
Error: wrong type or nil value
*/
I don't think you will be able to cast and unwrap in a case, but with this extension at least your Loop A would be less verbose:
extension MyObjects {
subscript<T: MyObjects, V>(topicOrQuestion keypath: (T) -> V) -> V? {
get {
guard let root = self as? T else { return nil }
return keypath(root)
}
}
}
you can also write this loop:
for topicOrQuestion in topicOrQuestions {
if let topicName = topicOrQuestion[topicOrQuestion: \MyTopic.name] as? String {
print(topicName)
}
if let questionText = topicOrQuestion[topicOrQuestion: \MyQuestion.text] as? String ,
let questionTopic = topicOrQuestion[topicOrQuestion: \MyQuestion.topic] as? String {
print(questionText, questionTopic)
}
}
// questionA topicA
// topicA
// topicB
I have a function that returns whether an item exists in 1 of 3 CoreData entities, depending on the list: CoreDataList enum argument passed to that function:
static func listContains(word: String, list: CoreDataList) -> Bool {
var reply = false
switch list {
case .itemsSearched:
let request : NSFetchRequest<ItemsSearched> = ItemsSearched.fetchRequest()
case .itemsAdded:
let request : NSFetchRequest<ItemsAdded> = ItemsAdded.fetchRequest()
case .itemsFinished:
let request : NSFetchRequest<ItemsFinished> = ItemsFinished.fetchRequest()
}
do {
request.predicate = NSPredicate(format: "item == %#", word)
let fetchedResults = try context.fetch(request)
if fetchedResults.count == 1 {
reply = true
}
} catch {
print ("fetch task failed", error)
}
return reply
}
The problem here is that let request cannot be initialized before the switch statement because its type will depend on the outcome of the switch.
Therefore I cannot find a way to do this function without repeating the entire do catch block for each case, which will result in repeating the same code three times. The other option is to separate into three different function, but this presents the same code repetition problem.
You can create a generic function for this
static func checkCount<T: NSManagedObject>(word: String, context: NSManagedObjectContext, type: T.Type) -> Bool {
let request = T.fetchRequest()
request.predicate = NSPredicate(format: "item == %#", word)
do {
let fetchedResults = try context.fetch(request)
if fetchedResults.count == 1 {
return true
}
} catch {
print ("fetch task failed", error)
}
return false
}
and then call it from inside the switch
case .itemsSearched:
return checkCount(word: word, context: context, type: ItemsSearched.self)
Note that I added context as a parameter to the function since I didn't know how it is declared, perhaps that parameter isn't needed.
I'd like to loop trough every key of mystruct and print its key and its value for every property.
struct mystruct {
var a = "11215"
var b = "21212"
var c = "39932"
}
func loopthrough {
for (key, value) in mystruct {
print("key: \(key), value: \(value)") // Type mystruct.Type does not conform to protocol 'Sequence'
}
}
But using the few lines from above I always get this error message:
Type mystruct.Type does not conform to protocol 'Sequence'
How can I avoid getting this message?
First of all let's use CamelCase for the struct name
struct MyStruct {
var a = "11215"
var b = "21212"
var c = "39932"
}
Next we need to create a value of type MyStruct
let elm = MyStruct()
Now we can build a Mirror value based on the elm value.
let mirror = Mirror(reflecting: elm)
The Mirror value does allow us to access all the properties of elm, here's how
for child in mirror.children {
print("key: \(child.label), value: \(child.value)")
}
Result:
key: Optional("a"), value: 11215
key: Optional("b"), value: 21212
key: Optional("c"), value: 39932
use following code to get array of all the properties
protocol PropertyLoopable
{
func allProperties() throws -> [String]
}
extension PropertyLoopable {
func allProperties() throws -> [String] {
var result: [String] = []
let mirror = Mirror(reflecting: self)
// Optional check to make sure we're iterating over a struct or class
guard let style = mirror.displayStyle, style == .struct || style == .class else {
throw NSError()
}
for (property,_) in mirror.children {
guard let property = property else {
continue
}
result.append(property)
// result[property] = value
}
return result
}
}
Now just
let allKeys = try self.allProperties()
Don't forgot to implement protocol
Hope it is helpful
You can use runtime introspection (on an instance of your type) combined with value-binding pattern matching to extract the property names and values; the latter used to unwrap the optional label property of the Mirror instance used to represent the sub-structure of your specific instance.
E.g.:
struct MyStruct {
let a = "11215"
let b = "21212"
let c = "39932"
}
// Runtime introspection on an _instance_ of MyStruct
let m = MyStruct()
for case let (label?, value) in Mirror(reflecting: m)
.children.map({ ($0.label, $0.value) }) {
print("label: \(label), value: \(value)")
} /* label: a, value: 11215
label: b, value: 21212
label: c, value: 39932 */
I hope it still helps someone:
This is my version of the protocol for more complicated classes/structs (Objects within Objects within Objects ;-) )
I am sure there is a more elegant functional solution but this was a quick and dirty solution, as I only needed it for a temporary log.
protocol PropertyLoopable {
func allProperties() -> [String: Any]
}
extension PropertyLoopable {
func allProperties() -> [String: Any] {
var result: [String: Any] = [:]
let mirror = Mirror(reflecting: self)
// make sure we're iterating over a struct or class
guard let style = mirror.displayStyle, style == .struct || style == .class else {
print("ERROR: NOT A CLASS OR STRUCT")
return result
}
for (property, value) in mirror.children {
guard let property = property else {
continue
}
// It was a very complicated struct from a JSON with a 4 level deep structure. This is dirty dancing, remove unnecessary "for" loops for simpler structs/classes
// if value from property is not directly a String, we need to keep iterating one level deeper
if value is String {
result.updateValue(value, forKey: property)
} else {
let mirror = Mirror(reflecting: value)
for (property, value) in mirror.children {
guard let property = property else {
continue
}
//let's go for a second level
if value is String {
result.updateValue(value, forKey: property)
} else {
let mirror = Mirror(reflecting: value)
for (property, value) in mirror.children {
guard let property = property else {
continue
}
//3rd level
if value is String {
result.updateValue(value, forKey: property)
} else {
let mirror = Mirror(reflecting: value)
for (property, value) in mirror.children {
guard let property = property else {
continue
}
result.updateValue(value, forKey: property)
}
}
}
}
}
}
}
return result
}
}
In Swift, you can use if let optional binding to unwrap an optional into a constant or variable with the same name:
func test()
{
let a: Int? = 1
if let a = a {
print("a = \(a)")
}
}
For everything inside the if let statement, the optional a is unwrapped into a regular int.
Likewise, I can use a guard statement to achieve a similar effect
func test()
{
let a: Int? = 1
guard let requiredA = a else{
return
}
print("a = \(requiredA)")
}
However, I can't use code like this: guard let a = a else:
func test()
{
let a: Int? = 1
guard let a = a else{
return
}
print("a = \(a)")
}
Why not?
In a guard statement, if the conditional of the guard statement fails, the else clause is executed and you exit the current scope. If the conditional succeeds, a new variable/constant is created from guard statement's closing brace to the end of the current scope.
Why can't I do the same trick of mapping an optional into a variable/constant with the same name for remainder of the current scope?
P.S.: I realize this question isn't a perfect fit for this site. I'm open to suggestions as to where would be a better place for this question.
The reason you can't do this:
func test()
{
let a: Int? = 1
guard let a = a else{
return
}
print("a = \(a)")
}
is because guard creates the new variable in the same scope, thus you have two variables called a in the same scope. One is an Int and the other is an Int?. That is not allowed.
The error that you get Definition conflicts with previous value is exactly the same as if you had done this:
func test()
{
let a: Int? = 1
let a = a!
}
Compare that with:
func test()
{
let a: Int? = 1
if let a = a {
print("a = \(a)")
}
}
In this case, the new variable a which is an Int exists only in the new scope of the if's then clause, so this works.
From the comments:
But I submit to you that the section of code after the closing brace
and to the end of the enclosing scope is actually an inner scope.
I can understand that you would like it to be so, but it isn't. If that were the case, then you could do this, but it too gives an error:
func test()
{
let a: Int? = 1
guard let b = a else{
return
}
print("b = \(b)")
let a = 5 // Definition conflicts with previous value
print("a = \(a)")
}
The beauty of guard is that it doesn't create new scopes and you avoid creating the pyramid of death that results when you repeatedly use if let to unwrap optionals (and in the process create new scopes).
See the follow-up question
When did guard let foo = foo become legal? for more insight on this topic.
I've got an enumeration with a few different cases which are different types, e.g.
enum X {
case AsInt(Int)
case AsDouble(Double)
}
I can switch on these just fine to get the underlying value back out. However, the switch statement is highly annoying with trying to make me execute some code for the other cases that I simply don't care about. For example, right now I have something like
func AsInt(x: X) -> Int? {
switch x {
case AsInt(let num):
return num;
default:
return nil;
}
}
This works but it's pretty tedious always having to reference this method and having to write a new one for each case of each enumeration. What I'm looking for is how to simply attempt to cast a value of type X to one of the cases, like
var object: X = func();
let value = obj as? Int;
if value {
// do shit
}
How can I simply check for a case without having to enumerate all of the cases about which I don't care and execute some non-statement for them?
Bonus points for any solution that can declare value as part of the conditional instead of polluting the scope.
There are actually multiple ways to do it.
Let's do it by extending your enum with a computed property:
enum X {
case asInt(Int)
case asDouble(Double)
var asInt: Int? {
// ... see below
}
}
Solutions with if case
By having let outside:
var asInt: Int? {
if case let .asInt(value) = self {
return value
}
return nil
}
By having let inside:
var asInt: Int? {
if case .asInt(let value) = self {
return value
}
return nil
}
Solutions with guard case
By having let outside:
var asInt: Int? {
guard case let .asInt(value) = self else {
return nil
}
return value
}
By having let inside:
var asInt: Int? {
guard case .asInt(let value) = self else {
return nil
}
return value
}
The last one is my personal favorite syntax of the four solutions.
As of Swift 2 (Xcode 7) this is possible with if/case and
pattern matching:
let x : X = ...
if case let .AsInt(num) = x {
print(num)
}
The scope of num is restricted to the if-statement.