How do I tell which guard statement failed?

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? {
    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.

    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")
    // ...

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

    // 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