Chapter 78: OOP Intro
1. What does “Object-Oriented Programming” actually mean? (the clearest possible explanation)
OOP is a way of thinking about and organizing code by grouping:
- data (properties / variables)
- behavior (methods / functions)
together inside objects.
Instead of writing one long list of instructions (like old procedural programming), you create small, smart, self-contained things (objects) that:
- know their own data
- know how to do things with that data
- can talk to other objects
Real-life analogy (the one that clicks for almost everyone):
Think of a smartphone as an object.
- Data it remembers: screen brightness, battery level, phone number, owner name, photos, apps installed…
- Behavior it can do: makeCall(), takePhoto(), sendMessage(), chargeBattery(), setBrightness(80), playMusic(…)
When you use the phone, you don’t open the back cover and manually move electrons. You just tell the phone what you want (“take a photo”, “call mom”).
OOP lets you model your program the same way:
- Create a User object that knows its name, email, profile picture, and can login(), logout(), updateProfile()
- Create a ShoppingCart object that knows its items, total price, and can addItem(), removeItem(), applyCoupon(), checkout()
You don’t manipulate the data directly — you ask the object to do things for you.
That’s the core idea.
2. The Four Classic Pillars of OOP (explained in Swift)
Almost every book and course talks about these four ideas. Here’s what they really mean in Swift, with clear examples.
Pillar 1 – Encapsulation
“Hide the internal details — only expose what is safe and necessary”
In Swift this is mainly done with:
- private / fileprivate / internal / public / open access modifiers
- computed properties (get/set)
- methods that control how data is read or changed
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
class BankAccount { // Nobody outside this class can touch balance directly private var balance: Double = 0.0 // Public read-only view var currentBalance: Double { balance } init(initialDeposit: Double) { deposit(amount: initialDeposit) } // Public methods — the only safe way to change balance func deposit(amount: Double) { guard amount > 0 else { print("Amount must be positive") return } balance += amount print("Deposited ₹\(amount). New balance: ₹\(balance)") } func withdraw(amount: Double) -> Bool { guard amount > 0 else { print("Amount must be positive") return false } guard amount <= balance else { print("Insufficient funds") return false } balance -= amount print("Withdrew ₹\(amount). New balance: ₹\(balance)") return true } } // Usage — encapsulation in action let myAccount = BankAccount(initialDeposit: 10000) myAccount.deposit(amount: 5000) myAccount.withdraw(amount: 2000) // myAccount.balance = -100000 // ← compile error — private! print("Balance: ₹\(myAccount.currentBalance)") |
Key lesson: Make data private, expose behavior (methods) that safely change or read the data.
Pillar 2 – Abstraction
“Show only the essential features — hide the complicated details”
In Swift, abstraction is mostly done with protocols.
A protocol says: “This thing can do these things” — without caring how it does them.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
protocol Drawable { func draw() var area: Double { get } } class Circle: Drawable { let radius: Double init(radius: Double) { self.radius = radius } func draw() { print("Drawing circle of radius \(radius)") } var area: Double { .pi * radius * radius } } class Rectangle: Drawable { let width: Double let height: Double init(width: Double, height: Double) { self.width = width self.height = height } func draw() { print("Drawing rectangle \(width)×\(height)") } var area: Double { width * height } } // Abstraction in action let shapes: [any Drawable] = [ Circle(radius: 5), Rectangle(width: 4, height: 6) ] for shape in shapes { shape.draw() print("Area = \(String(format: "%.2f", shape.area))") } |
Key lesson: The caller doesn’t need to know whether it’s a circle or rectangle — it only needs to know it can draw() and has area.
Pillar 3 – Inheritance
“A child class can inherit properties & methods from a parent class”
Swift supports single inheritance (only one direct parent).
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
class Animal { let name: String init(name: String) { self.name = name } func makeSound() { print("\(name) makes a generic sound") } } class Dog: Animal { override func makeSound() { print("\(name) says Woof! 🐶") } func wagTail() { print("\(name) wags tail happily") } } class Cat: Animal { override func makeSound() { print("\(name) says Meow! 🐱") } } let dog = Dog(name: "Bruno") let cat = Cat(name: "Luna") dog.makeSound() // Bruno says Woof! 🐶 cat.makeSound() // Luna says Meow! 🐱 dog.wagTail() // Bruno wags tail happily |
Modern Swift note: Inheritance is used much less in modern Swift compared to 10 years ago. Developers now prefer protocol conformance + composition (has-a) over deep inheritance trees.
Pillar 4 – Polymorphism
“Many forms” — same method name, different behavior depending on the actual object
This happens automatically thanks to inheritance + protocols.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
let animals: [Animal] = [Dog(name: "Bruno"), Cat(name: "Luna"), Animal(name: "Generic")] for animal in animals { animal.makeSound() // Bruno says Woof! // Luna says Meow! // Generic makes a generic sound } |
Protocol-based polymorphism (modern favorite):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
protocol Speakable { func speak() } extension Dog: Speakable { func speak() { print("Woof!") } } extension Cat: Speakable { func speak() { print("Meow!") } } let speakers: [any Speakable] = [Dog(name: "Bruno"), Cat(name: "Luna")] speakers.forEach { $0.speak() } |
5. Real-life examples — OOP patterns you will actually write
Example 1 – UIViewController inheritance (UIKit classic)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class BaseViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() setupCommonUI() } func setupCommonUI() { view.backgroundColor = .systemBackground navigationItem.backBarButtonItem = UIBarButtonItem(title: "") } } class ProfileViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() title = "Profile" // add profile-specific UI } } |
Example 2 – Payment processor (very common in fintech / e-commerce)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
protocol PaymentProcessor { func process(amount: Double) throws -> String } class RazorpayProcessor: PaymentProcessor { func process(amount: Double) throws -> String { // simulate Razorpay API return "RZP_TXN_\(UUID().uuidString.prefix(8))" } } class PaytmProcessor: PaymentProcessor { func process(amount: Double) throws -> String { // simulate Paytm API return "PTM_\(Int(Date().timeIntervalSince1970))" } } class PaymentService { let processor: any PaymentProcessor init(processor: any PaymentProcessor) { self.processor = processor } func pay(amount: Double) { do { let transactionID = try processor.process(amount: amount) print("Payment successful! Transaction ID: \(transactionID)") } catch { print("Payment failed: \(error)") } } } // Usage — polymorphism in action let service = PaymentService(processor: RazorpayProcessor()) service.pay(amount: 999.99) |
6. Quick Summary — The Four Pillars in Swift
| Pillar | Main Swift tools | Real-life feeling |
|---|---|---|
| Encapsulation | private, fileprivate, computed properties | Hide internal state, expose safe methods |
| Abstraction | Protocols + protocol extensions | Define “what” not “how” |
| Inheritance | class Child: Parent | “is-a” relationship (Dog is an Animal) |
| Polymorphism | method overriding + protocol conformance | Same message → different behavior |
7. Small Practice – Try these
- Create a base class Vehicle with property speed and method move() Create two subclasses: Car and Bike that override move() with different messages
- Create a protocol Payable with method processPayment(amount:) Implement it for two payment gateways (Razorpay, Paytm)
- Create a class Employee with private salary Add public method giveRaise(percent:) that safely increases salary
Paste your code here if you want feedback or want to see more elegant versions!
What would you like to explore next?
- Protocol-oriented programming (POP) vs classic OOP
- Struct vs Class — when to choose which
- Inheritance vs composition (modern preference)
- Access control (private, fileprivate, internal, public, open)
- OOP in SwiftUI (ObservableObject, @StateObject…)
- Or move to another topic (optionals, arrays, closures, switch…)
Just tell me — we’ll continue in the same clear, detailed, patient style 😊
