Swift Fundamentals
What is Swift?
Section titled “What is Swift?”- Developed by Apple in 2014
- Modern, safe, fast programming language
- Open-source and cross-platform
- Used for iOS, macOS, watchOS, tvOS, and server-side development
Key Features
Section titled “Key Features”- Type-safe with type inference
- Memory-safe with Automatic Reference Counting (ARC)
- Protocol-oriented programming
- Expressive and concise syntax
- Functional programming patterns
Setting Up Your Environment
Section titled “Setting Up Your Environment ”Option 1: Xcode (Recommended)
Section titled “Option 1: Xcode (Recommended)”# Download from Mac App Store# Includes Swift compiler, iOS simulator, and full IDEOption 2: Swift Playgrounds
Section titled “Option 2: Swift Playgrounds”- iPad app for learning Swift
- Interactive coding environment
Option 3: Online Compilers
Section titled “Option 3: Online Compilers”Option 4: Command Line (macOS/Linux)
Section titled “Option 4: Command Line (macOS/Linux)”# Install Swift from swift.org# Verify installationswift --version
# Create and run Swift filesecho 'print("Hello, Swift!")' > hello.swiftswift hello.swiftBasic Syntax and Fundamentals
Section titled “Basic Syntax and Fundamentals ”Hello World
Section titled “Hello World”// Simple print statementprint("Hello, World!")
// Multi-line stringprint("""Hello,World!""")Variables and Constants
Section titled “Variables and Constants”// Variables (mutable)var name = "John"var age = 25var height = 1.75var isStudent = true
// Constants (immutable)let firstName = "Jane"let maxAttempts = 3let pi = 3.14159
// Type annotations (explicit typing)var score: Int = 100var temperature: Double = 23.5var message: String = "Welcome"var isActive: Bool = false
// Multiple declarationsvar x = 0, y = 0, z = 0let red, green, blue: Doublered = 1.0green = 0.5blue = 0.2Comments
Section titled “Comments”// Single-line comment
/* Multi-line comment*/
/// Documentation comment/// - Parameter name: The name to greet/// - Returns: A greeting stringfunc greet(name: String) -> String { return "Hello, \(name)!"}String Interpolation
Section titled “String Interpolation”let name = "Alice"let age = 30
// Basic interpolationlet message = "Hello, my name is \(name) and I'm \(age) years old."
// Expressions in interpolationlet calculation = "5 + 3 = \(5 + 3)"
// Multi-line with interpolationlet bio = """Name: \(name)Age: \(age)Next year: \(age + 1)"""Data Types and Collections
Section titled “Data Types and Collections ”Basic Data Types
Section titled “Basic Data Types”// Integerslet minInt8 = Int8.min // -128let maxInt8 = Int8.max // 127let minInt = Int.min // Platform-dependentlet maxInt = Int.max // Platform-dependent
// Floating-point numberslet doubleValue: Double = 3.14159 // 64-bitlet floatValue: Float = 3.14 // 32-bit
// Booleanlet isTrue = truelet isFalse = false
// Characterlet letter: Character = "A"let emoji: Character = "😀"
// Stringlet greeting = "Hello, Swift!"let emptyString = ""Collections
Section titled “Collections”Arrays
Section titled “Arrays”// Creating arraysvar numbers = [1, 2, 3, 4, 5]var names: [String] = ["Alice", "Bob", "Charlie"]var emptyArray: [Int] = []var repeatedArray = Array(repeating: 0, count: 5)
// Accessing and modifyingprint(numbers[0]) // 1numbers[1] = 20 // Modifynumbers.append(6) // Add to endnumbers.insert(0, at: 0) // Insert at beginningnumbers.removeLast() // Remove lastnumbers.remove(at: 2) // Remove at index
// Array properties and methodsprint(numbers.count) // Number of elementsprint(numbers.isEmpty) // Check if emptyprint(numbers.contains(3)) // Check if contains value
// Iterationfor number in numbers { print(number)}
for (index, number) in numbers.enumerated() { print("Index \(index): \(number)")}
// Functional methodslet doubled = numbers.map { $0 * 2 }let evenNumbers = numbers.filter { $0 % 2 == 0 }let sum = numbers.reduce(0, +)Dictionaries
Section titled “Dictionaries”// Creating dictionariesvar ages = ["Alice": 25, "Bob": 30, "Charlie": 35]var scores: [String: Int] = [:]var coordinates: [String: Double] = ["x": 10.5, "y": 20.3]
// Accessing and modifyingprint(ages["Alice"]!) // Force unwrap (dangerous)print(ages["Alice"] ?? 0) // Safe with defaultages["David"] = 28 // Add new key-valueages["Alice"] = 26 // Update existingages["Bob"] = nil // Remove key
// Dictionary properties and methodsprint(ages.count)print(ages.isEmpty)print(ages.keys) // All keysprint(ages.values) // All values
// Iterationfor (name, age) in ages { print("\(name) is \(age) years old")}
for name in ages.keys { print(name)}// Creating setsvar uniqueNumbers: Set<Int> = [1, 2, 3, 4, 5]var colors = Set(["red", "green", "blue"])var emptySet: Set<String> = []
// Set operationslet oddNumbers: Set = [1, 3, 5, 7, 9]let evenNumbers: Set = [2, 4, 6, 8, 10]let primeNumbers: Set = [2, 3, 5, 7]
let union = oddNumbers.union(evenNumbers) // All elementslet intersection = oddNumbers.intersection(primeNumbers) // Common elementslet difference = oddNumbers.subtracting(primeNumbers) // In A but not Blet symmetricDiff = oddNumbers.symmetricDifference(primeNumbers) // Not in both
// Set methodsuniqueNumbers.insert(6)uniqueNumbers.remove(3)print(uniqueNumbers.contains(2))Tuples
Section titled “Tuples”// Creating tupleslet person = (name: "Alice", age: 25, isStudent: true)let coordinates = (x: 10.5, y: 20.3)let simpleTuple = ("Hello", 42)
// Accessing tuple elementsprint(person.name) // "Alice"print(person.0) // "Alice" - by indexprint(coordinates.x) // 10.5
// Decompositionlet (name, age, isStudent) = personprint(name) // "Alice"
// Ignoring elementslet (_, justAge, _) = personprint(justAge) // 25
// Returning multiple values from functionfunc calculateStats(_ numbers: [Int]) -> (min: Int, max: Int, sum: Int) { let min = numbers.min() ?? 0 let max = numbers.max() ?? 0 let sum = numbers.reduce(0, +) return (min, max, sum)}
let stats = calculateStats([1, 5, 3, 8, 2])print("Min: \(stats.min), Max: \(stats.max), Sum: \(stats.sum)")Control Flow
Section titled “Control Flow ”Conditional Statements
Section titled “Conditional Statements”If-Else
Section titled “If-Else”let temperature = 25
// Basic ifif temperature > 30 { print("It's hot!")}
// If-elseif temperature > 25 { print("Warm")} else { print("Cool")}
// Else-ifif temperature > 30 { print("Hot")} else if temperature > 20 { print("Warm")} else if temperature > 10 { print("Cool")} else { print("Cold")}
// Multiple conditionslet isSunny = trueif temperature > 25 && isSunny { print("Perfect beach weather!")}
if temperature < 10 || temperature > 35 { print("Extreme weather")}
// Optional binding in conditionsvar optionalName: String? = "John"if let name = optionalName { print("Hello, \(name)")}
// Multiple optional bindingsvar optionalAge: Int? = 25if let name = optionalName, let age = optionalAge { print("\(name) is \(age) years old")}
// Guard statement (early return)func processUser(name: String?, age: Int?) { guard let name = name, let age = age, age >= 18 else { print("Invalid user data") return } print("Processing \(name), age \(age)")}Switch Statement
Section titled “Switch Statement”let grade = "B"
// Basic switchswitch grade {case "A": print("Excellent!")case "B": print("Good")case "C": print("Average")case "D": print("Below average")case "F": print("Fail")default: print("Invalid grade")}
// Multiple valueslet character = "a"switch character {case "a", "e", "i", "o", "u": print("Vowel")case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": print("Consonant")default: print("Not a letter")}
// Range matchinglet score = 85switch score {case 90...100: print("A")case 80..<90: print("B")case 70..<80: print("C")case 60..<70: print("D")case 0..<60: print("F")default: print("Invalid score")}
// Tuple matchinglet point = (1, 1)switch point {case (0, 0): print("Origin")case (_, 0): print("On x-axis")case (0, _): print("On y-axis")case (-2...2, -2...2): print("Inside 2x2 square")case let (x, y) where x == y: print("On diagonal line y = x")case let (x, y): print("Point at (\(x), \(y))")}
// Value bindinglet anotherPoint = (2, 0)switch anotherPoint {case (let x, 0): print("On x-axis with x = \(x)")case (0, let y): print("On y-axis with y = \(y)")case let (x, y): print("Somewhere else at (\(x), \(y))")}For Loops
Section titled “For Loops”// Range-based loopsfor i in 1...5 { print(i) // 1, 2, 3, 4, 5}
for i in 1..<5 { print(i) // 1, 2, 3, 4}
// Iterating over arrayslet names = ["Alice", "Bob", "Charlie"]for name in names { print(name)}
// Iterating with indexfor (index, name) in names.enumerated() { print("\(index): \(name)")}
// Iterating over dictionarieslet ages = ["Alice": 25, "Bob": 30]for (name, age) in ages { print("\(name) is \(age)")}
// Stride for custom incrementsfor i in stride(from: 0, to: 10, by: 2) { print(i) // 0, 2, 4, 6, 8}
for i in stride(from: 10, through: 0, by: -2) { print(i) // 10, 8, 6, 4, 2, 0}While Loops
Section titled “While Loops”// While loopvar counter = 5while counter > 0 { print(counter) counter -= 1}
// Repeat-while (do-while equivalent)var number = 1repeat { print(number) number += 1} while number <= 5
// Break and continuefor i in 1...10 { if i == 3 { continue // Skip 3 } if i == 8 { break // Stop at 8 } print(i) // 1, 2, 4, 5, 6, 7}
// Labeled statementsouterLoop: for i in 1...3 { innerLoop: for j in 1...3 { if i == 2 && j == 2 { break outerLoop } print("i: \(i), j: \(j)") }}Functions
Section titled “Functions ”Function Basics
Section titled “Function Basics”// Basic functionfunc greet() { print("Hello!")}
// Function with parametersfunc greet(person: String) { print("Hello, \(person)!")}
// Function with return valuefunc square(number: Int) -> Int { return number * number}
// Multiple parametersfunc add(_ a: Int, _ b: Int) -> Int { return a + b}
// Multiple return values (using tuple)func minMax(array: [Int]) -> (min: Int, max: Int)? { if array.isEmpty { return nil } var currentMin = array[0] var currentMax = array[0] for value in array[1..<array.count] { if value < currentMin { currentMin = value } else if value > currentMax { currentMax = value } } return (currentMin, currentMax)}
// Using the functionsgreet()greet(person: "Alice")let result = square(number: 5)let sum = add(3, 4)
if let bounds = minMax(array: [8, 3, 9, 2, 7]) { print("Min: \(bounds.min), Max: \(bounds.max)")}Parameter Variations
Section titled “Parameter Variations”// Default parametersfunc greet(_ person: String, nicely: Bool = true) { if nicely { print("Hello, \(person)!") } else { print("Oh, it's \(person) again...") }}
greet("Alice") // Uses defaultgreet("Bob", nicely: false) // Override default
// Variadic parametersfunc average(_ numbers: Double...) -> Double { var total: Double = 0 for number in numbers { total += number } return numbers.isEmpty ? 0 : total / Double(numbers.count)}
print(average(1, 2, 3, 4, 5)) // 3.0
// In-out parametersfunc swapTwoInts(_ a: inout Int, _ b: inout Int) { let temporaryA = a a = b b = temporaryA}
var x = 5var y = 10swapTwoInts(&x, &y)print("x: \(x), y: \(y)") // x: 10, y: 5Function Types
Section titled “Function Types”// Function typesfunc add(_ a: Int, _ b: Int) -> Int { return a + b}
func multiply(_ a: Int, _ b: Int) -> Int { return a * b}
// Assigning functions to variablesvar mathFunction: (Int, Int) -> Int = addprint(mathFunction(2, 3)) // 5
mathFunction = multiplyprint(mathFunction(2, 3)) // 6
// Function types as parameter typesfunc printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) { print("Result: \(mathFunction(a, b))")}
printMathResult(add, 5, 3) // Result: 8
// Function types as return typesfunc chooseStepFunction(backward: Bool) -> (Int) -> Int { func stepForward(_ input: Int) -> Int { return input + 1 } func stepBackward(_ input: Int) -> Int { return input - 1 }
return backward ? stepBackward : stepForward}
var currentValue = 3let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
while currentValue != 0 { print("\(currentValue)...") currentValue = moveNearerToZero(currentValue)}print("zero!")Closures
Section titled “Closures”// Closure expressionslet names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
// Function as parameterfunc backward(_ s1: String, _ s2: String) -> Bool { return s1 > s2}var reversedNames = names.sorted(by: backward)
// Closure syntaxreversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2})
// Type inferencereversedNames = names.sorted(by: { s1, s2 in return s1 > s2 })
// Implicit returnsreversedNames = names.sorted(by: { s1, s2 in s1 > s2 })
// Shorthand argument namesreversedNames = names.sorted(by: { $0 > $1 })
// Operator methodsreversedNames = names.sorted(by: >)
// Trailing closuresreversedNames = names.sorted { $0 > $1 }
// Capturing valuesfunc makeIncrementer(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementer() -> Int { runningTotal += amount return runningTotal } return incrementer}
let incrementByTen = makeIncrementer(forIncrement: 10)print(incrementByTen()) // 10print(incrementByTen()) // 20print(incrementByTen()) // 30Optionals
Section titled “Optionals ”Understanding Optionals
Section titled “Understanding Optionals”// Optional typesvar optionalString: String? = "Hello"var optionalInt: Int? = 42var optionalDouble: Double? = nil
// Forced unwrapping (dangerous)let forcedString = optionalString!
// Optional binding (safe)if let string = optionalString { print("String is: \(string)")} else { print("String is nil")}
// Multiple optional bindingif let string = optionalString, let int = optionalInt { print("String: \(string), Int: \(int)")}
// Guard statementfunc processOptional(_ value: String?) { guard let value = value else { print("Value is nil") return } print("Processing: \(value)")}
// Nil-coalescing operatorlet actualString = optionalString ?? "Default Value"let actualInt = optionalInt ?? 0
// Optional chainingstruct Person { var name: String var address: Address?}
struct Address { var street: String var city: String}
let person: Person? = Person(name: "Alice", address: Address(street: "123 Main St", city: "Springfield"))let city = person?.address?.city // Optional("Springfield")
// Implicitly unwrapped optionalslet assumedString: String! = "Implicitly unwrapped"let implicitString: String = assumedString // No need for !
// Optional mappinglet number: Int? = 5let squared = number.map { $0 * $0 } // Optional(25)
let nilNumber: Int? = nillet nilSquared = nilNumber.map { $0 * $0 } // nilPractical Optional Patterns
Section titled “Practical Optional Patterns”// Function returning optionalfunc findFirstEven(in numbers: [Int]) -> Int? { for number in numbers { if number % 2 == 0 { return number } } return nil}
let numbers = [1, 3, 5, 7, 8, 9]if let firstEven = findFirstEven(in: numbers) { print("First even number: \(firstEven)")}
// Optional in switchlet someOptional: Int? = 42switch someOptional {case .some(let value): print("Value is \(value)")case .none: print("Value is nil")}
// Compact map for optionalslet strings = ["1", "2", "three", "4"]let numbers = strings.compactMap { Int($0) } // [1, 2, 4]
// Optional with enumsenum NetworkResult { case success(String) case failure(Error?)}
let result: NetworkResult = .success("Data loaded")switch result {case .success(let data): print("Success: \(data)")case .failure(let error): print("Error: \(error?.localizedDescription ?? "Unknown error")")}Structures and Classes
Section titled “Structures and Classes ”Structures
Section titled “Structures”// Basic structurestruct Point { var x: Double var y: Double
// Computed property var description: String { return "(\(x), \(y))" }
// Method func distance(to other: Point) -> Double { let deltaX = x - other.x let deltaY = y - other.y return sqrt(deltaX * deltaX + deltaY * deltaY) }
// Mutating method mutating func moveBy(x deltaX: Double, y deltaY: Double) { x += deltaX y += deltaY }}
// Using structuresvar point1 = Point(x: 0, y: 0)let point2 = Point(x: 3, y: 4)
print(point1.description) // (0.0, 0.0)print(point1.distance(to: point2)) // 5.0point1.moveBy(x: 1, y: 1)print(point1.description) // (1.0, 1.0)
// Memberwise initializerlet point3 = Point(x: 5, y: 5)
// Value type behaviorvar pointA = Point(x: 1, y: 1)var pointB = pointA // CopypointB.x = 2print(pointA.x) // 1.0 (unchanged)print(pointB.x) // 2.0Classes
Section titled “Classes”// Basic classclass Vehicle { var currentSpeed = 0.0 var description: String { return "traveling at \(currentSpeed) miles per hour" }
func makeNoise() { // Do nothing - override in subclasses }}
// Inheritanceclass Bicycle: Vehicle { var hasBasket = false
override func makeNoise() { print("Ring ring!") }}
class Car: Vehicle { var gear = 1 override var description: String { return super.description + " in gear \(gear)" }}
// Using classeslet bicycle = Bicycle()bicycle.hasBasket = truebicycle.currentSpeed = 15.0print(bicycle.description) // traveling at 15.0 miles per hourbicycle.makeNoise() // Ring ring!
let car = Car()car.currentSpeed = 60.0car.gear = 4print(car.description) // traveling at 60.0 miles per hour in gear 4
// Reference type behaviorlet vehicle1 = Vehicle()let vehicle2 = vehicle1 // Reference to same instancevehicle2.currentSpeed = 50.0print(vehicle1.currentSpeed) // 50.0 (changed)
// Identity operatorsprint(vehicle1 === vehicle2) // true (same instance)print(vehicle1 !== bicycle) // true (different instances)Properties
Section titled “Properties”struct Temperature { // Stored properties var celsius: Double
// Computed properties var fahrenheit: Double { get { return celsius * 9 / 5 + 32 } set { celsius = (newValue - 32) * 5 / 9 } }
var kelvin: Double { return celsius + 273.15 }}
var temp = Temperature(celsius: 25)print(temp.fahrenheit) // 77.0temp.fahrenheit = 100print(temp.celsius) // 37.777...
// Property observersclass StepCounter { var totalSteps: Int = 0 { willSet(newTotalSteps) { print("About to set totalSteps to \(newTotalSteps)") } didSet { if totalSteps > oldValue { print("Added \(totalSteps - oldValue) steps") } } }}
let stepCounter = StepCounter()stepCounter.totalSteps = 200stepCounter.totalSteps = 360
// Lazy propertiesclass DataImporter { var filename = "data.txt" // Data importing would happen here}
class DataManager { lazy var importer = DataImporter() var data: [String] = []}
let manager = DataManager()manager.data.append("Some data")// importer hasn't been created yetprint(manager.importer.filename) // Now importer is createdInitialization
Section titled “Initialization”struct Fahrenheit { var temperature: Double
init() { temperature = 32.0 }}
// Default initializervar f = Fahrenheit()
// Custom initializersstruct Celsius { var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) { temperatureInCelsius = (fahrenheit - 32.0) / 1.8 }
init(fromKelvin kelvin: Double) { temperatureInCelsius = kelvin - 273.15 }
init(_ celsius: Double) { temperatureInCelsius = celsius }}
let boilingPoint = Celsius(fromFahrenheit: 212.0)let freezingPoint = Celsius(fromKelvin: 273.15)let bodyTemperature = Celsius(37.0)
// Class inheritance and initializationclass Person { var name: String
init(name: String) { self.name = name }
convenience init() { self.init(name: "Unknown") }}
class Student: Person { var major: String
init(name: String, major: String) { self.major = major super.init(name: name) }
override convenience init(name: String) { self.init(name: name, major: "Undeclared") }}
let student1 = Student(name: "Alice", major: "Computer Science")let student2 = Student(name: "Bob") // Uses convenience initializerProtocols and Extensions
Section titled “Protocols and Extensions ”Protocols
Section titled “Protocols”// Protocol definitionprotocol Vehicle { var numberOfWheels: Int { get } var maxSpeed: Double { get }
func start() func stop() func describe() -> String}
// Protocol inheritanceprotocol Electric { var batteryLevel: Double { get set } func charge()}
// Protocol compositionprotocol ElectricVehicle: Vehicle, Electric { var chargingTime: Double { get }}
// Adopting protocolsstruct Car: Vehicle { let numberOfWheels = 4 let maxSpeed = 120.0
func start() { print("Car started") }
func stop() { print("Car stopped") }
func describe() -> String { return "A car with \(numberOfWheels) wheels and max speed \(maxSpeed) km/h" }}
class ElectricCar: ElectricVehicle { let numberOfWheels = 4 let maxSpeed = 150.0 let chargingTime = 8.0 var batteryLevel: Double = 100.0
func start() { print("Electric car started silently") }
func stop() { print("Electric car stopped") }
func describe() -> String { return "An electric car with \(batteryLevel)% battery" }
func charge() { batteryLevel = 100.0 print("Car charged to 100%") }}
// Using protocolsfunc operateVehicle(_ vehicle: Vehicle) { vehicle.start() print(vehicle.describe()) vehicle.stop()}
let car = Car()let electricCar = ElectricCar()
operateVehicle(car)operateVehicle(electricCar)Protocol Extensions
Section titled “Protocol Extensions”// Protocol with extensionprotocol Describable { func describe() -> String}
extension Describable { func describe() -> String { return "This is a describable object" }
func detailedDescription() -> String { return "Detailed: \(describe())" }}
struct Book: Describable { let title: String let author: String
func describe() -> String { return "Book: \(title) by \(author)" }}
let book = Book(title: "Swift Guide", author: "Apple")print(book.describe()) // "Book: Swift Guide by Apple"print(book.detailedDescription()) // "Detailed: Book: Swift Guide by Apple"
// Conditional conformanceextension Array: Describable where Element: Describable { func describe() -> String { let descriptions = self.map { $0.describe() } return "Array: [\(descriptions.joined(separator: ", "))]" }}
let books = [Book(title: "Book 1", author: "Author 1"), Book(title: "Book 2", author: "Author 2")]print(books.describe())Extensions
Section titled “Extensions”// Extending existing typesextension String { // Computed properties var isPalindrome: Bool { let cleaned = self.lowercased().filter { $0.isLetter } return cleaned == String(cleaned.reversed()) }
var wordCount: Int { return self.components(separatedBy: .whitespacesAndNewlines).count }
// Methods func withEmphasis() -> String { return "**\(self)**" }
mutating func addEmphasis() { self = self.withEmphasis() }
// Subscripts subscript(bounds: CountableClosedRange<Int>) -> String { let start = index(startIndex, offsetBy: bounds.lowerBound) let end = index(startIndex, offsetBy: bounds.upperBound) return String(self[start...end]) }}
let testString = "A man a plan a canal Panama"print(testString.isPalindrome) // trueprint(testString.wordCount) // 7print(testString.withEmphasis()) // "**A man a plan a canal Panama**"print(testString[0...5]) // "A man "
// Extending with protocolsextension Int: Describable { func describe() -> String { return "This is the number \(self)" }}
let number = 42print(number.describe()) // "This is the number 42"Error Handling
Section titled “Error Handling ”Defining and Throwing Errors
Section titled “Defining and Throwing Errors”// Error protocolenum NetworkError: Error { case invalidURL case noInternetConnection case requestTimeout case serverError(Int) case unknown}
enum ValidationError: Error { case emptyField case invalidEmail case passwordTooShort case ageRestriction}
// Throwing functionsfunc validateUser(email: String, password: String, age: Int) throws { guard !email.isEmpty && !password.isEmpty else { throw ValidationError.emptyField }
guard email.contains("@") && email.contains(".") else { throw ValidationError.invalidEmail }
guard password.count >= 8 else { throw ValidationError.passwordTooShort }
guard age >= 13 else { throw ValidationError.ageRestriction }}
// Function that can throw multiple errorsfunc fetchData(from urlString: String) throws -> String { guard let url = URL(string: urlString) else { throw NetworkError.invalidURL }
// Simulate network request let isSuccess = Bool.random()
if isSuccess { return "Data from \(urlString)" } else { throw NetworkError.serverError(500) }}Handling Errors
Section titled “Handling Errors”// Do-try-catchdo { try validateUser(email: "user@example.com", password: "password123", age: 25) print("Validation successful!")} catch ValidationError.emptyField { print("Please fill in all fields")} catch ValidationError.invalidEmail { print("Please enter a valid email address")} catch ValidationError.passwordTooShort { print("Password must be at least 8 characters")} catch ValidationError.ageRestriction { print("You must be at least 13 years old")} catch { print("An unexpected error occurred: \(error)")}
// Try? (convert to optional)if let data = try? fetchData(from: "https://example.com") { print("Data received: \(data)")} else { print("Failed to fetch data")}
// Try! (force try - use carefully)let data = try! fetchData(from: "https://example.com") // Will crash if throws
// Propagating errorsfunc processUserRegistration(email: String, password: String, age: Int) throws -> Bool { try validateUser(email: email, password: password, age: age) // Additional processing... return true}
// Error handling in async codefunc fetchDataAsync(from urlString: String) async throws -> String { // Simulate async network call try await Task.sleep(nanoseconds: 1_000_000_000) // 1 second
guard let url = URL(string: urlString) else { throw NetworkError.invalidURL }
return "Async data from \(urlString)"}
// Using async/await with error handlingTask { do { let data = try await fetchDataAsync(from: "https://example.com") print("Async data: \(data)") } catch { print("Async error: \(error)") }}Concurrency
Section titled “Concurrency ”Async/Await
Section titled “Async/Await”// Async functionsfunc fetchUserData() async throws -> String { try await Task.sleep(nanoseconds: 2_000_000_000) // 2 seconds return "User data"}
func fetchUserSettings() async throws -> String { try await Task.sleep(nanoseconds: 1_000_000_000) // 1 second return "User settings"}
// Using async functionsTask { do { let userData = try await fetchUserData() let userSettings = try await fetchUserSettings() print("\(userData) and \(userSettings)") } catch { print("Error: \(error)") }}
// Parallel executionTask { async let userData = fetchUserData() async let userSettings = fetchUserSettings()
do { let (data, settings) = try await (userData, userSettings) print("Both completed: \(data), \(settings)") } catch { print("Error in parallel tasks: \(error)") }}
// Async sequencesfunc numberStream() async -> AsyncStream<Int> { AsyncStream { continuation in Task { for i in 1...5 { try await Task.sleep(nanoseconds: 500_000_000) // 0.5 seconds continuation.yield(i) } continuation.finish() } }}
Task { for await number in numberStream() { print("Received: \(number)") } print("Stream finished")}Actors
Section titled “Actors”// Actor for thread-safe data accessactor BankAccount { private var balance: Double private let accountNumber: String
init(accountNumber: String, initialBalance: Double) { self.accountNumber = accountNumber self.balance = initialBalance }
func deposit(amount: Double) { balance += amount print("Deposited \(amount). New balance: \(balance)") }
func withdraw(amount: Double) -> Bool { if amount <= balance { balance -= amount print("Withdrew \(amount). New balance: \(balance)") return true } else { print("Insufficient funds. Balance: \(balance)") return false } }
func getBalance() -> Double { return balance }}
// Using actorsTask { let account = BankAccount(accountNumber: "12345", initialBalance: 1000.0)
// All actor method calls are async await account.deposit(amount: 500.0) let success = await account.withdraw(amount: 200.0) let balance = await account.getBalance()
print("Withdrawal successful: \(success), Final balance: \(balance)")}
// Multiple tasks accessing actorTask { let account = BankAccount(accountNumber: "67890", initialBalance: 500.0)
await withTaskGroup(of: Void.self) { group in for _ in 1...10 { group.addTask { await account.deposit(amount: 100.0) } } }
let finalBalance = await account.getBalance() print("Final balance after multiple deposits: \(finalBalance)")}SwiftUI Introduction
Section titled “SwiftUI Introduction ”Basic SwiftUI Views
Section titled “Basic SwiftUI Views”import SwiftUI
struct ContentView: View { @State private var name = "" @State private var isOn = false @State private var selectedColor = 0 let colors = ["Red", "Green", "Blue"]
var body: some View { VStack(spacing: 20) { Text("Hello, SwiftUI!") .font(.largeTitle) .foregroundColor(.blue)
TextField("Enter your name", text: $name) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding()
Toggle("Enable feature", isOn: $isOn) .padding()
Picker("Select a color", selection: $selectedColor) { ForEach(0..<colors.count, id: \.self) { index in Text(colors[index]) } } .pickerStyle(SegmentedPickerStyle()) .padding()
Button("Submit") { print("Name: \(name), Toggle: \(isOn), Color: \(colors[selectedColor])") } .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(8)
Spacer() } .padding() }}List and Navigation
Section titled “List and Navigation”struct Item: Identifiable { let id = UUID() let name: String let description: String}
struct ListView: View { let items = [ Item(name: "Apple", description: "A fruit"), Item(name: "Banana", description: "Yellow fruit"), Item(name: "Orange", description: "Citrus fruit") ]
var body: some View { NavigationView { List(items) { item in NavigationLink(destination: DetailView(item: item)) { HStack { Image(systemName: "circle.fill") .foregroundColor(.blue) VStack(alignment: .leading) { Text(item.name) .font(.headline) Text(item.description) .font(.subheadline) .foregroundColor(.gray) } } } } .navigationTitle("Fruits") .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("Add") { print("Add button tapped") } } } } }}
struct DetailView: View { let item: Item
var body: some View { VStack { Text(item.name) .font(.title) Text(item.description) .font(.body) .padding() Spacer() } .navigationTitle(item.name) }}Advanced Topics
Section titled “Advanced Topics ”Generics
Section titled “Generics”// Generic functionsfunc swapTwoValues<T>(_ a: inout T, _ b: inout T) { let temporaryA = a a = b b = temporaryA}
var string1 = "hello"var string2 = "world"swapTwoValues(&string1, &string2)print("string1: \(string1), string2: \(string2)") // string1: world, string2: hello
// Generic typesstruct Stack<Element> { private var elements: [Element] = []
mutating func push(_ element: Element) { elements.append(element) }
mutating func pop() -> Element? { return elements.popLast() }
func peek() -> Element? { return elements.last }
var isEmpty: Bool { return elements.isEmpty }}
var intStack = Stack<Int>()intStack.push(1)intStack.push(2)intStack.push(3)print(intStack.pop()) // 3
// Generic constraintsfunc findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? { for (index, value) in array.enumerated() { if value == valueToFind { return index } } return nil}
let strings = ["cat", "dog", "llama"]if let foundIndex = findIndex(of: "dog", in: strings) { print("Found at index \(foundIndex)") // Found at index 1}Memory Management
Section titled “Memory Management”// Reference cycles and weak referencesclass Person { let name: String var apartment: Apartment?
init(name: String) { self.name = name print("\(name) is being initialized") }
deinit { print("\(name) is being deinitialized") }}
class Apartment { let unit: String weak var tenant: Person? // Weak reference to break cycle
init(unit: String) { self.unit = unit print("Apartment \(unit) is being initialized") }
deinit { print("Apartment \(unit) is being deinitialized") }}
var john: Person? = Person(name: "John")var unit4A: Apartment? = Apartment(unit: "4A")
john?.apartment = unit4Aunit4A?.tenant = john
john = nilunit4A = nil// Both are deallocated properly
// Unowned referencesclass Customer { let name: String var card: CreditCard?
init(name: String) { self.name = name }
deinit { print("\(name) is being deinitialized") }}
class CreditCard { let number: UInt64 unowned let customer: Customer // Unowned reference
init(number: UInt64, customer: Customer) { self.number = number self.customer = customer }
deinit { print("Card #\(number) is being deinitialized") }}
var customer: Customer? = Customer(name: "Alice")customer?.card = CreditCard(number: 1234_5678_9012_3456, customer: customer!)customer = nil// Both are deallocatedPractice Projects
Section titled “Practice Projects ”Project 1: Todo List App
Section titled “Project 1: Todo List App”import SwiftUI
struct TodoItem: Identifiable, Codable { let id = UUID() var title: String var isCompleted = false}
class TodoStore: ObservableObject { @Published var items: [TodoItem] = [] { didSet { saveItems() } }
init() { loadItems() }
func addItem(_ title: String) { let newItem = TodoItem(title: title) items.append(newItem) }
func toggleItem(_ item: TodoItem) { if let index = items.firstIndex(where: { $0.id == item.id }) { items[index].isCompleted.toggle() } }
func deleteItem(_ item: TodoItem) { items.removeAll { $0.id == item.id } }
private func saveItems() { if let encoded = try? JSONEncoder().encode(items) { UserDefaults.standard.set(encoded, forKey: "todoItems") } }
private func loadItems() { if let data = UserDefaults.standard.data(forKey: "todoItems"), let decoded = try? JSONDecoder().decode([TodoItem].self, from: data) { items = decoded } }}
struct TodoListView: View { @StateObject private var store = TodoStore() @State private var newItemTitle = ""
var body: some View { NavigationView { VStack { HStack { TextField("New todo item", text: $newItemTitle) .textFieldStyle(RoundedBorderTextFieldStyle())
Button("Add") { if !newItemTitle.isEmpty { store.addItem(newItemTitle) newItemTitle = "" } } .disabled(newItemTitle.isEmpty) } .padding()
List { ForEach(store.items) { item in HStack { Image(systemName: item.isCompleted ? "checkmark.circle.fill" : "circle") .foregroundColor(item.isCompleted ? .green : .gray) .onTapGesture { store.toggleItem(item) }
Text(item.title) .strikethrough(item.isCompleted) .foregroundColor(item.isCompleted ? .gray : .primary)
Spacer() } } .onDelete(perform: deleteItems) } .listStyle(PlainListStyle()) } .navigationTitle("Todo List") .toolbar { EditButton() } } }
private func deleteItems(at offsets: IndexSet) { for index in offsets { store.deleteItem(store.items[index]) } }}Project 2: Weather App
Section titled “Project 2: Weather App”import SwiftUI
struct WeatherData: Codable { let name: String let main: Main let weather: [Weather]
struct Main: Codable { let temp: Double let humidity: Int }
struct Weather: Codable { let description: String let icon: String }}
class WeatherService: ObservableObject { @Published var weather: WeatherData? @Published var isLoading = false @Published var error: String?
func fetchWeather(for city: String) async { DispatchQueue.main.async { self.isLoading = true self.error = nil }
// Note: You'll need to replace with a real API key from OpenWeatherMap let apiKey = "YOUR_API_KEY" let urlString = "https://api.openweathermap.org/data/2.5/weather?q=\(city)&appid=\(apiKey)&units=metric"
guard let url = URL(string: urlString) else { DispatchQueue.main.async { self.error = "Invalid URL" self.isLoading = false } return }
do { let (data, _) = try await URLSession.shared.data(from: url) let weatherData = try JSONDecoder().decode(WeatherData.self, from: data)
DispatchQueue.main.async { self.weather = weatherData self.isLoading = false } } catch { DispatchQueue.main.async { self.error = "Failed to fetch weather: \(error.localizedDescription)" self.isLoading = false } } }}
struct WeatherView: View { @StateObject private var service = WeatherService() @State private var city = ""
var body: some View { VStack(spacing: 20) { Text("Weather App") .font(.largeTitle) .bold()
HStack { TextField("Enter city name", text: $city) .textFieldStyle(RoundedBorderTextFieldStyle())
Button("Get Weather") { Task { await service.fetchWeather(for: city) } } .disabled(city.isEmpty) } .padding()
if service.isLoading { ProgressView("Loading...") } else if let error = service.error { Text("Error: \(error)") .foregroundColor(.red) } else if let weather = service.weather { VStack(spacing: 10) { Text(weather.name) .font(.title)
Text("\(Int(weather.main.temp))°C") .font(.system(size: 48, weight: .bold))
if let weatherDescription = weather.weather.first { Text(weatherDescription.description.capitalized) .font(.title2) }
Text("Humidity: \(weather.main.humidity)%") .font(.body) } .padding() }
Spacer() } .padding() }}Learning Resources
Section titled “Learning Resources”Official Documentation
Section titled “Official Documentation”Practice Platforms
Section titled “Practice Platforms”Next Steps
Section titled “Next Steps”- Build projects - Start with simple apps and gradually increase complexity
- Learn design patterns - MVC, MVVM, Coordinator pattern
- Study Apple’s frameworks - UIKit, SwiftUI, Core Data, Combine
- Practice algorithms - Focus on common interview problems
- Join communities - Swift forums, GitHub open-source projects