Skip to content
Prepare to Grow

Swift Fundamentals

Access Control and Modules

Protect implementation details and design clear boundaries with private, internal, package, and public access.

Beginner4 min read2 guided activities

Before you begin

What you will understand

  • Explain Swift's access levels.
  • Hide implementation details behind a small API.
  • Distinguish file, type, package, and module boundaries.
  • Choose the least access required.
01

Learning stage

Concept Explanation

Access control defines where declarations can be used. Keeping implementation details hidden reduces accidental coupling and leaves a type free to change internally. Swift defaults to internal, which makes a declaration available inside its module but not to importing modules.

Use private for a declaration limited to its enclosing scope, fileprivate for the source file, package for modules in the same Swift package, internal for the current module, and public or open for exported framework APIs. open additionally permits external subclassing and overriding, so it should be deliberate.

  • private: enclosing declaration and same-file extensions.
  • fileprivate: the current source file.
  • internal: the current module; Swift's default.
  • package: modules within the same package.
  • public/open: importing modules; open also allows subclassing and overriding.
02

Learning stage

Syntax & Examples

The type exposes the behavior callers need while keeping mutable storage under its own control.

DownloadProgress.swiftswift
public struct DownloadProgress {
  public let totalBytes: Int
  public private(set) var receivedBytes = 0

  public init(totalBytes: Int) {
    self.totalBytes = totalBytes
  }

  public mutating func record(bytes: Int) {
    receivedBytes = min(receivedBytes + bytes, totalBytes)
  }
}
Other modules can read receivedBytes but can change it only through record(bytes:), which protects the type's rules.
03

Learning stage

Real-World Usage

These concepts appear throughout production iOS code. Focus on the requirement each technique communicates.

  • Keep view-model state writable only through meaningful methods.
  • Expose a stable API from a Swift package or framework.
  • Separate features into modules with intentional dependencies.
  • Make test helpers accessible at the narrowest useful boundary.
04

Learning stage

Common Mistakes

Notice these mistakes early so the compiler and type system can help instead of becoming obstacles.

  • Making everything public to solve an immediate compiler error.
  • Exposing mutable storage instead of domain behavior.
  • Using open when public is sufficient.
  • Assuming a source file and a module are the same boundary.
05

Learning stage

Practice Exercises

Work through these activities in a small Xcode project. No embedded playground is required on this site.

Activity 1

Protect a counter

Create a public Counter whose value is readable publicly but changes only through increment() and reset().

Hint: Use public private(set).

Activity 2

Review an API surface

Take a small type and label which members should be private, internal, or public. Explain each choice.

Hint: Start with the narrowest level and widen only for a real caller.

06

Learning stage

Knowledge Check

Answer each question before revealing the explanation.

QUESTION 1

What is Swift's default access level?

  1. A.private
  2. B.internal
  3. C.public
  4. D.open
Reveal answer

internal

Declarations are visible throughout their module unless another level is specified.

QUESTION 2

How does open differ from public for a class API?

Reveal answer

open permits subclassing and overriding from another module; public does not.

That extra extension point makes open a stronger and more consequential promise.

07

Learning stage

Interview Preparation

Explain the idea in terms of its purpose, behavior, and tradeoffs rather than reciting syntax.

Why prefer the least permissive access level?

Short answer: It prevents accidental dependencies and keeps implementation details changeable.

Explore the detailed answer

A small API surface communicates the supported contract. Callers cannot depend on hidden storage or helpers, so implementation changes have a smaller impact. Access control is therefore a design tool, not merely a compiler restriction.

Possible follow-ups

  • • When is fileprivate useful?
  • • What does package access solve?
08

Learning stage

Key Takeaways

Access control turns code boundaries into enforceable contracts. Expose the behavior callers need, protect mutable implementation details, and widen access only for a real module or package requirement.

  • internal is the default module-level access.
  • private and fileprivate protect implementation detail.
  • package supports cooperation across package modules.
  • public and open create long-lived external API commitments.