0

Let's say I have a function like this:

func findFooById(id: String) -> Foo {
    if let foo = fooList[id] {
        return foo
    } else {
        assertionFailure("Couldn't find a foo with id = \(id)")
    }
}

In debug builds, I want my assertion to trigger so I'm clued into the fact my server is sending me inconsistent data. But in release builds, I want to return an empty foo (return Foo(id: "", name: "", magic: nil)) to keep my UI consistent.

(I'd prefer not to redefine findFooById to return Foo?, as that would force me to deal with nil in every caller. Displaying a blank object is good enough for this rare case.)

How do I achieve this?

Robert Atkins
  • 23,528
  • 15
  • 68
  • 97
  • Just putting the `return` statement on the line after the `assertFailure` gives me a "Will never be executed" warning; I could turn this off with a #pragma in Objective-C, but I'm not sure how to do this in Swift. – Robert Atkins Mar 20 '15 at 10:15
  • I recently read a question here where this was explained. There is some keyword for it. Can't recall the thread :-/ – qwerty_so Mar 20 '15 at 10:18
  • Found it: put @noreturn in front of the func. – qwerty_so Mar 20 '15 at 10:19
  • Does that help though? In the assertionFailure() case @noreturn is implied and that's fine, but when assertions are off and assertionFailure() is a no-op, but that gives me the "Will never be executed" warning on the return that I want in that case. – Robert Atkins Mar 20 '15 at 10:21
  • Ups. Got it the wrong way. The warning is **because** of the @noreturn. – qwerty_so Mar 20 '15 at 10:22
  • According to the API docs, `assertionFailure()` is ignored in optimized builds, so that you can simply add `return Foo(...)` after the assertion. But practically, I found that `assertionFailure()` throws a runtime exception even in optimized builds. – Martin R Mar 20 '15 at 10:23
  • @MartinR Indirectly my question http://stackoverflow.com/questions/29143855/nil-optional-does-not-crash-in-release is related – qwerty_so Mar 20 '15 at 10:45

1 Answers1

0

As mentioned in another Stack Overflow answer, you can set flags based on being in Debug/Release modes, which would allow you to do:

#if DEBUG
    assertionFailure("Couldn't find a foo with id = \(id)")
#else
    return Foo()
#endif

However, since assertionFailure is a no-op in optimised builds (which release builds are by default), you should be able to do:

func findFooById(id: String) -> Foo {
    if let foo = fooList[id] {
        return foo
    } else {
        assertionFailure("Couldn't find a foo with id = \(id)")
        return Foo()
    }
}
Community
  • 1
  • 1
Joseph Duffy
  • 4,566
  • 9
  • 38
  • 68
  • That's what I thought, but I found that it throws a runtime exception even in optimized builds, at least with Xcode 6.2. Could be a Swift bug. – Martin R Mar 20 '15 at 10:24
  • I am not 100% sure this is correct. The way I read http://blog.krzyzanowskim.com/2015/03/09/swift-asserts-the-missing-manual/, it's the Swift optimisation level that controls whether asserts are compiled in, **not** DEBUG/RELEASE. We are running our release builds with -Onone because the Swift optimiser is broken. – Robert Atkins Mar 20 '15 at 10:25
  • Yes, in my linked page it states "In optimized builds this is a noop", so technically it is not based on debug/release, but by default it is. I will update my answer to make this more clear – Joseph Duffy Mar 20 '15 at 10:26
  • Note comment above: when I implement your second suggestion, I get a compiler warning I can't turn off. – Robert Atkins Mar 20 '15 at 10:26
  • `func testing() -> [Int] { assertionFailure("Failure in non-optimised mode") return [Int]() }` works for me in both debug and release modes. In which mode are you seeing the error? – Joseph Duffy Mar 20 '15 at 10:30
  • I'm getting the "Will Never Be Executed" warning in debug, but I've got Swift optimisations set to -Onone for debug and release. – Robert Atkins Mar 20 '15 at 16:46
  • If you're using `-Onone` in both then you may have to use the first solution I provided (the `#if DEBUG`) one – Joseph Duffy Mar 21 '15 at 11:48
  • The "correct" solution in my mind is your second one, but with a way to turn off that compiler warning in all cases (or for the compiler to be smart enough to not emit the warning.) I'll file a bug. – Robert Atkins Mar 23 '15 at 15:19
  • I don't think it's a bug; it's giving you the error because you're not compiling with `-O`, but rather `-Onone`. Maybe you could make your own version of `assertionFailure` that has the `#if DEBUG` check in it? – Joseph Duffy Mar 23 '15 at 15:34