Swift - class method which must be overridden by subclass

SwiftAbstractSwift Protocols

Swift Problem Overview


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?

Swift Solutions


Solution 1 - Swift

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

Solution 2 - Swift

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() { ... }

}

Solution 3 - Swift

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'

Solution 4 - Swift

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)
    }
}

Solution 5 - Swift

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.

Solution 6 - Swift

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() {
        // ...
    }
}


Solution 7 - Swift

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

Solution 8 - Swift

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJuJoDiView Question on Stackoverflow
Solution 1 - SwiftdrewagView Answer on Stackoverflow
Solution 2 - SwiftJMiguelView Answer on Stackoverflow
Solution 3 - SwiftConnorView Answer on Stackoverflow
Solution 4 - SwiftDavid MolesView Answer on Stackoverflow
Solution 5 - SwiftSazzad Hissain KhanView Answer on Stackoverflow
Solution 6 - SwiftAmircaView Answer on Stackoverflow
Solution 7 - SwiftPikaczView Answer on Stackoverflow
Solution 8 - SwiftJordanView Answer on Stackoverflow