52

is there is a way to negate the "if let" in swift? This looks silly to me:

    if let type = json.type  {

    } else {
        XCTFail("There is no type in the root element")
    }

I can't use XCTAssertNotNil, because json.type is a enum.

enum JSONDataTypes {
    case Object
    case Array
    case Number
    case String
}

Thanks a lot

EDIT: it is a:

var type: JSONDataTypes? = nil
Peter Shaw
  • 1,867
  • 1
  • 19
  • 32

3 Answers3

44

Swift 2.0 (Xcode 7) and later have the new guard statement, which sort of works like an "if not let" -- you can conditionally bind a variable in the remainder of the enclosing scope, keeping the "good path" in your code the least-indented.

guard let type = json.type else {
    XCTFail("There is no type in the root element")
}
// do something with `type` here

The catch to this is that the else clause of a guard must exit that scope (because otherwise you'd fall into code after that clause, where the guarded variables, like type above, are unbound). So it has to end with something like return, break, continue or a function that is known to the compiler to never return (i.e. annotated @noreturn, like abort()... I don't recall offhand if that includes XCTFail, but it should (file a bug if it's not).

For details, see Early Exit in The Swift Programming Language.


As for really-old stuff... There's no negated form of if-let in Swift 1.x. But since you're working with XCTest anyway, you can just make testing the optional part of an assertion expression:

XCTAssert(json.type != nil, "There is no type in the root element")
rickster
  • 124,678
  • 26
  • 272
  • 326
  • There's literally no "if not let" in Swift 2, but the new `guard` statement is pretty much what you're looking for. Updated answer. – rickster Jun 08 '15 at 21:14
  • 13
    `guard let else {}` is not the equivalent of `if not let {}` since in the scope of `guard` must always call `return` or `break`. They can not be allowed to fall through: `'guard' body may not fall through, consider using 'return' or 'break' to exit the scope` – richardjsimkins Jan 12 '17 at 12:11
24

Here's how you do it:

if json.type == nil {
  // fail
}
Abhi Beckert
  • 32,787
  • 12
  • 83
  • 110
  • 1
    That doesn't assign json.type to type though – Choppin Broccoli Dec 10 '14 at 23:30
  • Of course, because `type` is not an optional and therefore cannot hold a nil value. Your answer doesn't assign anything to type either. It's impossible. – Abhi Beckert Dec 10 '14 at 23:31
  • 3
    @ChoppinBroccoli The original question implies that Peter Shaw doesn't care about the actual type. He's writing a test case that fails if `json.type` is nil and he wants to do it more succinctly. – rob mayoff Dec 10 '14 at 23:36
  • 1
    @rob I don't understand how you can claim he doesn't care about the 'type' variable? He is assigning it and may want to use it after. I think it's important for this question because it doesn't seem to work by negating if let with an exclamation mark. – LegendLength Sep 17 '16 at 14:17
  • 1
    He didn't put anything (like `...` or `// code here`) in the consequent of the `if` statement. Also, he wanted to ‘negate the "if let"’. Together these imply that he didn't care about the value. – rob mayoff Sep 17 '16 at 18:25
2

Another alternative I've used a few times:

switch json.type
{
    case .None: // ...
    case .Some(.Object): // ...
    case .Some(.Array):  // ...
    case .Some(.Number): // ...
    case .Some(.String): // ...
}

Since the ? is actually Optional<T> which is an enum on its own, defined as:

enum Optional<T> : Reflectable, NilLiteralConvertible 
{
    case None
    case Some(T)

    ...
}
Can
  • 8,502
  • 48
  • 57