So I was reading a question someone passed by me today, and completely ignoring their question for the moment, my eye was caught by the following pattern:
let firstName: String = "" let lastName: String = "Smith" // Choice 1: Original Recipe func basicGuard() -> Any? { guard !firstName.isEmpty else { print("firstName can't be empty"); return nil } guard !lastName.isEmpty else { print("lastName can't be empty"); return nil } return "something" }
And I was thinking, surely there’s a nicer way to do this. So just brainstorming, I came up with the following two:
/* Debug flag is set in Build Settings > Custom Flags > Other Swift Flags See: http://i.imgur.com/GA7M4T4.jpg */ // Choice 2: Using Infix Operator func debugOnlyPrint(_ items: Any..., separator: String = " ", terminator: String = "\n") { #if debug print(items.map({ "\($0)" }).joined(separator: separator), terminator: terminator) #endif } infix operator ** func **(condition: Bool, message: String) -> Bool { if !condition { debugOnlyPrint(message) } return condition } func infixoperator() -> Any? { guard !firstName.isEmpty ** "firstName cannot be empty", !lastName.isEmpty ** "lastName cannot be empty" else { return nil } return "something" } // Choice 3: Using an enforce function func enforce(_ rule: String, _ condition: @autoclosure () -> Bool) -> Bool { let passing = condition() if !passing { debugOnlyPrint(rule) } return passing } func enforcement() -> Any? { guard enforce("firstName cannot be empty", !firstName.isEmpty), enforce("lastName cannot be empty", !lastName.isEmpty) else { return nil } return "something" }
So let me ask you, of these three, which do you like the best? And why?
Moving on to the question that was actually asked: what’s an easy way to print a return value for debugging? You can use any of a number of diagnostic solutions, I kind of like this one:
func here(_ note: String = "", line: Int = #line) -> Bool { if note.isEmpty { debugOnlyPrint("[line \(line)] ", terminator: "") } else { debugOnlyPrint("[line \(line) \(note)] ", terminator: "") } return true } precedencegroup VeryLowPrecedence { associativity: right lowerThan: AssignmentPrecedence } infix operator ?* : VeryLowPrecedence func ?*(_: Any, item: T) -> T { debugOnlyPrint(item); return item }
To use this, place a call to here()
followed by ?*
in-line before your return item, for example, return here() ?* "something"
. It prints out the line number followed by the return value. Operating at a very low precedence enables the entire return expression to be evaluated before printing.
4 Comments
Hi Erica! I believe this is your response to my Tweet linking to my original question on StackOverflow :).
I really like the “enforce” function. But as I suggested in my post on SO, it would be so cool if the “defer” statement would expose return value! 🙂
Did you continue down to the bottom with `here() *?` ?
Yes very elegant indeed! 🙂
But we still have to problem with multiple exit points from “advanced” methods such as the “fetchUserByFirstName” in my original post.
The cool thing with “defer” is that we know that the code in that scope always will be called disregarding of exit point.
So it is really too bad that the return value is not exposed!
But I will make use of your “here() *?” 🙂
You can throw in result = whatever, return result, and then use defer to print but I find that kind of excessive
You can also do a pass-through operator, which would allow `return result := something` or `return setValue(inout result, something)`. Again ugly.