Awesome Open Source
Awesome Open Source

The Principles of OOD in Swift 5

A short cheat-sheet with Playground (

👷 Project maintained by: @nsmeme (Oktawian Chojnacki)

❤️ Please consider supporting my work, become my Sponsor! 🙏

See my most popular project to date: Design-Patterns-In-Swift


🔐 The Single Responsibility Principle

A class should have one, and only one, reason to change. (read more)


protocol Openable {
    mutating func open()

protocol Closeable {
    mutating func close()

// I'm the door. I have an encapsulated state and you can change it using methods.
struct PodBayDoor: Openable, Closeable {

    private enum State {
        case open
        case closed

    private var state: State = .closed

    mutating func open() {
        state = .open

    mutating func close() {
        state = .closed

// I'm only responsible for opening, no idea what's inside or how to close.
final class DoorOpener {
    private var door: Openable

    init(door: Openable) {
        self.door = door

    func execute() {

// I'm only responsible for closing, no idea what's inside or how to open.
final class DoorCloser {
    private var door: Closeable

    init(door: Closeable) {
        self.door = door

    func execute() {

let door = PodBayDoor()

⚠ Only the DoorOpener is responsible for opening the door.

let doorOpener = DoorOpener(door: door)

⚠ If another operation should be made upon closing the door, like switching on the alarm, you don't have to change the DoorOpener class.

let doorCloser = DoorCloser(door: door)

✋ The Open Closed Principle

You should be able to extend a classes behavior, without modifying it. (read more)


protocol Shooting {
    func shoot() -> String

// I'm a laser beam. I can shoot.
final class LaserBeam: Shooting {
    func shoot() -> String {
        return "Ziiiiiip!"

// I have weapons and trust me I can fire them all at once. Boom! Boom! Boom!
final class WeaponsComposite {

    let weapons: [Shooting]

    init(weapons: [Shooting]) {
        self.weapons = weapons

    func shoot() -> [String] {
        return { $0.shoot() }

let laser = LaserBeam()
var weapons = WeaponsComposite(weapons: [laser])


I'm a rocket launcher. I can shoot a rocket.

⚠️ To add rocket launcher support I don't need to change anything in existing classes.

final class RocketLauncher: Shooting {
    func shoot() -> String {
        return "Whoosh!"

let rocket = RocketLauncher()

weapons = WeaponsComposite(weapons: [laser, rocket])

👥 The Liskov Substitution Principle

Derived classes must be substitutable for their base classes. (read more)


let requestKey: String = "NSURLRequestKey"

// I'm a NSError subclass. I provide additional functionality but don't mess with original ones.
class RequestError: NSError {

    var request: NSURLRequest? {
        return self.userInfo[requestKey] as? NSURLRequest

// I fail to fetch data and will return RequestError.
func fetchData(request: NSURLRequest) -> (data: NSData?, error: RequestError?) {

    let userInfo: [String:Any] = [requestKey : request]

    return (nil, RequestError(domain:"DOMAIN", code:0, userInfo: userInfo))

// I don't know what RequestError is and will fail and return a NSError.
func willReturnObjectOrError() -> (object: AnyObject?, error: NSError?) {

    let request = NSURLRequest()
    let result = fetchData(request: request)

    return (, result.error)

let result = willReturnObjectOrError()

// Ok. This is a perfect NSError instance from my perspective.
let error: Int? = result.error?.code

// But hey! What's that? It's also a RequestError! Nice!
if let requestError = result.error as? RequestError {

🍴 The Interface Segregation Principle

Make fine grained interfaces that are client specific. (read more)


// I have a landing site.
protocol LandingSiteHaving {
    var landingSite: String { get }

// I can land on LandingSiteHaving objects.
protocol Landing {
    func land(on: LandingSiteHaving) -> String

// I have payload.
protocol PayloadHaving {
    var payload: String { get }

// I can fetch payload from vehicle (ex. via Canadarm).

protocol PayloadFetching {
    func fetchPayload(vehicle: PayloadHaving) -> String

final class InternationalSpaceStation: PayloadFetching {

⚠ Space station has no idea about landing capabilities of SpaceXCRS8.

    func fetchPayload(vehicle: PayloadHaving) -> String {
        return "Deployed \(vehicle.payload) at April 10, 2016, 11:23 UTC"

// I'm a barge - I have landing site (well, you get the idea).
final class OfCourseIStillLoveYouBarge: LandingSiteHaving {
    let landingSite = "a barge on the Atlantic Ocean"

// I have payload and can land on things having landing site.
// I'm a very limited Space Vehicle, I know.
final class SpaceXCRS8: Landing, PayloadHaving {

    let payload = "BEAM and some Cube Sats"

⚠ CRS8 knows only about the landing site information.

    func land(on: LandingSiteHaving) -> String {
        return "Landed on \(on.landingSite) at April 8, 2016 20:52 UTC"

let crs8 = SpaceXCRS8()
let barge = OfCourseIStillLoveYouBarge()
let spaceStation = InternationalSpaceStation()

spaceStation.fetchPayload(vehicle: crs8) barge)

🔝 The Dependency Inversion Principle

Depend on abstractions, not on concretions. (read more)


protocol TimeTraveling {
    func travelInTime(time: TimeInterval) -> String

final class DeLorean: TimeTraveling {
	func travelInTime(time: TimeInterval) -> String {
		return "Used Flux Capacitor and travelled in time by: \(time)s"

final class EmmettBrown {
	private let timeMachine: TimeTraveling

⚠ Emmet Brown is given the DeLorean as a TimeTraveling device, not the concrete class DeLorean.

	init(timeMachine: TimeTraveling) {
		self.timeMachine = timeMachine

	func travelInTime(time: TimeInterval) -> String {
		return timeMachine.travelInTime(time: time)

let timeMachine = DeLorean()

let mastermind = EmmettBrown(timeMachine: timeMachine)
mastermind.travelInTime(time: -3600 * 8760)


📖 Descriptions from: The Principles of OOD by Uncle Bob

Get A Weekly Email With Trending Projects For These Topics
No Spam. Unsubscribe easily at any time.
swift (7,343) 
computer-science (146) 
playground (83) 
solid (36) 
object-oriented (35) 

Find Open Source By Browsing 7,000 Topics Across 59 Categories