Swift Programming Language

Introduction

Swift is a powerful and intuitive programming language developed by Apple for building apps across all Apple platforms. It's designed to be safe, fast, and expressive, with a modern syntax that makes it easy to read and write.

Swift combines the performance of compiled languages with the ease of use of scripting languages. It's a versatile language, suitable for everything from system programming to mobile app development, and even server-side applications.

Fundamentals

Variables and Constants

In Swift, you declare constants using the let keyword and variables using the var keyword. Constants are immutable (cannot be changed after initialization), while variables are mutable (can be changed).

let maximumLoginAttempts = 10 // Constant
var currentLoginAttempts = 0     // Variable

currentLoginAttempts = 2
// maximumLoginAttempts = 5 // Error: cannot assign to let constant

Data Types

Swift has a rich set of data types, including:

Swift also supports type inference, meaning you don't always need to explicitly declare the type:

let inferredInt = 100         // inferred as Int
let inferredDouble = 100.0      // inferred as Double
let inferredString = "Hello"    // inferred as String

Operators

Swift supports standard arithmetic, comparison, logical, and assignment operators. It also includes:

Control Flow

Swift provides standard control flow statements like if, else, else if, switch, for-in, while, and repeat-while.

let score = 85

if score > 90 {
    print("Excellent!")
} else if score > 70 {
    print("Good job.")
} else {
    print("Keep practicing.")
}

let letter = "a"
switch letter {
case "a", "e", "i", "o", "u":
    print("\(letter) is a vowel")
default:
    print("\(letter) is a consonant")
}

Collections

Swift provides three main collection types for storing groups of values:

Arrays

Ordered collections of values of the same type.

var shoppingList = ["milk", "eggs", "bread"]
shoppingList.append("butter")
let firstItem = shoppingList[0] // "milk"

Dictionaries

Unordered collections of key-value pairs, where keys must be unique and of the same type, and values must be of the same type.

var ages = ["Alice": 30, "Bob": 25]
ages["Charlie"] = 35
let bobAge = ages["Bob"] // Optional(25)

Sets

Unordered collections of unique values of the same type. They are useful for checking membership or removing duplicate values.

var favoriteGenres: Set = ["Rock", "Classical", "Hip Hop"]
favoriteGenres.insert("Jazz")

Functions

Functions are self-contained blocks of code that perform a specific task. They can take parameters and return values.

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

let message = greet(person: "World") // "Hello, World!"

Closures

Closures are self-contained blocks of functionality that can be passed around and used in your code. They are similar to blocks in C and blocks in Objective-C, and to lambdas in other programming languages. Swift closures capture and store references to any constants and variables from the context in which they're defined.

let numbers = [1, 5, 3, 8, 2]
let sortedNumbers = numbers.sorted { $0 < $1 } // Sorts in ascending order
// sortedNumbers will be [1, 2, 3, 5, 8]

Classes and Structs

Both classes and structs are blueprint for creating new types. They can both define properties and methods. However, classes are reference types, while structs are value types.

  • Classes: Inherit from other classes, allow downcasting, have deinitializers, can be extended to add functionality after their initial definition, and can be mutated by class instances when assigned to a variable.
  • Structs: Do not support inheritance, cannot be downcasted, do not have deinitializers, can be extended to add functionality after their initial definition, and are copied when assigned to a variable or passed to a function.
struct Point {
    var x: Double
    var y: Double
}

class Person {
    var name: String
    init(name: String) {
        self.name = name
    }
}

Enumerations

Enumerations (or enums) are a way to define a group of related values. They are particularly useful for representing states or distinct cases.

enum CompassPoint {
    case north
    case south
    case east
    case west
}

var direction = CompassPoint.north
direction = .east // inferred

Protocols

Protocols define a blueprint of methods, properties, and other requirements that a conforming type must implement. They are central to Swift's design for achieving flexibility and code reuse.

protocol Vehicle {
    var numberOfWheels: Int { get }
    func startEngine()
}

struct Car: Vehicle {
    let numberOfWheels = 4
    func startEngine() {
        print("Car engine starting...")
    }
}

Extensions

Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This can include new methods, computed properties, initializers, and even conforming to new protocols.

extension Double {
    var km: Double { return self * 1_000.0 }
    var meters: Double { return self }
}

let distance = 5.km // 5000.0

Error Handling

Swift provides first-class support for error handling, allowing you to detect and respond to errors that occur during runtime.

enum VendingMachineError: Error {
    case invalidSelection
    case insufficientFunds
    case outOfStock
}

func vend(itemNamed: String) throws {
    // ... logic to vend item ...
    if itemNamed == "Candy Bar" {
        // throw VendingMachineError.outOfStock
    }
}

do {
    try vend(itemNamed: "Candy Bar")
} catch VendingMachineError.outOfStock {
    print("Sorry, that item is out of stock.")
} catch {
    print("An unexpected error occurred: \(error)")
}

Concurrency

Swift's modern concurrency features (introduced in Swift 5.5) allow you to write asynchronous and parallel code more easily and safely. Key concepts include async, await, and Task.

func fetchData() async throws -> Data {
    // Simulating network request
    await Task.sleep(1_000_000_000) // Sleep for 1 second
    return Data("Some fetched data".utf8)
}

Task {
    do {
        let data = try await fetchData()
        print("Data received: \(String(data: data, encoding: .utf8) ?? "N/A")")
    } catch {
        print("Error fetching data: \(error)")
    }
}