Swift Programming

Swift is a modern, type-safe, and high-performance programming language developed by Apple. It is designed for building apps across iOS, macOS, watchOS, tvOS, and visionOS, and can also be used for server-side development.

This guide covers the essential concepts of Swift, starting from basic syntax to more advanced features.


1. Variables and Constants

Swift uses var to declare variables (values that can change) and let to declare constants (values that cannot change once set).

// Variable (mutable)
var score = 10
score = 15 // This is allowed

// Constant (immutable)
let pi = 3.14159
// pi = 3.14 // This will cause a compiler error

Type Inference

Swift is a statically typed language, but you rarely need to write types explicitly because Swift infers them from the initial value.

let languageName = "Swift" // Infered as String
var version = 5.9          // Infered as Double

If you want to specify the type explicitly, use type annotation:

let greeting: String = "Hello"
let year: Int = 2024

2. Data Types

Here are some of the most commonly used basic data types in Swift:

TypeDescriptionExample
IntInteger numbers (whole numbers)let age: Int = 25
Double64-bit floating-point numberslet price: Double = 19.99
Float32-bit floating-point numberslet altitude: Float = 120.5
BoolBoolean values (true or false)let isCompleted: Bool = true
StringA sequence of characterslet name: String = "Alice"
CharacterA single characterlet grade: Character = "A"

String Interpolation

You can insert variables or constants directly into strings using \(variableName):

let user = "Sarah"
let message = "Welcome back, \(user)!"
print(message) // Output: Welcome back, Sarah!

3. Control Flow

Conditionals (if, else if, else)

Conditionals in Swift do not require parentheses around the condition.

let temperature = 25

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

Switch Statement

Switch statements in Swift must be exhaustive, meaning they must cover every possible value. If they cannot, a default case is required. Unlike other C-based languages, Swift does not require a break at the end of each case; it exits the switch block automatically.

let direction = "North"

switch direction {
case "North":
    print("Heading North")
case "South":
    print("Heading South")
case "East", "West":
    print("Heading East or West")
default:
    print("Unknown direction")
}

Loops

Swift offers standard looping mechanisms.

For-In Loops

Used to iterate over ranges, arrays, or dictionaries.

// Range includes 5 (closed range)
for index in 1...5 {
    print("Number \(index)")
}

// Range excludes 5 (half-open range)
for index in 1..<5 {
    print("Count \(index)")
}

While Loops

Runs as long as a condition is true.

var countdown = 3
while countdown > 0 {
    print("\(countdown)...")
    countdown -= 1
}
print("Go!")

4. Collections

Swift provides three primary collection types: Arrays, Sets, and Dictionaries.

Arrays

An ordered collection of elements.

// Creating an array of strings
var fruits = ["Apple", "Banana", "Orange"]

// Adding elements
fruits.append("Mango")

// Accessing elements (0-indexed)
let firstFruit = fruits[0] // "Apple"

// Modifying an element
fruits[1] = "Blueberry"

Sets

An unordered collection of unique elements.

var uniqueNumbers: Set = [1, 2, 3, 3, 2, 1]
print(uniqueNumbers) // Output: [1, 2, 3] (order may vary)

Dictionaries

Key-value pairs where keys must be unique.

var capitals = [
    "USA": "Washington D.C.",
    "UK": "London",
    "France": "Paris"
]

// Accessing values
let usCapital = capitals["USA"] // Returns Optional("Washington D.C.")

// Adding/Updating values
capitals["Japan"] = "Tokyo"

5. Functions

Functions are defined using the func keyword. They can have parameters, return values, and customizable argument labels.

Basic Function

func greet() {
    print("Hello, world!")
}
greet() // Calling the function

Parameters and Return Values

func greetUser(name: String) -> String {
    return "Hello, \(name)!"
}

let greeting = greetUser(name: "Taylor")
print(greeting) // Output: Hello, Taylor!

Argument Labels

Swift allows you to specify both an external label (used when calling the function) and an internal name (used inside the function body). You can also use _ to omit the label entirely when calling.

// `to` is the external label, `name` is the internal parameter name
func sendGreeting(to name: String) {
    print("Hello, \(name)")
}

sendGreeting(to: "Alex") // "to" is used here

// Omitting the argument label
func square(_ number: Int) -> Int {
    return number * number
}

let result = square(5) // No argument label needed

6. Optionals

An Optional represents a variable that can either hold a value or be nil (meaning no value). Swift uses optionals to handle the absence of a value safely.

var middleName: String? = "John"
middleName = nil // This is allowed because it is an optional

You must "unwrap" an optional before you can use its underlying value safely.

1. Optional Binding (if let)

This is a safe way to check if an optional contains a value and extract it into a temporary constant.

let possibleScore: Int? = 95

if let actualScore = possibleScore {
    print("Your score is \(actualScore).") // Safe to use here
} else {
    print("No score recorded.")
}

2. Guard Statement (guard let)

guard let is used for early exits in functions. It checks if the optional has a value; if not, the else block runs, which must exit the function.

func printScore(score: Int?) {
    guard let actualScore = score else {
        print("No score found.")
        return
    }
    
    // actualScore is available for the rest of the function scope
    print("Score is: \(actualScore)")
}

3. Nil-Coalescing Operator (??)

Provides a default fallback value if the optional is nil.

let username: String? = nil
let displayName = username ?? "Guest"
print(displayName) // Output: Guest

7. Structures and Classes

Structures (struct) and Classes (class) are the building blocks of most program code.

FeatureStructures (struct)Classes (class)
TypeValue Type (copied when passed around)Reference Type (shared reference)
InheritanceDoes not support inheritanceCan inherit from another class
DeinitializerNoYes (deinit)
Use caseSimple data containers, stateless dataState-heavy data, shared state

Struct Example

struct Book {
    var title: String
    var author: String
}

var book1 = Book(title: "1984", author: "George Orwell")
var book2 = book1 // A completely independent copy is created
book2.title = "Animal Farm"

print(book1.title) // Output: 1984
print(book2.title) // Output: Animal Farm

Class Example

class Car {
    var model: String
    
    init(model: String) {
        self.model = model
    }
}

let car1 = Car(model: "Model S")
let car2 = car1 // Both point to the same instance in memory
car2.model = "Model 3"

print(car1.model) // Output: Model 3
print(car2.model) // Output: Model 3

8. Protocols and Extensions

Protocols

A protocol defines a blueprint of methods, properties, and other requirements. Other types can then "conform" to the protocol to implement these requirements.

protocol Describable {
    func describe() -> String
}

struct Employee: Describable {
    var name: String
    var role: String
    
    func describe() -> String {
        return "Employee \(name) works as a \(role)."
    }
}

Extensions

Extensions add new functionality to an existing class, structure, structure, or protocol, even if you do not have access to the original source code.

extension Int {
    func squared() -> Int {
        return self * self
    }
}

let number = 4
print(number.squared()) // Output: 16

9. Error Handling

Swift handles errors by throwing, catching, and propagating them at runtime.

Defining Errors

Errors are represented by types that conform to the Error protocol. Typically, you use an enum.

enum NetworkError: Error {
    case badConnection
    case userNotFound
}

Throwing Errors

A function must use the throws keyword to show it can throw an error.

func fetchUserData(userId: Int) throws -> String {
    if userId == 0 {
        throw NetworkError.userNotFound
    }
    return "User Profile Data"
}

Catching Errors (do-catch)

To call a throwing function, you prefix the call with try and wrap it inside a do-catch block.

do {
    let profile = try fetchUserData(userId: 0)
    print(profile)
} catch NetworkError.userNotFound {
    print("Error: The requested user was not found.")
} catch {
    print("An unexpected error occurred: \(error).")
}

The guide was created in June 2026.