Introduction to Swift & Setup
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:
print("Hello, Swift Mastery!") print("Ready to build amazing iOS apps!")
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
Set up your environment:
- Install Xcode (Mac) or the Swift Toolchain (Windows/Linux).
- Create a new project using the macOS Command Line Tool template.
- Modify the code to print your name and your favorite Apple device.
- Run the project and verify the output.
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.
Variables & Data Types
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:
piis a constant and cannot be changedscoreis 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
varwhenletis enough - Forgetting optional handling
- Using incorrect type conversions
- Trying to modify constants
- Confusing
FloatandDouble
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
letandvar - How Swift handles data types
- How type inference works
- How optionals improve safety
- How to work with strings, numbers, and booleans
Operators & Control Flow
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)
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.
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.
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.
// 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.
var counter = 3 while counter > 0 { print("Countdown: \(counter)") counter -= 1 } repeat { print("This runs at least once!") } while counter > 0
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.
Optionals & Unwrapping
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:
nameis an optional stringif letsafely unwraps the valueactualNamebecomes 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 letfor 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
Loops & Collections
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.
// 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.
// 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).
// 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.
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]
Create a "Contact Book" program:
- Create a Dictionary where the Key is a person's name and the Value is their phone number.
- Add 3 contacts to the dictionary.
- Use a
for-inloop to print all contacts in the format:"Contact: [Name] - Phone: [Number]". - Try adding a duplicate name and observe what happens to the phone number.
Functions & Closures
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
Structs, Classes & Enums
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
mutatingkeyword. - Confusing identity and equality.
- Not handling all enum cases in switch statements.
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.
Protocols & Extensions
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
Error Handling & Generics
do { try fetchData() } catch { print("Error: \(error)") }
Property Wrappers & Concurrency
Modern Swift uses async and await for concurrent code.
SwiftUI Basics
Declarative UI
struct ContentView: View { var body: some View { Text("Hello SwiftUI") } }
Capstone Project: iOS App
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
SwiftUI Navigation & State Management
Content coming soon...
Networking & URLSession
Content coming soon...
Core Data & Persistence
Content coming soon...
Final Project β Complete iOS App
Content coming soon...