Search code examples
swiftswift2swift-extensions

Restrict extensions on a type in Swift?


I have a specific class in Swift that I would like to restrict extensions from being created. I tried adding the final keyword, but it does not restrict extensions:

final class MyTest {
    func testFunc() {}
}

extension MyTest {
    func testFunc2() {}
}

let test = MyTest()
test.testFunc2()

Is this possible?


Solution

  • You cannot prevent someone from adding an extension to your class. Like you cannot prevent another developer from writing a function that uses your class.

    The final modifier only prevents subclassing. Other developers will always be able to add extensions.

    Is it really a problem?

    IMHO it is not.

    Let's look at this class

    final class Person {
        private let name: String
        func tellName() -> String { return "I am \(name)" }
        init(name: String) {
            self.name = name
        }
    }
    

    Now another developer could write (into another source file) this function

    func presentAndTellName(person: Person) {
        print("Hello everybody! \(person.tellName())")
    }
    

    This is possible in several other programming languages that don't support extensions. Well it's basically how many programming languages work and it's one of the reasons why we have access control (private, public, ...).

    Another developer infact cannot use private properties/methods of our class (unless it has access to our source file). He also cannot add properties to Person because it's a class marked as final so subclassing is offlimits. He can only write code that use it.

    We all are fine with this right?

    However writing a function that accepts a parameter and work only on that param sometimes is ugly. The function should be a method and the parameter value should be the instance.

    So we use extensions to convert this

    func presentAndTellName(person: Person) {
        print("Hello everybody! \(person.tellName())")
    }
    

    into this

    extension Person {
        func presentAndTellName() {
            print("Hello everybody! \(self.tellName())")
        }
    }
    

    It's only a way of writing the same logic and making it available in a OOP syntax sugar.

    Now instead of writing this

    presentAndTellName(person)
    

    we can write this

    person.presentAndTellName()
    

    Make your stuff private

    So how can you protect some of your class (/struct) logic and data from extensions? With the same mechanism you use to protect this stuff from functions external to your source file, just mark them private. This way other developers won't be able to use them into their extensions.

    Let's look again at our Person class.

    class Person {
        private let name: String
        func tellName() -> String { return "I am \(name)" }
        init(name: String) {
            self.name = name
        }
    }
    

    The name property is market as private so there is no way and external function or an extension will be able to directly access it.

    Wait, what about protocols?

    Ok, this is maybe the only thing where extensions offer something more than a better syntax.

    Infact given this protocol

    protocol Runner {
        func run()
    }
    

    we can conform to it a class we don't own

    extension Person: Runner {
        func run() { print("🏃") }
    }