Learn Swift - Object-Oriented Programming & Protocols
Episode 5 of 23

Learn Swift - Object-Oriented Programming & Protocols

This episode covers OOP in Swift: classes, inheritance, and method overriding, access control ranging from public to private, protocols with protocol extensions as the foundation of protocol-oriented programming, as well as generics and type constraints for safe and reusable code.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Swift is a multi-paradigm language, and its two most important pillars are object-oriented programming and protocol-oriented programming. Episode 5 covers both: classes with inheritance, access rules, protocols as behavioral contracts, and generics for writing abstract, type-safe code.

Many beginner developers think of protocols as merely a complement to OOP. In reality, Swift's official philosophy places protocols at the center — Swift even adds features such as protocol extensions and protocol composition that go beyond what interfaces offer in other languages.

Class, Inheritance, and Method Overriding

Class and Inheritance Basics

Classes in Swift support inheritance: a class can inherit properties, methods, and initializers from a parent. The parent class is called a superclass, and the child class is called a subclass:

Simple inheritance
class Kendaraan {
    var roda = 4
    func deskripsi() -> String {
        return "Kendaraan dengan \(roda) roda"
    }
}
 
class Mobil: Kendaraan {
    var bahanBakar = "bensin"
}
 
let mobil = Mobil()
print(mobil.deskripsi())
print(mobil.bahanBakar)

class Mobil: Kendaraan inherits every member of Kendaraan and adds its own property. Inheritance allows code to be shared, but don't overdo it — hierarchies that are too deep become fragile to change.

Method Overriding

A subclass can replace a parent method's implementation with the override keyword:

Method overriding
class MobilListrik: Kendaraan {
    var kapasitasBaterai = 60
 
    override func deskripsi() -> String {
        return "Mobil listrik dengan baterai \(kapasitasBaterai) kWh"
    }
}
 
let tesla = MobilListrik()
print(tesla.deskripsi())

override func deskripsi() replaces the parent's behavior. Swift requires the override keyword to prevent accidental overriding — without it, compilation fails.

Access Control

Access Levels in Swift

Swift provides five access levels, from most open to most restricted:

  • open: accessible and subclassable outside the module.
  • public: accessible outside the module, but not subclassable.
  • internal: only within the module (default).
  • fileprivate: only within a single file.
  • private: only within the declaration and its extensions in the same file.
Access control in practice
public struct AkunPengguna {
    public private(set) var saldo: Double = 0
    private var pin: String = "0000"
 
    public mutating func tambahSaldo(_ jumlah: Double) {
        saldo += jumlah
    }
}

public private(set) var saldo makes the saldo property publicly readable but only mutable within the type. Disciplined access control protects internal invariants — this is where encapsulation really comes to life.

Protocols and Protocol Extensions

Protocol as a Contract

A protocol describes the properties and methods that adopting types must have, without storing any implementation:

Simple protocol
protocol MenampilkanRingkas {
    var judulRingkas: String { get }
    func ringkasan() -> String
}
 
struct Artikel: MenampilkanRingkas {
    var judulRingkas: String
    var isi: String
 
    func ringkasan() -> String {
        return "\(judulRingkas): \(isi.prefix(40))"
    }
}

struct Artikel: MenampilkanRingkas declares a commitment to fulfill the protocol's contract. The compiler checks conformance — forgetting to implement a method means a compile error, not a runtime bug.

Protocol Extensions and Protocol-Oriented Programming

A protocol extension provides a default implementation so that every type adopting the protocol automatically gets the behavior:

Default implementation via extension
extension MenampilkanRingkas {
    func lengkap() -> String {
        return judulRingkas + " - " + ringkasan()
    }
}
 
let artikel = Artikel(judulRingkas: "Swift", isi: "Pemrograman modern")
print(artikel.lengkap())

extension MenampilkanRingkas adds the lengkap() method for all types adopting the protocol. This is the essence of protocol-oriented programming: behavior is composed through protocols and default implementations, rather than inherited through rigid class hierarchies.

Generics and Type Constraints

Generic Functions and Types

Generics let you write code that works across many types without sacrificing type safety:

Generic function
func tukar<T>(_ a: inout T, _ b: inout T) {
    let sementara = a
    a = b
    b = sementara
}
 
var x = 1
var y = 2
tukar(&x, &y)
print(x, y)

func tukar<T>(_ a: inout T) works for any type — Int, String, or a custom type. The inout marker means the parameters are modified in place. Generics prevent you from writing duplicate code for every type.

Type Constraints

Sometimes you need to restrict which types are allowed, for example types that conform to a specific protocol:

Type constraint
func gabungkan<T: StringProtocol>(_ a: T, _ b: T) -> String {
    return "\(a) + \(b)"
}
 
print(gabungkan("Swift", "iOS"))

T: StringProtocol restricts T to types adopting StringProtocol. Constraints let the compiler call that protocol's members on generic parameters — the combination of generics and protocols is the most powerful pair in Swift.

Info

The combination of generics, protocols, and protocol extensions is the foundation of many modern Swift APIs such as Collection, View in SwiftUI, and Publisher in Combine. Watch for this pattern when reading third-party code.

Closing

Key takeaways:

  • Classes support inheritance and method overriding with the override keyword.
  • Access control consists of open, public, internal, fileprivate, and private.
  • Protocols define contracts of properties and methods that types must fulfill.
  • Protocol extensions provide default implementations, the core of protocol-oriented programming.
  • Generics write code for many types with full type safety.
  • Type constraints restrict generic types to specific protocols.

In the next episode, episode 6, we'll cover error handling and debugging — handling errors with throws, try, catch, and defer, creating custom error types, debugging in Xcode with breakpoints and LLDB, and assertions and unit tests to validate behavior. Time to make your code resilient!

Learn Swift - Object-Oriented Programming & Protocols | Learn Swift