Lecture 1 / 12
Lecture 01 Β· Fundamentals

Introduction to Swift & Setup

Beginner ~50 min

What is Swift?

Swift is a powerful, intuitive, and open-source programming language developed by Apple. It was designed to replace Objective-C, providing a more modern syntax while maintaining the same high level of performance.

Swift is used to build applications for iOS, macOS, watchOS, and tvOS. However, it is not limited to Apple devices; Swift is also used for server-side development and is available on Linux and Windows.

Why Swift?

Swift was built with three main goals: Safety, Speed, and Expressiveness.

  • Memory Safety: Swift uses Automatic Reference Counting (ARC) and eliminates entire classes of common coding errors, such as null pointer dereferences, through a feature called Optionals.
  • Clean Syntax: Swift's syntax is designed to be easy to read and write. It removes the need for semicolons and reduces boilerplate code, making it feel more like a scripting language (like Python) but with the power of a compiled language.
  • High Performance: Because it is compiled using the LLVM compiler, Swift's performance is comparable to C++ and Objective-C.
  • Modern UI Frameworks: Swift is the foundation for SwiftUI (the modern declarative framework for building user interfaces) and UIKit (the classic framework for iOS apps).

Installation & Setup

Depending on your hardware, there are different ways to get started with Swift:

1. For Mac Users (The Standard Way):
The best way to learn Swift is by downloading Xcode from the Mac App Store. Xcode is Apple's complete Integrated Development Environment (IDE). It includes the Swift compiler, a debugger, and the tools needed to build apps for Apple devices.

2. For Windows/Linux Users:
You can install the Swift toolchain directly from Swift.org. While you won't be able to build full iOS apps without a Mac, you can still learn the language and build server-side or command-line tools.

The Magic of Swift Playgrounds

If you are using a Mac or iPad, I highly recommend starting with Swift Playgrounds. Playgrounds are interactive environments where you can write Swift code and see the results immediately without having to compile and run a full project. They are perfect for experimentation and learning.

Your First Swift Program

Swift is designed to be concise. You don't need a "Main" class or a complex wrapper just to print a message. In a Swift file or Playground, you can simply write:

main.swift
print("Hello, Swift Mastery!")
print("Ready to build amazing iOS apps!")
Output
Hello, Swift Mastery!
Ready to build amazing iOS apps!

Breaking Down the Code

  • print(): A built-in function that outputs text to the console. Unlike some other languages, you don't need to import any libraries to use the basic print function.
  • String Literals: Text is wrapped in double quotes. Swift handles strings as highly optimized objects that support powerful features like String Interpolation (e.g., "Hello, \(name)").
  • No Semicolons: You'll notice there are no semicolons at the end of the lines. While you can use them, they are not required and are generally avoided in the Swift community.

πŸ’» Try It Yourself - Multi-Language Compiler

Practice Swift and many other programming languages right here in your browser! Switch between languages, modify the code, and click "Run" to see results instantly.

πŸ’‘ Practice Tips:

  • Switch to Swift in the language selector and try the iOS development examples
  • Experiment with Swift's type safety and optionals
  • Try other mobile languages like Kotlin, Dart, or compare with C#
  • Use the "Load Example" button to see Swift-specific code samples
  • Use Ctrl+Enter to quickly run your code
🎯 Exercise 1.1

Set up your environment:

  1. Install Xcode (Mac) or the Swift Toolchain (Windows/Linux).
  2. Create a new project using the macOS Command Line Tool template.
  3. Modify the code to print your name and your favorite Apple device.
  4. Run the project and verify the output.

🎯 Exercise 1.2

Try using a Swift Playground. Create a new playground file and write five different print statements that tell a short story about why you want to learn Swift.

Pro Tip: If you are on a Mac, get comfortable with the keyboard shortcut Cmd + R. This is the universal "Run" command in Xcode that will execute your code and show you the output in the debug console.

Lecture 02 Β· Fundamentals

Variables & Data Types

Beginner ~45 min

Introduction to Variables

Variables are used to store data in memory. In Swift, variables and constants are designed to be safe, fast, and easy to understand.

Every variable stores a value of a specific type such as numbers, text, or boolean values.

let vs var

Use let for constants and var for variables. Swift encourages the use of constants whenever possible.

let pi = 3.14159
var score = 0
score += 10

In the example above:

  • pi is a constant and cannot be changed
  • score is mutable and can be updated

Why Swift Encourages Constants

Using constants improves code safety and readability. If a value should never change, using let prevents accidental modifications.

This approach helps reduce bugs in large applications.

Modifying Variables

Variables declared with var can change during program execution.

var lives = 3

lives -= 1

print(lives)

Output:

2

Constants Cannot Change

Trying to modify a constant causes a compile-time error.

let appName = "SwiftUI"

// appName = "iOS" // Error

Constants improve predictability because their values remain fixed.

Type Inference

Swift automatically detects data types using type inference.

let age = 20
let price = 19.99
let language = "Swift"

The compiler determines the correct types automatically.

Explicit Type Annotations

You can explicitly define types when needed.

let age: Int = 20
let price: Double = 19.99
let name: String = "Alex"

Explicit typing improves readability in complex programs.

Common Swift Data Types

Type Description Example
Int Whole numbers 10
Double Decimal numbers 3.14
String Text values "Hello"
Bool True or false values true
Character Single character "A"

Working with Strings

Strings store text values and are surrounded by double quotation marks.

let message = "Welcome to Swift"

print(message)

Strings are commonly used for user interfaces, messages, and data processing.

String Interpolation

Swift supports string interpolation using \().

let name = "John"

print("Hello \(name)")

Output:

Hello John

Boolean Values

Boolean variables can only store true or false.

let isLoggedIn = true
let isAdmin = false

Booleans are mainly used for decision-making and conditions.

Numeric Types

Swift provides several numeric types depending on size and precision.

Type Description
Int Whole numbers
Float Single-precision decimals
Double Double-precision decimals

Double is commonly used because it provides greater precision.

Type Safety

Swift is a type-safe language, meaning variables must contain the correct data type.

let age = 20

// age = "Twenty" // Error

This helps catch programming errors early.

Type Conversion

Swift requires explicit type conversion between different numeric types.

let number = 10
let decimal = Double(number)

print(decimal)

Common conversion functions include:

  • Int()
  • Double()
  • Float()
  • String()

Optionals

Optionals allow variables to contain either a value or nil.

var nickname: String? = nil

The question mark indicates the variable may contain no value.

Optional Binding

Optional binding safely unwraps optional values.

var username: String? = "SwiftUser"

if let name = username {
    print(name)
}

This safely checks whether the optional contains a value.

Tuples

Tuples allow multiple values to be grouped together.

let person = ("Alex", 20)

print(person.0)
print(person.1)

Constants for Configuration

Constants are commonly used for application settings and configuration values.

let maxPlayers = 4
let appVersion = "1.0.0"

Naming Conventions

Swift uses camelCase naming style for variables and constants.

  • Use meaningful names
  • Avoid unnecessary abbreviations
  • Keep names readable
let userName = "Alice"
var totalScore = 100

Common Beginner Mistakes

  • Using var when let is enough
  • Forgetting optional handling
  • Using incorrect type conversions
  • Trying to modify constants
  • Confusing Float and Double

Practice Exercise

Create:

  • A constant storing your name
  • A variable storing your score
  • A boolean indicating if you are learning Swift
  • An optional nickname variable
let name = "Alex"
var score = 50
let learningSwift = true
var nickname: String? = nil

score += 25

print(name)
print(score)

Summary

In this lecture, you learned:

  • The difference between let and var
  • How Swift handles data types
  • How type inference works
  • How optionals improve safety
  • How to work with strings, numbers, and booleans
Lecture 03 Β· Fundamentals

Operators & Control Flow

Beginner ~50 min

Basic Operators

Operators are symbols used to perform operations on variables and values. Swift provides a rich set of operators to handle everything from simple math to complex logic.

1. Arithmetic Operators

Used for mathematical calculations:

  • + (Addition), - (Subtraction)
  • * (Multiplication), / (Division)
  • % (Remainder/Modulo)

2. Comparison & Logical Operators

Used to compare values and return a Boolean (true or false):

  • Comparison: == (Equal), != (Not Equal), <, >, <=, >=
  • Logical: && (AND), || (OR), ! (NOT)
main.swift
let a = 10
let b = 3

print(a % b)      // 1 (Remainder of 10/3)
print(a > 5 && b < 5) // true (Both conditions are true)

Conditional Branching: If / Else

The if statement allows you to execute a block of code only if a certain condition is met. In Swift, parentheses around the condition are optional.

main.swift
let temperature = 25

if temperature > 30 {
    print("It's a hot day!")
} else if temperature > 15 {
    print("The weather is pleasant.")
} else {
    print("It's cold outside!")
}

Switch Statements

Swift's switch is extremely powerful. Unlike many other languages, Swift's switch does not require a break statement to prevent "fall-through"β€”it only executes the first matching case and then exits.

Important: Every switch statement in Swift must be exhaustive, meaning you must cover every possible value or provide a default case.

main.swift
let grade = "B"

switch grade {
case "A": 
    print("Excellent!")
case "B", "C": 
    print("Good job!") // Comma allows multiple values
case "D"..."F": 
    print("Keep trying") // Range matching
default: 
    print("Invalid grade")
}

Loops & Iteration

Loops allow you to repeat a block of code multiple times.

1. For-In Loop

The most common loop in Swift. It is used to iterate over a sequence, such as a range of numbers or an array.

main.swift
// Iterating through a range
for i in 1...5 {
    print("Number \(i)")
}

// Iterating through an array
let fruits = ["Apple", "Banana", "Orange"]
for fruit in fruits {
    print("I love \(fruit)s!")
}

2. While & Repeat-While Loops

Use while when you want to check the condition before running the code. Use repeat-while (similar to do-while in other languages) when you want the code to run at least once.

main.swift
var counter = 3

while counter > 0 {
    print("Countdown: \(counter)")
    counter -= 1
}

repeat {
    print("This runs at least once!")
} while counter > 0
🎯 Exercise 3.1

Write a program that uses a for-in loop to print numbers from 1 to 20. However, use an if statement inside the loop to only print the number if it is even.

Lecture 04 Β· Fundamentals

Optionals & Unwrapping

Beginner ~60 min

Introduction to Optionals

One of Swift’s most powerful safety features is the Optional type. Optionals allow variables to either contain a value or contain no value at all.

In Swift, the absence of a value is represented by nil.

Optionals help prevent one of the most common programming errors: accessing null values unexpectedly.

Why Optionals Matter

In many programming languages, null-related errors can crash applications. Swift solves this problem by forcing developers to safely handle missing values.

This makes Swift applications more stable and reliable.

Declaring Optionals

An optional is declared using a question mark ? after the type.

var username: String? = "SwiftUser"
var age: Int? = 20

The variables above may contain a value or nil.

Dealing with Nil

Optionals are a type that can either hold a value or nil.

var name: String? = "Tim"

if let actualName = name {
    print(actualName)
}

In this example:

  • name is an optional string
  • if let safely unwraps the value
  • actualName becomes available only if a value exists

Understanding Nil

nil means β€œno value”.

var nickname: String? = nil

This variable currently contains no string value.

Optional Binding

Optional binding safely checks and unwraps optionals.

var city: String? = "Tokyo"

if let currentCity = city {
    print("City is \(currentCity)")
} else {
    print("No city found")
}

Optional binding is the safest and most common way to handle optionals.

Force Unwrapping

Force unwrapping uses the exclamation mark ! to access an optional value directly.

var language: String? = "Swift"

print(language!)

Output:

Swift

Dangers of Force Unwrapping

If the optional contains nil, force unwrapping crashes the application.

var username: String? = nil

// print(username!) // Crash

Force unwrapping should only be used when you are absolutely sure the value exists.

Implicitly Unwrapped Optionals

Implicitly unwrapped optionals use ! in the declaration.

var email: String! = "test@example.com"

print(email)

These optionals automatically unwrap when accessed.

When to Use Implicitly Unwrapped Optionals

They are mainly used when a value starts as nil but will definitely contain a value later.

This commonly occurs in iOS interface elements.

Nil Coalescing Operator

The nil coalescing operator ?? provides a default value if the optional is nil.

var username: String? = nil

print(username ?? "Guest")

Output:

Guest

Optional Chaining

Optional chaining safely accesses properties or methods on optionals.

var message: String? = "Hello"

print(message?.count)

If the value is nil, Swift safely returns nil instead of crashing.

Optional Chaining with Classes

class User {
    var name = "Alex"
}

var user: User? = User()

print(user?.name)

Guard Statements

guard let is another way to safely unwrap optionals.

func greet(name: String?) {

    guard let actualName = name else {
        print("No name provided")
        return
    }

    print("Hello \(actualName)")
}

guard improves readability by handling failure cases early.

Multiple Optional Bindings

You can unwrap multiple optionals simultaneously.

var firstName: String? = "John"
var lastName: String? = "Doe"

if let first = firstName,
   let last = lastName {

    print("\(first) \(last)")
}

Comparing Optionals to Nil

You can directly check whether an optional contains a value.

if name != nil {
    print("Name exists")
}

However, optional binding is usually preferred.

Optional Arrays

Collections can also be optional.

var numbers: [Int]? = [1, 2, 3]

print(numbers?.count)

Common Beginner Mistakes

  • Force unwrapping nil values
  • Confusing optional types with normal types
  • Forgetting optional binding
  • Ignoring nil safety checks
  • Using implicitly unwrapped optionals unnecessarily

Best Practices

  • Prefer optional binding over force unwrapping
  • Use guard let for cleaner code
  • Use ?? for default values
  • Only use force unwrapping when completely safe

Practice Exercise

Create:

  • An optional string variable
  • A safe optional binding example
  • A nil coalescing example
  • An optional chaining example
var username: String? = "SwiftCoder"

if let actualUser = username {
    print(actualUser)
}

print(username ?? "Guest")

Summary

In this lecture, you learned:

  • What optionals are in Swift
  • How to safely unwrap optionals
  • The dangers of force unwrapping
  • How optional chaining works
  • How nil safety improves app stability
Lecture 05 Β· Fundamentals

Loops & Collections

Beginner ~45 min

Collections are used to group multiple values together. Swift provides three primary collection types: Arrays, Sets, and Dictionaries. Choosing the right one depends on whether you care about the order of elements or if the values must be unique.

Arrays

An Array is an ordered collection of values of the same type. Arrays are the most common way to store lists of data.

main.swift
// Declaring an array of strings
var fruits = ["Apple", "Banana", "Cherry"]

// Adding an element
fruits.append("Mango")

// Accessing by index (starts at 0)
print(fruits[0]) // "Apple"

// Iterating through an array
for fruit in fruits {
    print("I love \(fruit)s!")
}

Sets

A Set is an unordered collection of unique values. If you try to add a duplicate value to a set, it will be ignored. Sets are extremely fast for checking if an item exists.

main.swift
// A set of unique IDs
var userIds: Set = [101, 102, 103, 101] 

print(userIds) // [101, 103, 102] (Order may vary, 101 appears only once)

// Checking for existence (Very fast in Sets)
if userIds.contains(102) {
    print("User exists!")
}

Dictionaries

A Dictionary stores associations between keys and values. Each key must be unique, and they are used to look up values quickly (like a real-life dictionary or a phonebook).

main.swift
// Key: String, Value: Double
var heights = ["Taylor": 1.78, "Ed": 1.73]

// Updating a value
heights["Taylor"] = 1.80

// Adding a new key-value pair
heights["Ariana"] = 1.53

// Iterating over a dictionary
for (name, height) in heights {
    print("\(name) is \(height)m tall")
}

Pro Tip: When you access a dictionary by key, Swift returns an Optional because the key might not exist. You'll need to unwrap it (which we will cover in the Optionals lecture).

Common Collection Methods

Swift provides powerful built-in methods to manipulate collections without using manual loops:

  • filter: Returns a new collection containing only elements that satisfy a condition.
  • map: Transforms every element in the collection into something else.
  • sorted: Returns the elements in ascending or descending order.
main.swift
let numbers = [1, 5, 2, 8, 3]

// Only keep numbers greater than 3
let filtered = numbers.filter { $0 > 3 } // [5, 8]

// Double every number
let doubled = numbers.map { $0 * 2 } // [2, 10, 4, 16, 6]

print(numbers.sorted()) // [1, 2, 3, 5, 8]
🎯 Exercise 5.1

Create a "Contact Book" program:

  1. Create a Dictionary where the Key is a person's name and the Value is their phone number.
  2. Add 3 contacts to the dictionary.
  3. Use a for-in loop to print all contacts in the format: "Contact: [Name] - Phone: [Number]".
  4. Try adding a duplicate name and observe what happens to the phone number.

Lecture 06 Β· Core Concepts

Functions & Closures

Intermediate ~60 min

Introduction to Functions

Functions are reusable blocks of code designed to perform specific tasks. They help organize programs into smaller, manageable sections.

Instead of repeating the same code multiple times, developers create functions and call them whenever needed.

Why Functions Matter

Functions improve:

  • Code readability
  • Code reuse
  • Program organization
  • Maintainability

Functions are one of the most important building blocks in Swift programming.

Creating Functions

Functions are created using the func keyword.

func sayHello() {
    print("Hello!")
}

Calling the function:

sayHello()

Function Parameters

Functions can receive input values called parameters.

func greet(name: String) {
    print("Hello \(name)")
}

Calling the function:

greet(name: "Alex")

Functions Returning Values

Functions can return values using the -> syntax.

func square(number: Int) -> Int {
    return number * number
}

Using the returned value:

let result = square(number: 5)

print(result)

Output:

25

Functions & Closures

Functions can accept parameters and return values.

func greet(person: String) -> String {
    return "Hello, \(person)!"
}

Calling the function:

let message = greet(person: "Tim")

print(message)

External and Internal Parameter Names

Swift functions support external and internal parameter names.

func multiply(by number: Int) {
    print(number * 2)
}

multiply(by: 5)

The external name improves readability when calling the function.

Omitting External Parameter Names

Use an underscore _ to remove the external parameter label.

func add(_ a: Int, _ b: Int) -> Int {
    return a + b
}

print(add(2, 3))

Default Parameter Values

Functions can define default parameter values.

func welcome(name: String = "Guest") {
    print("Welcome \(name)")
}

welcome()
welcome(name: "John")

Multiple Return Values

Swift functions can return multiple values using tuples.

func getUser() -> (String, Int) {
    return ("Alex", 20)
}

let user = getUser()

print(user.0)
print(user.1)

Nested Functions

Functions can exist inside other functions.

func outerFunction() {

    func innerFunction() {
        print("Inside inner function")
    }

    innerFunction()
}

What are Closures?

Closures are self-contained blocks of functionality that can be passed around and used in code.

Closures are similar to functions but are more lightweight and flexible.

Basic Closure Syntax

let greeting = {
    print("Hello from closure")
}

greeting()

The closure is stored inside a variable and called using parentheses.

Closures with Parameters

Closures can accept parameters and return values.

let multiply = { (a: Int, b: Int) -> Int in
    return a * b
}

print(multiply(3, 4))

Output:

12

The in Keyword

The in keyword separates closure parameters from the closure body.

{ (parameters) -> ReturnType in
    // Closure body
}

Trailing Closures

If the final parameter of a function is a closure, Swift allows trailing closure syntax.

func performTask(action: () -> Void) {
    action()
}

performTask {
    print("Task completed")
}

Trailing closures improve readability.

Closures in Collection Functions

Closures are commonly used with collection methods such as map() and filter().

let numbers = [1, 2, 3, 4]

let doubled = numbers.map {
    $0 * 2
}

print(doubled)

Output:

[2, 4, 6, 8]

Shorthand Argument Names

Swift closures provide shorthand argument names such as $0, $1, and so on.

let sum = { $0 + $1 }

print(sum(5, 3))

Capturing Values

Closures can capture and remember values from their surrounding scope.

func makeCounter() -> () -> Int {

    var count = 0

    return {
        count += 1
        return count
    }
}

The closure remembers the count variable even after the function finishes.

Anonymous Functions

Closures are often called anonymous functions because they usually do not have names.

They are heavily used in SwiftUI and asynchronous programming.

Common Beginner Mistakes

  • Forgetting parameter labels
  • Confusing functions with closures
  • Using force unwraps inside closures unsafely
  • Incorrect closure syntax
  • Not understanding shorthand arguments

Best Practices

  • Keep functions focused on one task
  • Use descriptive function names
  • Prefer closures for short operations
  • Avoid overly large functions
  • Use trailing closures for readability

Practice Exercise

Create:

  • A function that adds two numbers
  • A function returning a greeting message
  • A closure that multiplies two integers
  • A trailing closure example
func subtract(a: Int, b: Int) -> Int {
    return a - b
}

let result = subtract(a: 10, b: 3)

print(result)

Summary

In this lecture, you learned:

  • How functions work in Swift
  • How parameters and return values operate
  • How closures work
  • How trailing closures improve readability
  • How closures interact with collections
Lecture 07 Β· Core Concepts

Structs, Classes & Enums

Intermediate ~70 min

Introduction

Swift provides powerful ways to model and organize data using structs, classes, and enums. These are fundamental building blocks used throughout Swift development, especially in iOS and macOS applications.

Although structs, classes, and enums may appear similar at first, each one serves a different purpose and follows different behavior rules.

Structs vs Classes

Structs are value types (copied), Classes are reference types (shared).

This difference is one of the most important concepts in Swift programming.

What Is a Struct?

A struct is a value type used to group related properties and functionality together.

struct User {

    var name: String

    var age: Int
}

Structs are commonly used for lightweight models and immutable data.

Creating Struct Instances

var user1 = User(name: "John", age: 25)

print(user1.name)

Swift automatically creates a memberwise initializer for structs.

Value Type Behavior

When a struct is assigned to another variable, Swift creates a copy.

var user1 = User(name: "John", age: 25)

var user2 = user1

user2.name = "Alice"

print(user1.name)
print(user2.name)

Changing user2 does not affect user1 because they are separate copies.

What Is a Class?

A class is a reference type that supports shared instances, inheritance, and object-oriented programming features.

class Employee {

    var name: String

    init(name: String) {
        self.name = name
    }
}

Classes are commonly used when multiple parts of an application need to share the same object.

Reference Type Behavior

Unlike structs, classes do not create copies during assignment.

var emp1 = Employee(name: "David")

var emp2 = emp1

emp2.name = "Emma"

print(emp1.name)
print(emp2.name)

Both variables point to the same object in memory, so changes affect both references.

When to Use Structs

Structs are recommended when:

  • The data is simple.
  • You want safer copying behavior.
  • The object does not require inheritance.
  • You want immutable models.
  • Performance and safety are priorities.

When to Use Classes

Classes are preferred when:

  • Objects need shared mutable state.
  • Inheritance is required.
  • The object lifecycle must be managed carefully.
  • You need identity comparison.

Properties in Structs and Classes

Both structs and classes can contain properties.

struct Car {

    var brand: String

    var speed: Int
}

Properties store values associated with the object.

Methods

Methods are functions that belong to structs or classes.

struct Rectangle {

    var width: Double

    var height: Double

    func area() -> Double {

        return width * height
    }
}

Methods help organize related behavior inside the type.

Mutating Methods

Struct methods that modify properties must use the mutating keyword.

struct Counter {

    var value = 0

    mutating func increment() {

        value += 1
    }
}

This tells Swift that the method changes the struct’s internal state.

Inheritance in Classes

Classes support inheritance, allowing one class to inherit properties and methods from another class.

class Animal {

    func sound() {
        print("Animal sound")
    }
}

class Dog: Animal {

    override func sound() {
        print("Bark")
    }
}

Structs do not support inheritance.

Identity Operators

Classes support identity comparison using ===.

if emp1 === emp2 {
    print("Same object")
}

This checks whether two variables reference the exact same instance.

Introduction to Enums

Enums define a group of related values. Swift enums are far more powerful than traditional enums in many languages.

enum Direction {

    case north

    case south

    case east

    case west
}

A variable can only contain one enum case at a time.

Using Enums

var move = Direction.north

Enums improve code readability and reduce invalid values.

Switch Statements with Enums

Enums work extremely well with switch statements.

switch move {

case .north:
    print("Going North")

case .south:
    print("Going South")

case .east:
    print("Going East")

case .west:
    print("Going West")
}

Swift requires all enum cases to be handled.

Associated Values

Enums can also store additional associated data.

enum Result {

    case success(String)

    case failure(String)
}

This makes enums extremely flexible for representing application states.

Raw Values

Enums can contain raw values such as strings or integers.

enum Status: Int {

    case pending = 1

    case completed = 2
}

Raw values are useful when working with APIs or databases.

Benefits of Structs, Classes & Enums

  • Improve code organization.
  • Create reusable data models.
  • Support safer programming patterns.
  • Reduce bugs and invalid states.
  • Make applications easier to maintain.

Common Beginner Mistakes

  • Using classes when structs are better.
  • Forgetting that structs are copied.
  • Mutating structs without the mutating keyword.
  • Confusing identity and equality.
  • Not handling all enum cases in switch statements.

Practice

Practice

Create a struct called Student with properties for name and grade.

Create a class called Teacher with a subject property.

Then create an enum called TrafficLight with cases:

  • red
  • yellow
  • green

Use a switch statement to print actions for each traffic light state.

Summary

Structs, classes, and enums are essential parts of Swift programming. Structs provide safe value semantics, classes support shared object behavior and inheritance, while enums create powerful state representations.

Mastering these concepts is critical for building scalable, maintainable, and professional Swift applications.

Lecture 08 Β· Core Concepts

Protocols & Extensions

Intermediate ~50 min

Introduction to Protocol-Oriented Programming

Swift strongly encourages a programming style known as Protocol-Oriented Programming (POP).

Instead of relying heavily on inheritance, Swift promotes building flexible and reusable code using protocols and extensions.

  • Improves code reusability
  • Encourages modular design
  • Reduces complex inheritance chains
  • Makes applications easier to maintain

What Are Protocols?

Protocols define a blueprint of methods, properties, and behaviors that classes, structs, or enums can adopt.

A protocol does not provide implementation details directly. It only defines requirements.

Creating a Protocol

protocol Drivable {

    var speed: Int { get set }

    func drive()
}

Any type adopting this protocol must provide the required property and method.

Adopting a Protocol

struct Car: Drivable {

    var speed = 120

    func drive() {
        print("Car is driving")
    }
}

Protocol Properties

Protocols can require computed or stored properties.

protocol Person {

    var name: String { get }
}

The get keyword means the property must be readable.

Read-Write Properties

protocol Employee {

    var salary: Double { get set }
}

get set means the property must support both reading and writing.

Protocol Methods

Protocols commonly define required behaviors using methods.

protocol Greetable {

    func greet()
}

Multiple Protocol Conformance

A type can adopt multiple protocols simultaneously.

protocol Flyable {
    func fly()
}

protocol Swimmable {
    func swim()
}

struct Duck: Flyable, Swimmable {

    func fly() {
        print("Flying")
    }

    func swim() {
        print("Swimming")
    }
}

Protocols with Classes

Classes can also conform to protocols.

protocol Animal {

    func makeSound()
}

class Dog: Animal {

    func makeSound() {
        print("Bark")
    }
}

Introduction to Extensions

Extensions allow developers to add new functionality to existing types without modifying the original source code.

Extensions are heavily used in Swift development.

Creating Extensions

extension String {

    func shout() -> String {
        return self.uppercased()
    }
}

print("swift".shout())

Adding Computed Properties

Extensions can add computed properties to existing types.

extension Int {

    var squared: Int {
        return self * self
    }
}

print(5.squared)

Protocol Extensions

Swift allows protocols to provide default method implementations using extensions.

protocol Printable {

    func printDetails()
}

extension Printable {

    func printDetails() {
        print("Default printing")
    }
}

Types adopting the protocol automatically receive the default behavior.

Using Default Implementations

struct User: Printable {

}

let u = User()

u.printDetails()

Benefits of Protocol Extensions

  • Reduce duplicate code
  • Provide reusable default behavior
  • Improve code organization
  • Encourage cleaner architecture

Mutating Methods in Protocols

Structs and enums require the mutating keyword when methods modify properties.

protocol Counter {

    mutating func increment()
}

Extensions on Custom Types

struct Rectangle {

    var width: Double
    var height: Double
}

extension Rectangle {

    func area() -> Double {
        return width * height
    }
}

Protocol Composition

Swift allows combining multiple protocols into one requirement.

protocol A {
    func methodA()
}

protocol B {
    func methodB()
}

func execute(value: A & B) {

}

Real-World Use Cases

  • Networking services
  • Reusable UI components
  • Data validation systems
  • Custom utility libraries
  • Flexible app architecture

Best Practices

  • Prefer protocols over deep inheritance
  • Keep protocols focused and small
  • Use protocol extensions for shared behavior
  • Choose meaningful protocol names
  • Avoid overly large protocols

Common Mistakes

  • Creating protocols with too many responsibilities
  • Overusing inheritance instead of protocols
  • Adding unnecessary extensions
  • Ignoring protocol composition
  • Writing duplicate default implementations

Mini Practice

Try creating the following:

  • A protocol with properties and methods
  • A struct conforming to a protocol
  • An extension for the String type
  • A protocol extension with default behavior
  • A protocol composition example
Lecture 09 Β· Advanced

Error Handling & Generics

Advanced ~60 min
do {
    try fetchData()
} catch {
    print("Error: \(error)")
}
Lecture 10 Β· Advanced

Property Wrappers & Concurrency

Advanced ~55 min

Modern Swift uses async and await for concurrent code.

Lecture 11 Β· Advanced

SwiftUI Basics

Advanced ~60 min

Declarative UI

struct ContentView: View {
    var body: some View {
        Text("Hello SwiftUI")
    }
}
Lecture 12 Β· Capstone

Capstone Project: iOS App

Advanced ~180 min

Create a full SwiftUI application that fetches data from an API and displays it in a beautiful list with detailed views.

// Project goals:
// 1. Model your data with Codable
// 2. Use URLSession for networking
// 3. Build a multi-view SwiftUI navigation
// 4. Implement State and Binding
Lecture 13 Β· Advanced

SwiftUI Navigation & State Management

Intermediate ~50 min Requires: Lecture 12

Content coming soon...

Lecture 14 Β· Advanced

Networking & URLSession

Advanced ~50 min Requires: Lecture 13

Content coming soon...

Lecture 15 Β· Advanced

Core Data & Persistence

Advanced ~55 min Requires: Lecture 14

Content coming soon...

Lecture 16 Β· Professional

Final Project β€” Complete iOS App

Advanced ~90 min Requires: All Previous

Content coming soon...