DEV Community

Gamya
Gamya

Posted on

Swift Structs โ€” Access Control ๐Ÿ”

So far, every property and method we've added to our structs has been freely accessible from anywhere. But that's not always what you want. Sometimes you need to protect certain data from being changed in ways that could break your logic. That's exactly what access control is for. ๐Ÿฅ


The Problem Without Access Control

Let's say we're building a guild system for an anime RPG. A guild has a treasury, and members can deposit or withdraw gold โ€” but only through proper channels:

struct GuildTreasury {
    var gold = 0

    mutating func deposit(amount: Int) {
        gold += amount
    }

    mutating func withdraw(amount: Int) -> Bool {
        if gold >= amount {
            gold -= amount
            return true
        } else {
            print("Not enough gold!")
            return false
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This looks good โ€” we have proper deposit and withdraw methods that handle the logic. But here's the problem: gold is public by default, so nothing stops someone from doing this:

var fairyTail = GuildTreasury()
fairyTail.deposit(amount: 1000)
fairyTail.gold -= 9999  // ๐Ÿ˜ฑ Bypasses all our logic!
Enter fullscreen mode Exit fullscreen mode

That completely skips our validation logic. The guild treasury could go deeply negative, and our program would behave in unpredictable ways.


private โ€” Lock It Down

The fix is simple: mark gold as private. This tells Swift that gold can only be accessed inside the struct โ€” not from outside:

struct GuildTreasury {
    private var gold = 0

    mutating func deposit(amount: Int) {
        gold += amount
    }

    mutating func withdraw(amount: Int) -> Bool {
        if gold >= amount {
            gold -= amount
            return true
        } else {
            print("Not enough gold!")
            return false
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Now this would fail to compile:

fairyTail.gold -= 9999 // โŒ 'gold' is inaccessible due to 'private' protection level
Enter fullscreen mode Exit fullscreen mode

But this still works perfectly:

fairyTail.deposit(amount: 1000)   // โœ… uses our controlled method
fairyTail.withdraw(amount: 200)   // โœ… uses our controlled method
Enter fullscreen mode Exit fullscreen mode

Swift itself is now enforcing the rules โ€” you can't accidentally bypass the logic even if you tried. ๐ŸŽ‰

Important note: If you use private for one or more properties, you'll likely need to write a custom initializer, since Swift's auto-generated memberwise initializer won't be able to set private properties from outside the struct.


private(set) โ€” Read Freely, Write Carefully

Sometimes you want the best of both worlds: anyone can read the value, but only the struct's own methods can change it.

That's exactly what private(set) does:

struct GuildTreasury {
    private(set) var gold = 0

    mutating func deposit(amount: Int) {
        gold += amount
    }

    mutating func withdraw(amount: Int) -> Bool {
        if gold >= amount {
            gold -= amount
            return true
        } else {
            print("Not enough gold!")
            return false
        }
    }
}

var fairyTail = GuildTreasury()
fairyTail.deposit(amount: 1000)

print(fairyTail.gold)     // โœ… Reading is fine โ€” 1000
fairyTail.gold = 9999     // โŒ Writing directly is not allowed
Enter fullscreen mode Exit fullscreen mode

This is perfect for our treasury โ€” members of the guild can check how much gold is in the pot, but they can't just set it to whatever they want. Only the official deposit and withdraw methods can change it.


The Access Control Options

Swift gives us a few levels to choose from:

Keyword What It Means
private Only accessible inside this struct (or class/enum)
fileprivate Accessible anywhere within the same Swift file
public Accessible from anywhere โ€” inside or outside the module
private(set) Anyone can read, but only the struct can write

When you're learning and building apps, you'll mostly reach for private and private(set). The others become more relevant when you're building frameworks or larger multi-file projects.


Why Bother? You Could Just Not Break the Rules

You might be thinking: "I know not to bypass the withdrawal logic โ€” why do I need Swift to enforce it?"

Here's the honest answer: you are not always the person who will touch this code.

Maybe you write the GuildTreasury struct today, and six months from now you're in a rush to fix a bug and accidentally write directly to gold without thinking. Or maybe a teammate who joined the project later doesn't know about the rules. Or maybe you're using a library someone else wrote and you need to follow their rules.

Access control is a way of saying: "here's the interface you're supposed to use โ€” everything else is off limits." You're building a door and putting a lock on it, so that the only way in is through the proper entrance.

There's another benefit too: it reduces the surface area of your struct. If a struct has 15 properties and methods but 10 of them are private, anyone reading the code immediately knows: the 5 public ones are what I should be working with. The private ones are internal details, not something to worry about from the outside.


A Real-World Analogy

Think of a vending machine ๐Ÿฅ. You can see how many snacks are left through the glass (read access). You can press a button and pay to get one (controlled write through a method). But you can't open the front panel and take snacks directly โ€” that access is locked.

private(set) is the glass panel. private is the locked door. The button is your deposit() or withdraw() method โ€” the only approved way in.


Wrap Up

Concept What It Means
Access control Controlling who can read or write a struct's properties and methods
private Only accessible inside the struct itself
private(set) Anyone can read, only the struct can write
public Accessible from anywhere
Why it matters Prevents accidental misuse, enforces rules, reduces surface area

Access control is one of those features that feels optional when you're just getting started, but becomes genuinely important the moment your code grows or gets shared with anyone else โ€” including future you. ๐ŸŒธ


This article was written by me; AI was used to improve grammar and readability.


Top comments (0)