Swift: When to use guard vs if
One thing I’ve noticed recently in my code-base is that I tend to default to guard vs if. In fact, whenever I write an if statement, I facepalm myself and change it to a guard without thinking much.
But that’s become a problem. There is in fact a difference between guard and if and thought does need to be put into which one to use.
The difference is a bit subtle, but it is there. guard should be used when certain values are expected to be present for the function to execute as intended.
For example, in the try! Swift app, when it displays a presentation session type, the presentation title is the session title.
However, not every session has a presentation, so the presentation is optional. However, for this specific session type, it is expected that a presentation is in fact present and that it has a title. This is a perfect use-case for guard!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
@objc public enum SessionType: Int { case workshop case meetup case breakfast case announcement case talk case lightningTalk case sponsoredDemo case coffeeBreak case lunch case officeHours case party } public class Session: Object { // this is optional because not all sessions have presentations // e.g. no presentation during breakfast open dynamic var presentation: Presentation? // other properties here /** The main name of this session */ public var formattedTitle: String { switch self.type { case .talk, .lightningTalk: // for the talk / lighting talk session type // we expect the presentation to be there // if it's not there, it's a fail, so `guard` is used guard let presentation = presentation else { return defaultTitle } return presentation.localizedTitle // other cases continued... } } |
The talk title should always be present for a presentation session type. If it’s not there, it’s a fail. This is why we use guard in this case.
However, consider another case. A Coffee Break session could be sponsored. In that case, the title of the coffee break should include the sponsor name. Both are correct – if there is a sponsor, we include the name of the sponsor, but if there isn’t one, we don’t include it. This is the type of case where if should be used:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class Session: Object { /** A sponsor, if any, responsible for this session. */ open dynamic var sponsor: Sponsor? /** The main name of this session */ public var formattedTitle: String { switch self.type { case .coffeeBreak: // some sessions are sponsored, some aren't // it's not a fail if there is no sponsor // so `if` is used if let sponsor = sponsor { return "Coffee Break, by \(sponsor.name)".localized() } return "Coffee Break".localized() // other cases continued... } } |
So as @ecerney puts it so well, think of guard as a lightweight Assert:
Like an if statement, guard executes statements based on a Boolean value of an expression. Unlike an if statement, guard statements only run if the conditions are not met. You can think of guard more like an Assert, but rather than crashing, you can gracefully exit.
So think if before you guard!