iOS

A Beginner’s Guide to Access Levels in Swift


Welcome to another programming tutorial in the Swift programming language! Today we we are going to talk about a topic that usually everybody knows something more or less, but it’s important for new developers in Swift to really understand what is all about and how it works. It’s quite often for the subject of our discussion not to receive the proper attention, so it’s either misused or not used at all. It’s a topic that the more someone advances in Swift, the more necessary it becomes in order to write better and clearer code. And that topic is about Access Levels in Swift!

In an effort to provide a definition, access levels actually define the accessibility, or in other words, the visibility of code entities out of the type, file or module that they have been defined. This could also be rephrased to that access levels define the protection level of the code in terms of visibility among programming entities, files and modules. For example, you might have an instance method that you want to be accessible only inside the class it belongs to, but being inaccessible from another class in a different file. You can achieve that by setting the proper access level to the method.

Or, another example, you have a custom type (class, structure, enumeration) that you want to keep just for internal use inside another custom type. Once again, you can achieve that by changing the access level of the first custom type. Or, you are creating your own library as a Swift package, or a closed framework, and you want to have a specific set of classes and methods available to be used publicly; once again… access levels.

Right above I mentioned the term “module”. If you are not sure what exactly a module is, the last example in the previous paragraph it’s what a module is all about. A library that you integrate as a Swift package, a framework, even a Xcode project that you add to another one; each one of them is considered to be a module. The project that accepts them is also another module. You will read more about all that later in this post.

Before changing the access levels of the various entities in your code, you must know why you want to do so, and being able to handle any side effects that such an action might have. Don’t worry though; nothing terrible or irreversible can happen. You can go back to previous states easily. My purpose in this post is to showcase how different access levels can be applied by using simple examples, and through that process to help you understand how all that works. Then, it’ll be easy to decide when and where you should change the default access levels, and what would be the meaning of such an action.

Access levels regard almost everything in Swift: Classes, structures, enumerations, properties, methods, protocols. Applying different access levels is not difficult; it’s just a matter of writing a special keyword, a specifier, right before the entity you need it. In a more general context, it’s mostly a matter of decision making than a matter of doing something programmatically heavy or intensive.

The Available Access Levels

The available access levels in Swift are the following:

  • private: This is the most strict access level, as entities marked as private are accessible only inside their defining type (such as properties or methods inside a class or structure), or their defining source file if they’re top-level types (such as classes, structures, enumerations or protocols defined in a file). None of these entities can be accessed by parts of code being outside their defining type or file.
  • file-private: This is a less strict access level, as it’s between the private and the internal (see next). Entities marked as file-private are visible and accessible from any other entity only inside the same source file, but not outside of it.
  • internal: The internal access level is the default one that is given automatically to all entities that they have not been marked otherwise. It’s actually the access level of all entities by default even though it’s not explicitly written (it’s implied). Internal entities are normally accessible in the scope of their defining module, but inaccessible by parts of code belonging to other modules. We’ll talk about modules later in this post.
  • public: The less restrictive access level existing in Swift. By making an entity public you make it accessible by other parts of code inside the same module, and to other modules as well. For example, you have a library integrated into a project as a Swift Package; entities that should be available to the project out of the library must be marked as public, otherwise they won’t be accessible. More about that later.
  • open: Similar to public, but it provides an additional “freedom”: Classes and methods marked as open can be subclassed and overridden respectively out of their defining module. More about that later too.

The Starter Material

Before we begin, please download the starter material pack that you will need to have in order to follow along. You will find the following in it:

  • A starter project to use called AccessLevels. It contains a few source files that we’ll add code to. Note that the starter project is a Command Line Tool for macOS that you simply run and get the results in the output area of Xcode.
  • A Swift Package named AccessLevelsPackage with the few necessary bits of code already implemented. We’ll add that package later to the starter project when we’ll talk about access levels and modules.

Once you get the starter project open it in Xcode. Through simple examples we are going to meet all access levels and how they work under various circumstances.

Note: As I said the starter project is a command line app for macOS. That makes it easy to get printed messages to the Xcode’s output area simply by pressing Cmd+R. However, if you are curious about how to run it from Terminal, then follow these steps:

1. Open Xcode preferences.

2. Click on the Locations tab.

3. Click on the small arrow right after the Derived data path.

4. A new Finder window will open with folders of derived data from all your projects. Find the one that contains the AccessLevel in its name.

5. Navigate to Build > Products > Debug.

6. Right click or Cmd+click on the Debug folder. Select New Terminal At Folder (or Services > New Terminal at Folder if you’re using macOS Mojave).

7. The Terminal application will show app right into the selected folder. Type ./AccessLevels and the program will be executed in Terminal.

The Internal Access Level

Let’s start with something simple, and that is the internal access level. As I mentioned already, this is the access level that all declared entities get by default, and in this part we’ll emphasize on that.

An internal entity is accessible by any part of code anywhere inside the same module, no matter if it exists on a different file or belongs to another type than the one we are working on. Even though we’ll talk about modules in details later, I should say shortly here that a module is any third party code like a library, a framework, or a project that is added to the working project, and it’s usually necessary to include its features to a file using the import statement. So, in short, an internal entity is visible throughout the scope of its defining module.

Usually internal entities don’t have to be explicitly specified, but doing so isn’t wrong; it’s just redundant. To see that, open the MessagePrinter.swift file where you will find the MessagePrinter class defined. Add the following method to it:

This is an extremely simple method. It accepts a String value as an argument and prints it to the output area. See that we didn’t specify any access level, so the above method is automatically marked as internal. The same applies to the class, as it doesn’t have any explicit specifier to change its access level. Both of them are visible anywhere in the project.

Open now the main.swift file, where we will create an instance of the MessagePrinter class and we’ll call the show(message:) method providing a message:

Press Cmd+R in your keyboard to run the program. In the output area you will see the above message printed.

Now go back to the MessagePrinter.swift file, and add the internal specifier right at the beginning of the method:

Run the program again. You will see the message printed to the output area and Xcode won’t complain by showing any errors. show(message:) method is still normally accessible by the main.swift file and the result is the same as before, even though we marked it explicitly as internal.

You could do the same to the MessagePrinter class and set the internal access level manually; nothing would change either.

So, it’s up to you to decide whether you’ll be writing the internal specifier in front of your entities or not; it’s not mandatory to do that, so you can skip it. But always remember that when you don’t see any access level specified, then most probably entities are internal by default. An exception on that rule is the methods declared in a protocol, but in that case they inherit the access level defined to the protocol.

Making Entities Inaccessible

One of the most common reasons that we want to change the default access levels is for making certain entities inaccessible out of their defining type or file, and therefore hide them from other parts of code in the module.

Let’s suppose that we don’t want the show(message:) method to be available out of the class that has been defined. In order to achieve that, we can set the method either as private, or as fileprivate.

In the MessagePrinter.swift file set the private access level to the show(message:) method:

Try to run the app now. You will get the following error in the main.swift file:

The error message that appears is quite explanatory:

‘show’ is inaccessible due to ‘private’ protection level

It clearly tells us that the show(message:) method cannot be reached because it has been marked as private. It’s only visible inside the type that was declared in, MessagePrinter in this case.

We would have had the exact same result if the show(message:) method had been marked as fileprivate. It would remain inaccessible from the main.swift file, but in that case it would still be visible in the MessagePrinter.swift file.

If private and file-private access levels cause some confusion right now we’ll make it clear right next. What you should keep from this part is that by declaring a method (or any other entity) as private or file-private it immediately becomes inaccessible by other parts of code, depending always on the access level you specify (private or file-private).

The Difference Between Private And File Private Levels

Even though private and file-private access levels look similar at first glance, and even though in the most cases that they are used lead to the same result, they have a certain difference that we ought to point out here.

Before you continue, go to the MessagePrinter.swift file and set the fileprivate access level to the show(message:) method:

What we are going to do now is to add the following right after the end of the MessagePrinter implementation (inside the MessagePrinter.swift file):

  1. An extension of the MessagePrinter class that will contain a new method, which in turn will call the show(message:).
  2. A class that will be a subclass of MessagePrinter and that will access show(message:) to print a message.
  3. A class that won’t be a subclass of MessagePrinter, but it will use an instance of it in order to access show(message:) and print a message.

Following the above order, here is the MessagePrinter extension.

Next, we’re adding a new class called AnotherPrinter that is declared as a subclass of MessagePrinter and inherits from it.

Finally, let’s add one more class called PrinterUser that is not a subclass of any other class, but it initializes an instance of MessagePrinter so it can access the show(message:) method:

Here we can make our first conclusion: fileprivate access level makes show(message:) accessible everywhere inside the file that has been defined into, regardless of the type of code that tries to access it (extension, subclass, another class). At the same time, it makes it inaccessible to any piece of code that tries to use it out of the MessagePrinter.swift file. The proof of that is that Xcode shows the error message in the main.swift file.

Change now the fileprivate access level of show(message:) to private and look at what happens to the code:

By changing the access level of the show(message:) method to private, AnotherPrinter and PrinterUser instantly lose access to it, but MessagePrinter‘s extension still keeps it. That’s normal, as it’s known that methods and properties are not visible by other types once they’re declared as private. One might would expect here to find a protected access level, like other programming languages have, so by using that instead of private then AnotherPrinter could keep having access to the show(message:) method. Unfortunately, Swift does not have such an access level. Consequently, private level affects the same way both subclasses of the parent class, and third classes (or structures) that use instances of it.

So, time for conclusion #2 in this part:

Only extensions of a custom type (class, structure) can access methods (or properties) that have been set as private to the type. Everything else, even if it exists in the same file, loses access to the private entity.

Let’s proceed to one last “experiment” in this part of the post. Cut the MessagePrinter extension, the AnotherPrinter and PrinterUser definitions from the MessagePrinter.swift file, and paste them in the Assistive.swift file you will find in the starter project.

What do you observe now?

MessagePrinter‘s extension loses access to the show(message:) method too!

So, here we can make an addition to the previous conclusion:

Extensions of custom types can have access to private entities of the main type if only they are implemented in the same file. If they are defined in a different file, then they lose access to private entities, like any other custom type or piece of code that normally does.

All the discussion above also apply to properties (not only methods) or other custom types inner to top-level types, and you can put structures in place of classes as well. Of course, subclassing is impossible in case of structures, but all the rest remain the same.

Note: You can remove the private access level of the show(message:) method so you can get rid of all errors Xcode shows.

Access Levels And Properties

When defining custom types such as classes or structures, it’s almost impossible not to declare properties in them. When instances of these custom types are used in other parts of code, then properties are accessible and easy to be changed even accidentally if they are left to the default, internal access level. The best strategy is to increase the protection level of those properties that are meaningful only internally to the custom type, so they are not visible out of it, and treat carefully other properties that should be visible out of their defining type, but they shouldn’t be changed that easily.

We’ll see how to achieve that through another simple example. For starters, go to MessagePrinter class and add the following property:

message property will keep the message given to the show(message:) method as an argument. Of course, we need to update that method too in order to make this happen:

Now, let’s define another simple method in MessagePrinter that prints the given message uppercased.

Note: We don’t really need a method to do that, but we’re defining it just for the sake of the example.

Switch to the main.swift file and append the next line:

Run the program now to see the result in the output area:

Let’s become a little bit vicious now and let’s temper with the message property before we call the showUppercasedMessage() method. Since it has the internal access level, we can easily access it from the main.swift file and change the original message.

Between the the call of the show(message:) and showUppercasedMessage() add the next line:

Now press Cmd+R to run again. This time the uppercased message is not the original one:

Even though we messed with the message property intentionally here, it’s quite possible such a change to happen accidentally sometimes. And even though doing so here is harmless, in real projects can cause problems.

So, let’s protect the message property by setting the private access level to it. Open the MessagePrinter.swift file and update the message property as shown below:

Altering its value now in the main.swift file is impossible, and Xcode shows this error:

‘message’ is inaccessible due to ‘private’ protection level

Ok, comment out the problematic line in main.swift file and run. You see once again in the output area what is expected to be seen.

Now, what if we wanted to have read access to the message property? It’s acceptable not to be able to set its value out of the MessagePrinter class, but what about getting it?

To make that even more clear, let’s suppose we need to add the next line to main.swift file after all previous lines:

The above statement forces Xcode to show the error message again that origins from the inability to access the private message property. The most obvious solution in order to get the message property’s value is to implement a new method that will simply return the message‘s value.

In MessagePrinter.swift file add the following inside the MessagePrinter class:

This method returns either the actual message if it’s not nil, or an empty string in case it’s nil.

Back to main.swift file, let’s use the above so the line that triggers the error to be fixed:

This time there is no problem, and we get the proper output by running the program.

However, despite the fact that the approach above works, the idea of creating a new method in order to get the value of each property is not right. There is a better way to do that using access levels.

To be precise, it’s possible to make a property maintain the private access level when setting its value, but keep the internal level when it comes to get its value. Here is how this is done in the message property of the MessagePrinter class:

By doing so, we are now able to get the message value, but unable to set it out of the MessagePrinter class.

In main.swift file now we can access message normally:

If we enable the following:

then Xcode shows a new error indicating that it’s impossible to set a value to message property out of the MessagePrinter class:

The getMessage() method is no longer needed in MessagePrinter class, as there is a better way to protect and access at the same time the message property!

Entities And Types With Different Access Levels

Sometimes does not only matter if an entity has been given an access level that makes it accessible from code out of the file or the type that was defined. It also matters whether that entity uses other entities with a more restrictive access level.

Think of the following example: Suppose that you have a method with an internal access level, but it returns a value of a custom type that has been given the private access level. What happens in such a case?

We’re about to find out, so go to MessagePrinter.swift file and start by adding the following enum as an inner custom type to MessagePrinter:

Still inside the MessagePrinter body, define the following new method. It returns a MessageSize value depending on how long the message is. If it’s nil, then it just returns the .none value:

Right now, both the MessageSize enum and the getMessageSize() methods have the same access level, which is internal. Now, make the MessageSize enum private:

Here’s what happens:

Xcode shows a new error pointing to getMessageSize() method:

Method must be declared private because its result uses a private type

Quite clear, don’t you think? The error is caused because we are trying to return a value of a private type from a method with a less protective level. Even though getMessageSize() is allowed to be accessed by code out of MessagePrinter, MessageSize enum must remain protected inside the class, therefore using it as a result type is not allowed. Xcode also suggests a solution: Method must be declared private. So, in order to get rid of this error, we must also make getMessageSize() private as well. Alternatively, we can change MessageSize enum’s access level to internal.

So, what’s the conclusion here? It’s not allowed to use a type with a more restrictive access level to an entity with less restrictive access level. Both must have the same, or compatible access levels. For example, public and internal access levels would have the same result inside the same module.

Note that if the MessageSize private type had not been used as a result value, but only inside the body of the getMessageSize() method, then we would have no problem at all. To demonstrate that, first make the getMessageSize() method private:

Then, add the next, internal new method:

As you can see, Xcode shows no error here. Regardless of whether getMessageSize() is private and it also uses the private MessageSize, no problem really exists since all private entities are used inside the isSmall() method and nothing is exposed to third party code.

Let’s see another example, and this time let’s use structures as custom types. Open the Structs.swift file and add the following two structure implementations:

The first structure, called SizeStruct contains two properties that describe the width and height of a size value. The second structure is supposed to represent a shape programmatically, and among other properties that describe it, it also contains a SizeStruct object in order to keep the shape’s size. Shape size is declared as private so it’s visible only inside the Shape struct.

Both structures have the internal access level, as it’s the one automatically assigned given that we did not explicitly set another one. That means that both structures are accessible by other parts of code out of that file inside the project.

Let’s change now the access level of the SizeStruct structure to file-private:

Instantly Xcode shows a complication:

The message that describes the error says:

Initializer must be declared fileprivate because its parameter uses a fileprivate type

The problem in this case is that we have a method, an initializer in particular here, that has a parameter value with a lower access level than the one that the initializer has. In other words, we cannot initialize a Shape object using that specific initializer because it’s using a parameter value of a strictly protected type. But once again, the root of the error is the exact same to the one we met in the previous example with the MessageSize enum and the getMessageSize() method. We cannot use a type or entity with a more restrictive protection level to another entity with a less restrictive access level.

The workaround here would be:

  1. either to remove the file-private level from the SizeStruct struct,
  2. or to define two separate parameter values that describe the width and height, and use those in order to set values to the size object inside the initializer:

Note that the same problem as above would exist if the SizeStruct had been marked as private.

Access Levels And Protocols

Let’s carry on now by going through a few examples that regard Swift protocols and access levels. Open the Protocols.swift file and add the following protocol definition:

This protocol is supposed to contain methods that perform simple math operations.

Right below the protocol add the following class that adopts the MathOperations protocol:

Xcode will display the error: Type ‘MathMaker’ does not conform to protocol ‘MathOperations’. That’s because the add(op1:op2:) required method is not implemented by the MathMaker class. To fix that, either click on the proposed Fix button, or copy and paste the following in the MathMaker class:

Switch now to main.swift file where we are going to initialize an object of the MathMaker class, and then call the add(op1:op2:) method in order to add two numbers:

If you run the program you will see the result in the output area:

So far, so good. Back to the Protocols.swift file again, change the default internal access level of the MathOperations protocol to private:

You see that nothing changes, even though MathOperations has been marked as private. That’s because the add(op1:op2:) method implementation is internal and still accessible by the main.swift file.

Go to the add(op1:op2) method declaration now in the MathOperations protocol and change the access level to private. Xcode shows an error that says:

‘private’ modifier cannot be used in protocols

It also provides a button to remove it. Actually, try to set various access levels. You will notice that for all cases except for the internal, Xcode prompts to remove the access level specifier.

So, that means that access levels cannot be specified in methods declared inside a protocol. Remove it.

Next, inside the MathMaker class change the access level of the add(op1:op2) method to private as well. Xcode shows another error this time:

Method ‘add(op1:op2:)’ must be declared fileprivate because it matches a requirement in private protocol ‘MathOperations’

Keep experimenting by changing the MathOperations protocol access level to internal, and by keeping the add(op1:op2) method in the MathMaker class private. There’s a new error message:

Method ‘add(op1:op2:)’ must be declared internal because it matches a requirement in internal protocol ‘MathOperations’

Change the MathOperations access level now to public. The error message changes again:

Method ‘add(op1:op2:)’ must be as accessible as its enclosing type because it matches a requirement in protocol ‘MathOperations’

Finally, change one last time the MathOperations access level to file-private. Here’s the error message:

Method ‘add(op1:op2:)’ must be declared fileprivate because it matches a requirement in fileprivate protocol ‘MathOperations’

The conclusion that results from the above experiments is clear:

The access level of the protocol’s methods that are defined in another type (like the MathMaker class here) must be equal to or less restrictive than the access level specified to the protocol, depending on the case.

Let’s suppose that you make both protocol and the method inside the MathMaker class file-private, so protection levels match. In that case, add(op1:op2:) method becomes inaccessible from the main.swift file, so a new error is appearing there.

Let’s try something else now. Comment out or delete the add(op1:op2:) method in the MathMaker class, and add the following protocol extension right after the MathOperations protocol:

This extension is meant to provide a default implementation of the methods declared in the protocol.

Remove now any access level specified to the MathOperations protocol (or specify the internal level), and set the private level to the add(op1:op2:) method inside the protocol’s extension:

add(op1:op2:) method is no longer visible to MathMaker class, therefore an error saying that Type ‘MathMaker’ does not conform to protocol ‘MathOperations’ shows up. That’s expected to happen, so change the access level of the method to file-private:

The fileprivate level of the method does not match to the internal level of the protocol, so Xcode prompts to change the access level of the method:

Method ‘add(op1:op2:)’ must be declared internal because it matches a requirement in internal protocol ‘MathOperations’

A similar message would appear if we would mark MathOperations protocol as public:

Method ‘add(op1:op2:)’ must be as accessible as its enclosing type because it matches a requirement in protocol ‘MathOperations — Mark the instance method as ‘internal’ to satisfy the requirement’

In both cases Xcode provides a Fix button to automatically fix the problem, and see that in both cases it suggests to use the internal access level. Notice also that so far the protocol has a less restrictive level, while we are trying to set a more restrictive access level to the method in the protocol’s extension.

So, it’s obvious that this cannot happen, and that:

  • the access levels of the protocol and the methods in the protocol’s extension must be the same,
  • or methods should be marked as internal,
  • or methods should have a less restrictive access level than the protocol’s.

Private access level is the only exception to the above, as it totally hides the method implementation in the protocol extension to types that adopt the protocol.

To verify the last option presented above, set the private or file-private level to protocol, and either the internal or public to add(op1:op2:) method in the protocol’s extension. You will notice that MathMaker class can now “see” the method from the extension, but the problem is in the main.swift file. The more restrictive protection level of the protocol prevails over the less restrictive level of the method in the extension, so eventually the method is not accessible in the main.swift file according to Xcode’s message:

‘add’ is inaccessible due to ‘private’ protection level

Generally, it’s up to you to decide if and when you’ll set different access levels to methods than the one that has been specified to the protocol when providing default implementations through protocol extensions. Most of the times it won’t be necessary to do that, unless you want to keep some methods inside the file or module that they were defined (for example, in the scope of a library only), and have some others publicly accessible from other modules or parts of code within the same module.

Modules And Access Levels

Time to talk about how access levels work when using other modules into a project, and to see what kind of limitations come up that do not exist when all code lies within the same module. To start, let’s make clear what a module is. According to Apple docs:

A module is a single unit of code distribution—a framework or application that is built and shipped as a single unit and that can be imported by another module with Swift’s import keyword.

Each build target (such as an app bundle or framework) in Xcode is treated as a separate module in Swift. If you group together aspects of your app’s code as a stand-alone framework—perhaps to encapsulate and reuse that code across multiple applications—then everything you define within that framework will be part of a separate module when it’s imported and used within an app, or when it’s used within another framework.

Also, according to Wikipedia:

Modular programming is a software design technique that emphasizes separating the functionality of a program into independent, interchangeable modules, such that each contains everything necessary to execute only one aspect of the desired functionality.

I’ll add to the above two definitions what I have already mentioned: Any library, framework, or even another project that you add into your project and you’re using the import statement in order to make it available into your source files is a module.

To demonstrate modules and access levels, there’s a sample Swift Package in the starter pack you downloaded and that should be added to our demo Xcode project. Open Finder and Xcode side by side, and drag and drop the AccessLevelsPackage folder to Project Navigator in Xcode.

Then, click on the AccessLevels project in the Project Navigator, make sure that the General tab and the AccessLevels target are selected, and click on the plus (+) button under the Frameworks and Libraries section.

In the window that will show up, locate and select the AccessLevelsPackage library, and then click on the Add button.

access-levels-package-xcode

At the end, you should be seeing AccessLevelsPackage in the Frameworks and Libraries section. Doing the above will make possible to use the import statement and include the AccessLevelsPackage module anywhere we want in our project.

Note: If you haven’t done it already, then now it’s a good time to take a quick look at the contents of the AccessLevelsPackage’s package. You will find a simpler version of the MessagePrinter class that we met above already, the SimpleMessagePrinter, and the BasicMathOperations protocol in homonymous files.

Accessing Code Into The Package

Modules contain code. But they must provide ways to make that code accessible, or more precisely, they must provide a public API (application programming interface) to other modules so that code can be actually used. A module does not have to disclose everything to other modules or entities out of it; whatever can be kept internally, should be kept internally. But there should be public access points that make possible the use of the module’s features.

Currently there’s nothing public in the AccessLevelsPackage that the AccessLevels project can have access to. If you explore both simple code files it includes you will instantly find this out. To see what’s the effect of that out of the module, open the main.swift file and start by importing the package:

Next, let’s create a new instance of the SimpleMessagePrinter class that has been defined in the module:

Note: You might want to comment out any content you added previously in the main.swift file.

Xcode will display the error:

Use of unresolved identifier ‘SimpleMessagePrinter’:

That’s happening because the AccessLevels module (our original project) cannot see the SimpleMessagePrinter class; it exists in a different module, the AccessLevelsPackage, therefore it cannot be accessed.

The current access level of the SimpleMessagePrinter is set to internal by default, and that makes it visible only within the AccessLevelsPackage module. Not outside of it.

Let’s change that, so open the SimpleMessagePrinter.swift file in the AccessLevelsPackage (make sure to expand it first in the Project Navigator), and add the public level specifier in front of the SimpleMessagePrinter class:

Switch back to main.swift file and press Cmd+B to build the project. There’s a new message now from Xcode:

‘SimpleMessagePrinter’ initializer is inaccessible due to ‘internal’ protection level

We come to realize here that not only we need a public class, but also a public initializer as well! So, in the SimpleMessagePrinter.swift change the access level of the init() method to public too:

Press once again Cmd+B to build the project. This time everything should be okay.

Note: Changes made to another module that has been added to the project like the AccessLevelsPackage here are not instantly visible to the entire project. That’s why it’s always necessary to build after every edit.

In the main.swift file now, let’s use the show(message:) method of the SimpleMessagePrinter class to print a message:

Xcode is complaining again about the access level of the method:

‘show’ is inaccessible due to ‘internal’ protection level

It’s like Xcode is saying: “Change the access level of the show method if you really want to access it!”

So, let’s do that. In the SimpleMessagePrinter.swift again set the public access level to show(message:) method:

Build the project again, or even better, run it. There should be no problem now so you will get the message specified in the output area of Xcode.

SimpleMessagePrinter class contains the message stored property that has been marked as private(set) so its value can be set only privately in the class. However, can we access it from the main.swift file or anywhere else out of the AccessLevelsPackage module?

Yes, we can. All we need is to make it public too:

By doing the above we can access the message property and get its value from anywhere in our project. For example, the following will work in the main.swift file as message is accessible through the printer object:

You realize at this point that whatever has to be used out of its defining module must be declared as public (or open as we’ll see right next). The collection of the public entities consist of the public API that AccessLevelsPackage provides.

Subclassing A Class That Exists In A Different Module

In a previous part of the tutorial we subclassed the MessagePrinter class that was defined in the MessagePrinter.swift file. That was easy to do, as both the parent class and its subclass either existed in the same file, or in different files but in the same module (the AccessLevels projects), so MessagePrinter was easily accessible by its subclass.

We’ll make now another experiment: We’ll subclass the SimpleMessagePrinter that is defined in the AccessLevelsPackage package to AccessLevels project. Open the Subclass.swift file and first of all import the AccessLevelsPackage:

Then, define the following class:

Xcode shows the next error:

Cannot inherit from non-open class ‘SimpleMessagePrinter’ outside of its defining module

It makes it clear that the SimpleMessagePrinter class has not been marked as open, and since it lives in a different module it’s not possible to create a subclass of it out of that module.

So, in the SimpleMessagePrinter.swift file change the access level of the SimpleMessagePrinter from public to open and press Cmd+B to build again:

What changes with that? Open access level is similar to public, so classes marked as open are accessible outside of their defining module. However, the addition that comes with open is that it makes subclassing possible.

Let’s add the following custom initializer now to Subclass.swift file:

This initializer initiates a new instance of the class and accepts a message as an argument. Once the super.init() is called to initialize the parent, it’s trying to assign the given message to the message property of the parent class.

But now, here it comes again the next Xcode error:

Cannot assign to property: ‘message’ setter is inaccessible

Of course, message property has been marked with the private(set) access level in the SimpleMessagePrinter class. So, how can we work around this problem?

One solution would be to remove the private(set) level and have the message property public, available both for get and set operations. However, we would take the risk that way to allow unwanted access to message from parts of code that should not be able to set its value. Given that there’s not a protected access level specifier as we said in a previous part of the tutorial, the best approach would be to have a custom initializer in the SimpleMessagePrinter class too that accepts the message as an argument. In the SimpleMessagePrinter class inside the SimpleMessagePrinter.swift file add this:

And then in the ChildPrinter class update init with this:

Notice that the override keyword is necessary in the beginning of the init method; parent has a similar initializer.

Building the program again produces no errors this time! What we’ve seen in this part so far covers how to subclass a class that exists in a different module using the open access level, and how to deal with a custom initializer. Why not to try to override a method too and see what is going to happen?

In the ChildPrinter class inside the Subclass.swift file override the one and only method of the SimpleMessagePrinter class:

And what a surprise… another error from Xcode:

Overriding non-open instance method outside of its defining module

That message indicates that show(message:) method must be marked as open, as public is not enough to let it be overridden. So, here you can see that the open access level is not used only in classes, but in methods too when they are about to be overridden in subclasses that exist in other modules.

Fixing the above is easy; simply change the public access level to open in the show(message:) method of the SimpleMessagePrinter class in the AccessLevelsPackage module:

Our project now can build successfully!

Preventing Subclassing

In case you want to prevent a class from being subclassed, then you can mark it as final. The same specifier can be used with methods so it’s impossible to override them. To get a taste of that, change temporarily the open level of the SimpleMessagePrinter class to final public:

You will also need to change the open access level in show(message:) to public:

Building now will make Xcode show the following two new errors in the Subclass.swift file:

access-level-in-swift

The first one regards the inheritance we’re trying to achieve for the ChildPrinter class:

Inheritance from a final class ‘SimpleMessagePrinter’

The second error regards the show(message:) method we’re trying to override:

Instance method overrides a ‘final’ instance method

Along with that, there’s also a message that we’ve seen already: Overriding non-open instance method outside of its defining module.

By marking SimpleMessagePrinter class as final, as well as doing the same in the show(message:) method, we prevented any effort to subclass the class and override the method respectively.

Note that final is not an access level specifier, rather an indication about whether a class is allowed to be subclassed, and a method to be overridden or not. Here we matched it with the public access level, but it can match with any of the other levels too, but not with open. Open allows subclassing and overriding, and it’s the exact opposite of the restrictions that final brings. So, open and final just don’t go together! If you try to do so Xcode won’t leave you with many options:

Class cannot be declared both ‘final’ and ‘open’

You can undo at this point the few recent changes you did for demonstrating final so Xcode errors to disappear.

Using Protocols Defined To Another Module

AccessLevelsPackage contains another source file besides the SimpleMessagePrinter.swift; that is the BasicMathOperations.swift that contains the definition of the BasicMathOperations protocol along with an extension that provides default implementation to the four methods that perform the four basic math operations.

By opening that file you will notice that there’s no special indication neither about the access level of the protocol, nor the implemented methods in the extension. Therefore, they have the default level, internal, and that makes them accessible only by parts of code inside their own module; AccessLevelsPackage.

Let’s try now to use that protocol in a class that we have already defined for that purpose in the Protocols.swift file, the MathMaker. For the sake of the examples in previous parts, this class already adopts the MathOperations protocol that is implemented inside the same source file:

Change that so it adopts the BasicMathOperations from the AccessLevelsPackage module:

Also, don’t forget to import the module at the beginning of the file:

Since BasicMathOperations protocol is accessible internally in its defining module only (AccessLevelsPackage), MathMaker class cannot access it, so the following error that Xcode shows is absolutely justified:

Use of undeclared type ‘BasicMathOperations’

Let’s switch back to BasicMathOperations.swift file, and let’s make the protocol public:

The above will make it accessible from the MathMaker class, but the following problem will come up:

Type ‘MathMaker’ does not conform to protocol ‘BasicMathOperations’

Do you want to add protocol stubs?

Xcode says here that the methods defined in the protocol must be implemented by the MathMaker class. But it shouldn’t be asking for that, since there’s a default implementation for them in the protocol’s extension. So, why is it doing that?

The problem is that even though the BasicMathOperations protocol has been marked as public, the implemented methods in the extension remain visible internally only; it’s mandatory to explicitly make them public too if we want to access them out of their module. They don’t get the access level of the protocol automatically.

So, in the BasicMathOperations.swift file let’s mark them as public:

Building the program now succeeds! Both protocol and the default implementation of its required methods are now public and they can be used by the MathMaker class. To make sure about that, let’s go to main.swift file and let’s make a few math operations:

Here’s what is printed in the output area:

Conclusion

So, that discussion about access levels in Swift is about to come to its end. The examples that we went through in this tutorial were quite simple, but I believe that they demonstrated clearly how access levels are used in various situations.

As a final advice I’d like to recommend this: Use access levels even if you’re not importing other modules into your project. By doing so, you can hide file-specific or type-specific entities and implementations from the rest of the code, and make accessible only those entities (properties, methods, custom types) that you really need out of their defining file or type.

Knowing how to deal with access levels becomes mandatory especially when you create your own libraries and frameworks; you must know what entities will consist of the public API, and what to keep hidden inside the modules. I hope you enjoyed this post, and thanks for reading!

Tutorial
Working with Auto Layout Visual Format Language and Programmatically Creating Constraints
Tutorial
A Beginner’s Guide to Protocols and Protocol Extensions in Swift
SwiftUI
Working with Maps and Annotations in SwiftUI
Shares