105
votes

Is there a standard way to make a "pure virtual function" in Swift, ie. one that must be overridden by every subclass, and which, if it is not, causes a compile time error?

8
You could implement it in the super class and make an assertion. I've seen this used in Obj-C, Java and Python.David Skrundz
@NSArray This causes a runtime, and not a compile time, errorJuJoDi
This answer will help you too. enter link description hereChamath Jeevan
A pure virtual function is implemented by protocols (compared to interfaces in Java) If you need to use them like abstract methods have look at this question/answer: stackoverflow.com/a/39038828/2435872jboi

8 Answers

166
votes

You have two options:

1. Use a Protocol

Define the superclass as a Protocol instead of a Class

Pro: Compile time check for if each "subclass" (not an actual subclass) implements the required method(s)

Con: The "superclass" (protocol) cannot implement methods or properties

2. Assert in the super version of the method

Example:

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

Pro: Can implement methods and properties in superclass

Con: No compile time check

61
votes

The following allows to inherit from a class and also to have the protocol's compile time check :)

protocol ViewControllerProtocol {
    func setupViews()
    func setupConstraints()
}

typealias ViewController = ViewControllerClass & ViewControllerProtocol

class ViewControllerClass : UIViewController {

    override func viewDidLoad() {
        self.setup()
    }

    func setup() {
        guard let controller = self as? ViewController else {
            return
        }

        controller.setupViews()
        controller.setupConstraints()
    }

    //.... and implement methods related to UIViewController at will

}

class SubClass : ViewController {

    //-- in case these aren't here... an error will be presented
    func setupViews() { ... }
    func setupConstraints() { ... }

}
35
votes

There isn't any support for abstract class/ virtual functions, but you could probably use a protocol for most cases:

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

If SomeClass doesn't implement someMethod, you'll get this compile time error:

error: type 'SomeClass' does not conform to protocol 'SomeProtocol'
14
votes

Another workaround, if you don't have too many "virtual" methods, is to have the subclass pass the "implementations" into the base class constructor as function objects:

class MyVirtual {

    // 'Implementation' provided by subclass
    let fooImpl: (() -> String)

    // Delegates to 'implementation' provided by subclass
    func foo() -> String {
        return fooImpl()
    }

    init(fooImpl: (() -> String)) {
        self.fooImpl = fooImpl
    }
}

class MyImpl: MyVirtual {

    // 'Implementation' for super.foo()
    func myFoo() -> String {
        return "I am foo"
    }

    init() {
        // pass the 'implementation' to the superclass
        super.init(myFoo)
    }
}
2
votes

You can use protocol vs assertion as suggested in answer here by drewag. However, example for the protocol is missing. I am covering here,

Protocol

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

Now every subclasses are required to implement the protocol which is checked in compile time. If SomeClass doesn't implement someMethod, you'll get this compile time error:

error: type 'SomeClass' does not conform to protocol 'SomeProtocol'

Note: this only works for the topmost class that implements the protocol. Any subclasses can blithely ignore the protocol requirements. – as commented by memmons

Assertion

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

However, assertion will work only in runtime.

1
votes

This is what I usually do, to causes the compile-time error :

class SuperClass {}

protocol SuperClassProtocol {
    func someFunc()
}

typealias SuperClassType = SuperClass & SuperClassProtocol


class Subclass: SuperClassType {
    func someFunc() {
        // ...
    }
}
0
votes

You can achieve it by passing function into initializer.

For example

open class SuperClass {
    private let abstractFunction: () -> Void

    public init(abstractFunction: @escaping () -> Void) {
        self.abstractFunction = abstractFunction
    }

    public func foo() {
        // ...
        abstractFunction()
    }
}

public class SubClass: SuperClass {
    public init() {
        super.init(
            abstractFunction: {
                print("my implementation")
            } 
        )
    }
}

You can extend it by passing self as the parameter:

open class SuperClass {
    private let abstractFunction: (SuperClass) -> Void

    public init(abstractFunction: @escaping (SuperClass) -> Void) {
        self.abstractFunction = abstractFunction
    }

    public func foo() {
        // ...
        abstractFunction(self)
    }
}

public class SubClass: SuperClass {
    public init() {
        super.init(
            abstractFunction: {
                (_self: SuperClass) in
                let _self: SubClass = _self as! SubClass
                print("my implementation")
            }
        )
    }
}

Pro:

  • Compile time check for if each subclassimplements the required method(s)
  • Can implement methods and properties in superclass
  • Note that you can't pass self to the function so you won't get memory leak.

Con:

  • It's not the prettiest code
  • You can't use it for the classes with required init
-2
votes

Being new to iOS development, I'm not entirely sure when this was implemented, but one way to get the best of both worlds is to implement an extension for a protocol:

protocol ThingsToDo {
    func doThingOne()
}

extension ThingsToDo {
    func doThingTwo() { /* Define code here */}
}

class Person: ThingsToDo {
    func doThingOne() {
        // Already defined in extension
        doThingTwo()
        // Rest of code
    }
}

The extension is what allows you to have the default value for a function while the function in the regular protocol still provides a compile time error if not defined