14

Because Swift does not have abstract methods, I am creating a method whose default implementation unconditionally raises an error. This forces any subclass to override the abstract method. My code looks like this:

class SuperClass {
    func shouldBeOverridden() -> ReturnType {
        let exception = NSException(
            name: "Not implemented!",
            reason: "A concrete subclass did not provide its own implementation of shouldBeOverridden()",
            userInfo: nil
        )
        exception.raise()
    }
}

The problem: Because the function is expected to return a value, and the above function has no return statement, compilation fails. I need some way to convince the compiler that this function could never complete execution, because it will always raise an error.

How could this be done in Swift, when all of the error-handling seems be library-level, and therefore beyond the understanding of the compiler? Is there any kind of language-level feature for (hopefully gracefully) terminating the execution of a program?

exists-forall
  • 4,148
  • 4
  • 22
  • 29
  • While you've self-answered your question with a solution, you're trying to solve a bad/wrong problem. There's a reason why Swift doesn't have abstract methods built in. – nhgrif Dec 05 '15 at 13:23

4 Answers4

24

Swift's @noreturn attribute marks functions and methods as not returning to their caller.

As probably the simplest example, the signature of the built-in function abort()'s is:

@noreturn func abort()

This gives the compiler all the information it needs. For example, the following will compile just fine:

func alwaysFail() -> Int {
    abort()
}

Although alwaysFail() theoretically returns an Int, Swift knows that execution can't continue after abort() is called.

The reason my original code didn't work is because NSException.raise is a pre-Swift method, and therefore doesn't have the @noreturn attribute. To easily solve this, I can either use abort():

func shouldBeOverridden() -> ReturnType {
    println("Subclass has not implemented abstract method `shouldBeOverridden`!")
    abort()
}

or, if I still want to use NSException, I can define an extension with the proper attribute

extension NSException {
    @noreturn func noReturnRaise() {
        self.raise()
        abort() // This will never run, but Swift will complain that the function isn't really @noreturn if I don't call a @noreturn function before execution finishes.
    }
}

As a third option, I can just use a never-called abort() after an NSException.raise() to placate the compiler. The earlier option, using an extension, is really just an abstraction for doing this:

func shouldBeOverridden() -> ReturnType {
    let exception = NSException(
        name: "Not implemented!",
        reason: "A concrete subclass did not provide its own implementation of shouldBeOverridden()",
        userInfo: nil
    )
    exception.raise()
    abort() // never called
}
exists-forall
  • 4,148
  • 4
  • 22
  • 29
12

In Xcode 8 beta 6 (Swift 3 beta 6) you can now use the Never return type instead of @noreturn to indicate that a function won't return to its caller:

func crash() -> Never {
    fatalError("Oops")
}
Luke
  • 4,908
  • 1
  • 37
  • 59
2

It sounds like what you're doing would be better accomplished by creating a protocol and making shouldBeOverridden a required method, then having your classes conform to that protocol. https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html

Andy Riordan
  • 1,154
  • 1
  • 8
  • 13
  • This could work, but there are other elements of all subclasses of `SuperClass` which would be best implemented using a shared implementation in `SuperClass`. In particular, there are several stored properties that all of my "subclasses" (or classes conforming to the protocol, per your suggestion) must have, and it would be good to not have to redeclare these for every single subclass. – exists-forall Jan 07 '15 at 21:46
  • 2
    I would recommend combining the two approaches, and having a protocol as well as a base class. Abstract methods aren't very common in Objective-C or Swift, and I haven't found a case which protocols weren't a better choice for. Apple uses this combination approach on occasion as well (see UIBarPositioning). – Andy Riordan Jan 07 '15 at 21:49
  • 1
    Ok, that works well enough. It seems a little bit more verbose, but it makes sense and it's pretty clean. – exists-forall Jan 07 '15 at 21:51
  • Also, abstract methods often belong to abstract objects, I find. `Furniture` wouldn't make sense as a class, as having a `seatingCapacity` property doesn't make sense for an abstract Furniture concept. A `Chair` certainly does, though, as does a `Couch`. Therefore, both of those objects conforming to a Furniture protocol rather than deriving from a Furniture base class generally makes more sense. Checks like `myCouch as? Furniture` would work the same for protocols as they would for classes. The general rule of thumb I use is, "Would it ever make sense to instantiate a `Furniture` object?" – Andy Riordan Jan 07 '15 at 21:56
0

What about creating a computed property returning a Never called abstract or overrideMe or similar, like so:

   var abstract: Never { fatalError("Must be overridden") }

Example of usage:

class AbstractTableViewCell: UITableViewCell {
    func configure(with model: Any) { abstract }
}

final class ContactTableViewCell: AbstractTableViewCell {
    override func configure(with model: Any) { 
        /* Real implementation here */
    }
}
Sajjon
  • 8,938
  • 5
  • 60
  • 94