The Complete Swift Language Guide
Master Swift — Apple's powerful and expressive language for building iOS, macOS, watchOS, and tvOS applications.
Why Learn Swift?
Swift was introduced by Apple in 2014 and open-sourced in 2015. It was designed to replace Objective-C with a modern, safer, and more expressive syntax. Swift combines the performance of compiled languages with the expressiveness of dynamic languages.
Swift is the primary language for Apple platform development — iOS, macOS, watchOS, and tvOS. It features a strong type system, optionals to eliminate null pointer errors, value semantics with structs, and protocol-oriented programming as a core paradigm.
With SwiftUI, Apple's declarative UI framework, you can build stunning apps across all platforms using a single codebase.
Did you know? Swift is consistently ranked among the fastest-growing languages and is now available on Linux and Windows too.
1. Swift Basics & Variables
Variables, Constants, and Types
Swift uses let for constants and var for variables. Swift infers types but you can also annotate them explicitly. Type safety prevents unexpected type coercions.
// Constants and variables
let appName = "MyApp" // constant, inferred as String
var score = 0 // variable, inferred as Int
var temperature: Double = 98.6 // explicit type annotation
// String interpolation
let user = "Alice"
let greeting = "Hello, \(user)! Your score is \(score)."
print(greeting)
// Multi-line strings
let message = """
Welcome to Swift.
It's type-safe and fast.
"""
// Type conversions
let num = 42
let str = String(num) // Int to String
let parsed = Int("123") // String to Int? (optional)
// Tuples
let coordinates = (x: 10.5, y: 20.3)
print("x: \(coordinates.x), y: \(coordinates.y)")
// Control flow
for i in 1...5 {
if i % 2 == 0 {
print("\(i) is even")
} else {
print("\(i) is odd")
}
}
var i = 0
while i < 3 {
print("while: \(i)")
i += 1
}2. Collections & Optionals
Arrays, Dictionaries & Optionals
Swift's collections are strongly typed. Optionals (T?) represent values that might be absent — a core safety feature that replaces nil/null crashes with compile-time checks.
// Arrays
var fruits = ["Apple", "Banana", "Mango"]
fruits.append("Orange")
fruits.insert("Grape", at: 1)
print(fruits.count)
for (index, fruit) in fruits.enumerated() {
print("\(index): \(fruit)")
}
// Dictionaries
var capitals: [String: String] = [
"France": "Paris",
"Japan": "Tokyo",
"India": "New Delhi"
]
capitals["Germany"] = "Berlin"
if let capital = capitals["France"] {
print("Capital of France: \(capital)")
}
// Optionals
var name: String? = "Alice"
var age: Int? = nil
// Optional binding
if let unwrappedName = name {
print("Name: \(unwrappedName)")
}
// Nil coalescing
let displayName = name ?? "Anonymous"
// Optional chaining
let uppercased = name?.uppercased()
// Guard let (early exit)
func greet(_ name: String?) {
guard let name = name else {
print("No name provided")
return
}
print("Hello, \(name)!")
}3. Functions & Closures
Functions and Closures
Swift functions have named parameters, default values, and can return multiple values. Closures are self-contained blocks of functionality — used heavily for callbacks and higher-order functions.
// Function with argument labels
func greet(person name: String, from city: String) -> String {
return "Hello \(name) from \(city)!"
}
let msg = greet(person: "Alice", from: "Paris")
// Default parameters
func createUser(name: String, role: String = "user", active: Bool = true) {
print("\(name) | \(role) | \(active)")
}
createUser(name: "Bob")
createUser(name: "Carol", role: "admin")
// Returning multiple values with tuple
func minMax(of array: [Int]) -> (min: Int, max: Int)? {
guard !array.isEmpty else { return nil }
return (array.min()!, array.max()!)
}
// Closures
let multiply: (Int, Int) -> Int = { x, y in x * y }
print(multiply(3, 4)) // 12
// Higher-order functions
let numbers = [1, 2, 3, 4, 5, 6]
let evens = numbers.filter { $0 % 2 == 0 }
let doubled = numbers.map { $0 * 2 }
let sum = numbers.reduce(0) { $0 + $1 }
print(evens, doubled, sum)4. Classes, Structs & Protocols
Protocol-Oriented Programming
Swift favors structs over classes for value semantics. Protocols define contracts that types must conform to. Protocol extensions add default implementations.
// Protocol definition
protocol Describable {
var description: String { get }
func describe()
}
// Protocol extension with default implementation
extension Describable {
func describe() {
print(description)
}
}
// Struct conforming to protocol
struct Point: Describable {
let x: Double
let y: Double
var description: String {
"Point(\(x), \(y))"
}
}
// Class with inheritance
class Vehicle: Describable {
let make: String
let year: Int
init(make: String, year: Int) {
self.make = make
self.year = year
}
var description: String { "\(year) \(make)" }
}
class ElectricCar: Vehicle {
let range: Int
init(make: String, year: Int, range: Int) {
self.range = range
super.init(make: make, year: year)
}
override var description: String {
super.description + " (Electric, \(range)mi range)"
}
}
let point = Point(x: 3.0, y: 4.0)
point.describe()
let car = ElectricCar(make: "Tesla", year: 2024, range: 350)
car.describe()5. Error Handling
Throwing and Catching Errors
Swift uses do-catch with throwing functions marked with throws. Errors conform to the Error protocol. Swift's error model is explicit and safe.
import Foundation
enum NetworkError: Error {
case invalidURL
case noData
case decodingFailed(String)
}
struct User: Decodable {
let id: Int
let name: String
let email: String
}
// Throwing function
func fetchUser(from urlString: String) throws -> User {
guard let url = URL(string: urlString) else {
throw NetworkError.invalidURL
}
// Simulate data (in real app, use URLSession)
let mockJSON = """
{"id": 1, "name": "Alice", "email": "alice@example.com"}
"""
guard let data = mockJSON.data(using: .utf8) else {
throw NetworkError.noData
}
do {
return try JSONDecoder().decode(User.self, from: data)
} catch {
throw NetworkError.decodingFailed(error.localizedDescription)
}
}
// Using do-catch
do {
let user = try fetchUser(from: "https://api.example.com/user/1")
print("User: \(user.name)")
} catch NetworkError.invalidURL {
print("Bad URL")
} catch NetworkError.noData {
print("No data received")
} catch NetworkError.decodingFailed(let msg) {
print("Decode error: \(msg)")
}6. Async/Await & Concurrency
Swift Concurrency Model
Swift 5.5 introduced async/await and actors for structured concurrency. Actors prevent data races by ensuring only one task at a time can access their mutable state.
import Foundation
// Async function
func fetchPosts() async throws -> [String] {
let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
let (data, _) = try await URLSession.shared.data(from: url)
// Decode and return titles
return ["Post 1", "Post 2", "Post 3"]
}
// Calling async functions
Task {
do {
let posts = try await fetchPosts()
posts.forEach { print($0) }
} catch {
print("Error: \(error)")
}
}
// Parallel async tasks
func loadDashboard() async {
async let user = fetchUser(from: "https://api.example.com/user/1")
async let posts = fetchPosts()
// Both run in parallel
do {
let (loadedUser, loadedPosts) = try await (user, posts)
print("User: \(loadedUser.name), Posts: \(loadedPosts.count)")
} catch {
print("Error: \(error)")
}
}
// Actor for thread-safe state
actor BankAccount {
private var balance: Double = 0
func deposit(_ amount: Double) { balance += amount }
func withdraw(_ amount: Double) -> Bool {
guard balance >= amount else { return false }
balance -= amount
return true
}
func getBalance() -> Double { balance }
}Start Building Apple Apps!
Swift's safety, expressiveness, and performance make it a joy to develop with. Combine it with SwiftUI to build stunning apps across all Apple platforms.