Start learning free

LESSON 403 OF 415 · OBJECT-ORIENTED PROGRAMMING

Access Modifiers in Swift: private and Encapsulation

Lesson Read the full explanation

TL ; DR:

  • An access modifier controls who can see a member of a class. The two you'll use most are public (anyone) and private (only this class).


  • Make data private so callers can't break invariants. Expose what they need through public methods (often called getters and setters):

    class Dog {
        private var name: String
    
        init(_ name: String) {
            self.name = name
        }
    
        func getName() -> String {
            return name
        }
    }
    

  • Outside the class, rex.name is a compile error. Use rex.getName() instead.



Full lesson:

So far every field we wrote was visible to all the code in your module (Swift's default level, internal). That's convenient, but leaks internals. If you later decide to validate or compute the value differently, every caller in the codebase breaks.


Encapsulation

Encapsulation is the idea that an object's data should be hidden behind methods. The class controls how it's read and written; outside code only sees the API.

class Account {
    private var balance: Double = 0

    func deposit(_ amount: Double) {
        if amount < 0 { fatalError("Invalid amount") }
        balance += amount
    }
}

The Swift access levels

open / publicVisible everywhere, including other modules (open also allows subclassing and overriding there).
internalThe default: visible anywhere inside the same module.
fileprivateVisible only inside the same source file.
privateVisible only inside the enclosing declaration (and its extensions in the same file).

For now, focus on public and private.


Your task

The Dog class's name field is now private. Add a func getName() -> String method that returns it.

Dog's name: Rex
STEP 1 OF 2
    Ctrl + Enter to check

    SWIFT · OBJECT-ORIENTED PROGRAMMING

    Access Modifiers in Swift, explained

    What access control does

    An access modifier decides which code may use a member of a type. If every property is open to everyone, any code can change any value, and if you later change how a value is stored or validated, every caller that touched it directly may break. Hiding data and exposing a small set of methods is called encapsulation.

    The Swift levels

    Swift has five access levels, from most to least open:

    LevelVisible
    openEverywhere, and classes can be subclassed or overridden from other modules.
    publicEverywhere, but no subclassing or overriding outside the defining module.
    internalAnywhere in the same module. This is the default when you write nothing.
    fileprivateAnywhere in the same source file.
    privateOnly inside the enclosing declaration (and extensions of it in the same file).

    For these lessons, the two that matter are the default (members are reachable from other code) and private. Swift has no protected keyword and no package-private level like some other languages.

    Making a property private

    class Dog {
        private let name: String
    
        init(_ name: String) {
            self.name = name
        }
    
        func getName() -> String {
            return name
        }
    }
    

    Inside Dog, name is used directly. Outside the class, it is off limits. Even though Solution is in the same file, writing rex.name there fails with 'name' is inaccessible due to 'private' protection level, because private is scoped to the class, not the file. The public way in is the method: rex.getName() returns the value without allowing a change.

    The lesson's property is private let, so it can be set once by the initializer and never changed afterwards, not even inside the class. A private var could still be changed by the class's own methods.

    A read-only property without a getter

    Swift offers a shortcut for the common case of "readable by everyone, writable only by the class": private(set).

    class Account {
        private(set) var balance: Double = 0
    
        func deposit(_ amount: Double) {
            if amount < 0 { return }
            balance += amount
        }
    }
    
    let account = Account()
    account.deposit(5)
    print(account.balance)   // 5.0
    

    Reading account.balance is allowed from outside, and assigning to it is not: account.balance = 100 fails with cannot assign to property: 'balance' setter is inaccessible. Only deposit can change the balance, so it can enforce the rule that deposits are not negative. Swift code often uses this form in place of a getBalance() method.

    The exercise

    The starter Dog has a private let name, and mainFunction prints "Dog's name: " + rex.getName(). Add the missing method, first with an empty body and a String return type, then returning the name:

    class Dog {
        private let name: String
    
        init(_ name: String) {
            self.name = name
        }
    
        func getName() -> String {
            return name
        }
    }
    
    class Solution {
        func mainFunction() {
            let rex = Dog("Rex")
            print("Dog's name: " + rex.getName())
        }
    }
    

    The output is Dog's name: Rex.

    Why bother hiding anything

    Suppose Account let anyone write to balance. Any line of code in the program could set it to a negative number, and a bug could appear far from the line that caused it. With the property restricted, the places that can change it are exactly the methods inside the class, so you only have to look there. The class can also change its internals later, for example storing cents as an Int instead of a Double, without touching a single caller, as long as the public methods keep their signatures.

    Common mistakes

    • Reading the private property directly from outside the class. Use the method.
    • Returning nothing. A method declared -> String needs a return.
    • Making the getter private as well. Then no outside code can read the name at all.
    • Making everything private. Hide what callers must not touch, and expose what they need.

    Key takeaways

    • Members are internal by default; private limits them to their own declaration.
    • Expose data through methods, or through private(set) for a read-only property.
    • Swift's levels are open, public, internal, fileprivate and private.
    Keep going with SwiftAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free