You may have encountered an open class in a library you use, or maybe you’ve considered using one within your own code. open provides an additional access control level that’s even less restrictive than public. But what exactly does open mean, and how is it different from public?

Here’s what you need to know about open:

1. open classes can be subclassed within modules that import them.

In contrast, public classes cannot be subclassed in modules that import them.

So if I have ModuleA with the following classes defined:

open class ClassA {}
public class ClassB {}

And I try to subclass them in my own module ModuleB:

import ModuleA

// this compiles just fine.
class SubclassA: ClassA {}

// this errors.
class SubclassB: ClassB {}

My subclass of the open class works just fine - but when I try to subclass the public class, I get error: cannot inherit from non-open class 'ClassB' outside of its defining module.

2. open class members can be overridden within modules that import their classes.

(By “class members”, I mean class properties and methods.)

In contrast, public class members cannot be subclassed in modules that import their classes.

Let’s return to our previous open class and add a method to it:

open class ClassA {
    open func foo() {
        print("foo in base class")
    }
}

Now, when we import ModuleA into our own module, we can override foo in our subclass:

import ModuleA

class SubclassA: ClassA {
    override func foo() {
        print("foo in subclass")
    }
}

3. Class members of an open class don’t have to be open too, and they’re internal by default.

Just like with members of public classes, if you don’t specify the access control level for a member of an open class, it will default to internal.

If ClassA looked like this:

open class ClassA {
    open func foo() {
        print("foo in base class")
    }

    func bar() {
        print("bar in base class")
    }
}

I could override foo within ModuleB just fine like before, but trying to override bar would give me error: method does not override any method from its superclass, since there is no public or open method barthat we know about from within ModuleB.

We also could’ve marked bar with an explicit level that wasn’t open, like private or public, and the typical rules for those access levels would apply.

4. open only applies to classes and class members.

Since open describes behavior related to inheritance, it only makes sense to use it with types that support inheritance - in Swift, that’s just classes!

5. Think carefully before marking anything in your library open.

There’s a reason that open is not the default - it should only be used when you’re sure that you want your users to be able to do what it allows.

The Swift language guide states:

Marking a class as open explicitly indicates that you’ve considered the impact of code from other modules using that class as a superclass, and that you’ve designed your class’s code accordingly.

Basically, unless if there is a good, concrete reason for users to subclass your class, don’t allow it! Once you make a library class open, changing it back to public becomes a breaking API change, as users may have already implemented subclasses that would no longer be allowed.

It’s worth noting that it’s used very sparingly in the Swift standard library – I only count 6 occurrences of open class.

In conclusion

Making a class and its members open can be a useful tool if you want the ability to subclass/override outside of the module where the class is defined, but use it carefully!

For more information, I recommend reading the Swift language guide chapter on access control.

Please let me know any thoughts or questions you have via Twitter or email!