🚀 Swift Introduction

🎯 Complete Definition

Swift is a modern, safe, fast, and expressive multi-paradigm compiled programming language developed by Apple Inc. and the open-source community. First announced at WWDC 2014 and released in September 2014, Swift was designed by Chris Lattner and team to replace Objective-C for Apple ecosystem development while maintaining interoperability. Swift combines the best features of C/Objective-C without the constraints of C compatibility, featuring modern syntax, safety features, and high performance through LLVM compilation.

🔬 Core Characteristics

  • Type Safe: Strong static typing with type inference prevents type errors at compile time
  • Memory Safe: Automatic Reference Counting (ARC) manages memory automatically
  • Fast: Compiled via LLVM to native code, often outperforming Objective-C
  • Expressive: Clean syntax with modern features like closures, tuples, generics
  • Protocol-Oriented: Emphasizes protocol-oriented programming over classical inheritance
  • Multi-platform: Runs on iOS, macOS, watchOS, tvOS, Linux, and Windows
  • Open Source: Developed openly on GitHub with Apache 2.0 license

📊 Industry Usage

Swift powers 90%+ of new iOS/macOS applications, used by Uber, Airbnb, LinkedIn, and all Apple first-party apps. Ranked in top 10 most loved languages in Stack Overflow Developer Survey (2024). Swift on Server growing with Vapor and Kitura frameworks. Used by 4M+ developers worldwide with 200K+ open source packages. Performance benchmarks show 2.6x faster than Objective-C and 8.4x faster than Python 3.12.

import Foundation print("🦅 Welcome to Swift Master Track!") print("Swift Version: \(ProcessInfo.processInfo.operatingSystemVersionString)") let currentDate = Date() let formatter = DateFormatter() formatter.dateStyle = .long print("Today is: \(formatter.string(from: currentDate))") struct WelcomeMessage { let platform: String let year: Int func display() -> String { return "Building for \(platform) in \(year) 🚀" } } let message = WelcomeMessage(platform: "iOS & macOS", year: 2024) print(message.display())

📱 Swift Basics

🎯 Complete Definition

Swift Basics encompass the fundamental building blocks including variables, constants, data types, operators, and type annotations. Swift uses type inference to deduce types automatically while allowing explicit type annotations for clarity. The language distinguishes between mutable (var) and immutable (let) references, encouraging immutability by default. Swift's type system is strong and static, meaning type checking happens at compile time, preventing many common programming errors.

🏷️ 8 Fundamental Data Types

  • Int: Signed integers (Int8, Int16, Int32, Int64, Int) - platform dependent size
  • UInt: Unsigned integers (UInt8, UInt16, UInt32, UInt64, UInt)
  • Float: 32-bit floating point numbers (6-9 decimal digits precision)
  • Double: 64-bit floating point numbers (15-17 decimal digits precision, default)
  • Bool: Boolean values (true/false, not 0/1 like in C)
  • String: Unicode-compliant text with value semantics
  • Character: Single Unicode character
  • Optional: Special type representing either a value or nil

🔧 Advanced Features

Swift supports type aliases for creating alternative names for existing types, tuples for grouping multiple values, and type casting with as, as?, as!. The language uses Unicode-compliant identifiers allowing emoji and non-Latin characters in variable names. String interpolation provides clean syntax for embedding values in strings. Semicolons are optional except when multiple statements appear on one line.

// Variables (mutable) vs Constants (immutable) var userName = "CodeOrbitPro" // Type inferred as String let userId = 12345 // Constant - cannot be changed var score: Double = 95.5 // Explicit type annotation var isActive: Bool = true // Boolean type // Type inference in action let pi = 3.14159 // Inferred as Double (not Float) let maxScore = 100 // Inferred as Int let message = "Hello, \(userName)!" // String interpolation // Multiple assignment with tuples let (x, y, z) = (1, 2, 3) print("Coordinates: \(x), \(y), \(z)") // Type aliases typealias StudentID = Int typealias CompletionHandler = (Bool, String) -> Void let studentId: StudentID = 54321 print("Student ID: \(studentId)") // String operations let greeting = "Welcome to Swift" print(greeting.count) // Character count print(greeting.uppercased()) // Uppercase conversion

🎯 Optionals

🎯 Complete Definition

Optionals are Swift's solution to representing the absence of a value in a type-safe manner. An optional in Swift is an enumeration with two cases: .none (representing nil) and .some(value) (wrapping a value). This system eliminates null pointer exceptions common in other languages by forcing developers to explicitly handle missing values. Optionals are a generic type written as Type? which is syntactic sugar for Optional. The ! operator forces unwrapping (dangerous), while safe unwrapping methods prevent runtime crashes.

🔍 Optional Handling Techniques

  • Optional Binding: if let and guard let for safe unwrapping
  • Nil Coalescing: ?? operator provides default values
  • Optional Chaining: ? allows safe property/method access
  • Force Unwrapping: ! assumes value exists (crashes if nil)
  • Implicitly Unwrapped Optionals: Type! for values set after initialization
  • Optional Mapping: map() and flatMap() transform optional values

⚡ Advanced Optional Patterns

Swift 5.7 introduced if let shorthand reducing boilerplate. Optional pattern matching works with switch statements. Result builders can handle optional values in DSLs. The @OptionalType attribute marks protocol requirements as optional. Key paths support optional chaining. Swift's compiler ensures optionals are properly handled through exhaustive checking, making "unexpectedly found nil" errors explicit rather than silent failures.

// Optional declaration var optionalString: String? = "Hello" var nilString: String? = nil // Optional binding (safe unwrapping) if let unwrappedString = optionalString { print("String has value: \(unwrappedString)") } else { print("String is nil") } // Guard statement for early exit func processUser(name: String?) { guard let userName = name else { print("No username provided") return } print("Processing user: \(userName)") } // Nil coalescing operator let displayName = optionalString ?? "Anonymous User" print("Welcome, \(displayName)!") // Optional chaining struct User { var profile: Profile? } struct Profile { var bio: String? } let user = User(profile: Profile(bio: "Swift Developer")) let bioLength = user.profile?.bio?.count // Optional Int print("Bio length: \(bioLength ?? 0)") // Implicitly unwrapped optional (use cautiously) var forcedString: String! = "I'm guaranteed to have a value" print(forcedString.uppercased()) // No need to unwrap // Optional mapping let numberString: String? = "42" let number = numberString.map { Int($0) } // Optional> let flatNumber = numberString.flatMap { Int($0) } // Optional print("Mapped number: \(flatNumber ?? 0)")

🔀 Control Flow

🎯 Complete Definition

Control flow in Swift determines the execution order of statements and includes conditional statements, loops, and control transfer statements. Swift provides modern control flow constructs with safety features and expressive syntax. Unlike C-based languages, Swift doesn't require parentheses around conditions, and braces are always required even for single-line bodies. The language eliminates common errors like accidental assignment in conditions (= instead of ==) by making assignments non-Bool returning. Swift's control flow is designed to be both safe and expressive.

🏗️ Control Structures

  • if-else if-else: Standard conditional branching
  • switch-case: Powerful pattern matching with exhaustive checking
  • for-in: Iterates over sequences, ranges, and collections
  • while/repeat-while: Conditional loops (repeat-while = do-while)
  • guard: Early exit condition with required else clause
  • range operators: a...b (closed), a.. (half-open)
  • where clauses: Additional conditions in loops and switch cases

⚡ Advanced Control Flow

Swift's switch statements are extremely powerful supporting value binding, tuple matching, type casting patterns, and where clauses. Pattern matching works with if-case-let syntax. Labeled statements allow breaking/continuing specific loops. Fallthrough explicitly continues to next case. #available checks API availability. Control flow constructs work with optionals through optional binding. The compiler performs exhaustiveness checking on switches ensuring all cases are handled.

// If-else statements let temperature = 25 if temperature > 30 { print("It's hot! 🔥") } else if temperature < 10 { print("It's cold! ❄️") } else { print("Perfect weather! 🌤️") } // Switch with pattern matching let point = (2, 0) 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 4x4 square") default: print("Somewhere else") } // For-in loops for number in 1...5 { print("Number: \(number)") } let languages = ["Swift", "Kotlin", "Python"] for (index, language) in languages.enumerated() { print("\(index + 1). \(language)") } // While loop var countdown = 3 while countdown > 0 { print("\(countdown)...") countdown -= 1 } print("Launch! 🚀") // Repeat-while (do-while equivalent) var attempts = 0 repeat { print("Attempt \(attempts + 1)") attempts += 1 } while attempts < 3 // Guard statement for early exit func login(username: String?, password: String?) -> Bool { guard let username = username, !username.isEmpty, let password = password, password.count >= 8 else { return false } // username and password are safely unwrapped here return true }

⚙️ Functions

🎯 Complete Definition

Functions in Swift are self-contained chunks of code that perform specific tasks, defined with the func keyword. Swift functions support multiple return values via tuples, parameter labels (external and internal), default parameter values, variadic parameters, and in-out parameters. Functions are first-class citizens meaning they can be assigned to variables, passed as arguments, and returned from other functions. Swift uses a clean, expressive syntax that eliminates much of the verbosity found in Objective-C while maintaining clarity.

🏗️ Function Features

  • func keyword: Defines a function with parameters and return type
  • Parameter labels: External (argument label) and internal (parameter name)
  • Default parameters: Provide default values for parameters
  • Variadic parameters: Accept zero or more values of specified type
  • In-out parameters: Modify variables passed as arguments
  • Function types: (ParameterTypes) -> ReturnType
  • Nested functions: Functions defined inside other functions

⚡ Advanced Function Capabilities

Swift supports function overloading (multiple functions with same name but different parameters). @discardableResult suppresses warnings for unused return values. @autoclosure automatically wraps expressions in closures. @escaping indicates closures that outlive function scope. @inlinable suggests inlining for performance. Functions can have generic parameters making them work with any type. The throws/rethrows keywords enable error propagation. Swift's function syntax balances expressiveness with safety.

// Basic function func greet(person: String) -> String { return "Hello, \(person)!" } print(greet(person: "CodeOrbitPro")) // Multiple return values via tuple func minMax(array: [Int]) -> (min: Int, max: Int)? { guard !array.isEmpty else { return nil } var currentMin = array[0] var currentMax = array[0] for value in array[1.. currentMax { currentMax = value } } return (currentMin, currentMax) } if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) { print("Min: \(bounds.min), Max: \(bounds.max)") } // Parameter labels func calculateArea(for width: Double, and height: Double) -> Double { return width * height } print(calculateArea(for: 5.0, and: 10.0)) // Default parameters func increment(number: Int, by amount: Int = 1) -> Int { return number + amount } print(increment(number: 5)) // Uses default (6) print(increment(number: 5, by: 3)) // Custom increment (8) // Variadic parameters func average(_ numbers: Double...) -> Double { var total = 0.0 for number in numbers { total += number } return total / Double(numbers.count) } print(average(1, 2, 3, 4, 5)) // In-out parameters (modify original variable) func swapTwoInts(_ a: inout Int, _ b: inout Int) { let temporaryA = a a = b b = temporaryA } var x = 3 var y = 107 swapTwoInts(&x, &y) print("x is now \(x), y is now \(y)") // Function types let mathFunction: (Int, Int) -> Int = { $0 + $1 } print("Sum: \(mathFunction(2, 3))")

📦 Collections

🎯 Complete Definition

Collections in Swift are generic containers that store multiple values. Swift provides three primary collection types: Arrays (ordered collections), Sets (unordered unique elements), and Dictionaries (key-value pairs). All Swift collections are value types with copy-on-write optimization, meaning they're copied only when mutated. Collections are generic over their element types using angle bracket syntax Array. Swift's collection API is comprehensive with functional programming methods like map, filter, reduce.

📊 Collection Types

  • Array: Ordered, random-access collection, zero-based indexing
  • Set: Unordered collection of unique Hashable elements
  • Dictionary: Unordered collection of key-value pairs with unique keys
  • Range: Represents an interval of values (closed, half-open, one-sided)
  • String: Collection of Characters with bidirectional indexing
  • Collection protocols: Sequence, Collection, BidirectionalCollection, RandomAccessCollection

⚡ Collection Operations

Swift collections support subscripting for element access, range operators for slicing, and collection algorithms for searching, sorting, and transforming. The Collection protocol provides unified interface. Lazy collections defer computation until needed. Key paths enable type-safe property access. Collections are bridged to Objective-C counterparts (NSArray, NSSet, NSDictionary) when imported. Performance characteristics: Array O(1) random access, Set O(1) average membership test, Dictionary O(1) average key lookup.

// Arrays var programmingLanguages = ["Swift", "Kotlin", "Python"] programmingLanguages.append("JavaScript") programmingLanguages.insert("Rust", at: 1) print("Languages: \(programmingLanguages)") print("First language: \(programmingLanguages.first ?? "")") print("Count: \(programmingLanguages.count)") // Array operations let numbers = [1, 2, 3, 4, 5] let doubled = numbers.map { $0 * 2 } let evenNumbers = numbers.filter { $0 % 2 == 0 } let sum = numbers.reduce(0, +) print("Doubled: \(doubled)") print("Even: \(evenNumbers)") print("Sum: \(sum)") // Sets (unique, unordered) var programmingSet: Set = ["Swift", "Python", "Java"] programmingSet.insert("Kotlin") programmingSet.insert("Swift") // Won't add duplicate print("Set: \(programmingSet)") let swiftSet: Set = ["Swift", "iOS", "Xcode"] let pythonSet: Set = ["Python", "Django", "Data Science"] print("Union: \(swiftSet.union(pythonSet))") print("Intersection: \(swiftSet.intersection(pythonSet))") // Dictionaries var userScores = ["Alice": 95, "Bob": 87, "Charlie": 92] userScores["David"] = 88 // Add new entry userScores["Alice"] = 96 // Update existing for (name, score) in userScores { print("\(name): \(score)") } // Safe dictionary access if let bobScore = userScores["Bob"] { print("Bob's score: \(bobScore)") } // Default values let eveScore = userScores["Eve", default: 0] print("Eve's score: \(eveScore)") // Collection transformations let scores = [85, 92, 78, 95, 88] let scoreDescriptions = scores.map { score -> String in switch score { case 90...100: return "\(score): Excellent ⭐⭐⭐" case 80..<90: return "\(score): Good ⭐⭐" case 70..<80: return "\(score): Fair ⭐" default: return "\(score): Needs improvement" } } print(scoreDescriptions)

🎚️ Enumerations

🎯 Complete Definition

Enumerations (enums) in Swift are first-class types that define a common type for a group of related values. Unlike C enums which are essentially integers, Swift enums are much more powerful: they can have associated values, methods, computed properties, initializers, and can conform to protocols. Enums are value types and support pattern matching. Each enum case is a full-fledged value, not just an integer constant. Swift enums don't have an implicit raw value (unless explicitly specified), preventing common errors where enum values are used interchangeably with integers.

🏗️ Enum Features

  • Case values: Distinct members of the enumeration
  • Raw values: Pre-populated values (String, Int, Float, Character)
  • Associated values: Additional data attached to enum cases
  • Methods: Functions defined within enums
  • Computed properties: Properties that calculate values
  • Initializers: Custom initialization logic
  • Protocol conformance: Enums can conform to protocols

⚡ Advanced Enum Capabilities

Swift enums support recursive enumerations with indirect keyword for cases that have associated values of the enum type itself. CaseIterable protocol provides automatic allCases collection. Enums can have stored properties through associated values. Pattern matching with switch statements exhaustively handles all cases. RawRepresentable protocol enables conversion between raw values and cases. Enums are hashable by default when all associated values are hashable. OptionSet protocol enables bitmask-style flags.

// Basic enum enum CompassPoint { case north case south case east case west } var direction = CompassPoint.north direction = .east // Type inference allows shorthand // Switch on enum switch direction { case .north: print("Heading north 🧭") case .south: print("Heading south") case .east: print("Heading east") case .west: print("Heading west") } // Raw values (String, Int, Character) enum Planet: Int { case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune } let earth = Planet.earth print("Earth is planet #\(earth.rawValue)") // Associated values enum Barcode { case upc(Int, Int, Int, Int) case qrCode(String) } let productBarcode = Barcode.upc(8, 85909, 51226, 3) let siteBarcode = Barcode.qrCode("https://codeorbitpro.com") switch productBarcode { case .upc(let numberSystem, let manufacturer, let product, let check): print("UPC: \(numberSystem)-\(manufacturer)-\(product)-\(check)") case .qrCode(let productCode): print("QR code: \(productCode)") } // Methods in enums enum TrafficLight { case red, yellow, green func description() -> String { switch self { case .red: return "Stop 🛑" case .yellow: return "Caution ⚠️" case .green: return "Go ✅" } } mutating func next() { switch self { case .red: self = .green case .yellow: self = .red case .green: self = .yellow } } } var light = TrafficLight.red print(light.description()) light.next() print("Next light: \(light.description())") // CaseIterable for iteration enum Beverage: CaseIterable { case coffee, tea, juice, water } print("Available beverages:") for beverage in Beverage.allCases { print("- \(beverage)") } // Recursive enum indirect enum ArithmeticExpression { case number(Int) case addition(ArithmeticExpression, ArithmeticExpression) case multiplication(ArithmeticExpression, ArithmeticExpression) } let expression = ArithmeticExpression.addition( .number(5), .multiplication(.number(2), .number(3)) ) func evaluate(_ expression: ArithmeticExpression) -> Int { switch expression { case let .number(value): return value case let .addition(left, right): return evaluate(left) + evaluate(right) case let .multiplication(left, right): return evaluate(left) * evaluate(right) } } print("Expression result: \(evaluate(expression))") // 5 + (2 * 3) = 11

🏗️ Structs & Classes

🎯 Complete Definition

Structs and Classes in Swift are general-purpose, flexible constructs that become the building blocks of programs. Both can define properties to store values and methods to provide functionality. The key difference is that structs are value types (copied when assigned or passed) while classes are reference types (share a single instance). Structs don't support inheritance, while classes do. Swift encourages using structs by default due to their safety benefits - no unexpected sharing, thread-safe by default, and easier to reason about. Classes are used when reference semantics are needed or when inheritance is required.

🔬 Key Differences

  • Type: Structs = Value type, Classes = Reference type
  • Inheritance: Structs = No, Classes = Single inheritance
  • Deinitializer: Structs = No, Classes = Yes (deinit)
  • ARC: Structs = No (stored on stack), Classes = Yes (managed by ARC)
  • Identity operators: Structs = No, Classes = Yes (===, !==)
  • Default memberwise initializer: Structs = Yes, Classes = No

🏗️ Common Features

Both support properties (stored, computed, lazy), methods, initializers, subscripts, and protocol conformance. Both can be extended with extensions. Both support property observers (willSet, didSet). Both can have type properties/methods (static/class). Swift uses automatic reference counting for classes. Structs get memberwise initializers automatically. Both support access control (private, fileprivate, internal, public, open).

// Struct definition struct Resolution { var width = 0 var height = 0 // Computed property var aspectRatio: Double { return Double(width) / Double(height) } // Method func description() -> String { return "\(width)x\(height)" } } // Class definition class VideoMode { var resolution = Resolution() var frameRate = 0.0 var name: String? // Property observers var duration: Double = 0 { willSet { print("About to set duration to \(newValue)") } didSet { print("Duration changed from \(oldValue) to \(duration)") } } // Initializer init(resolution: Resolution, frameRate: Double, name: String?) { self.resolution = resolution self.frameRate = frameRate self.name = name } // Deinitializer (classes only) deinit { print("VideoMode instance is being deallocated") } } // Value type behavior (struct) var hd = Resolution(width: 1920, height: 1080) var cinema = hd // This creates a COPY cinema.width = 2048 print("HD width: \(hd.width)") // 1920 (unchanged) print("Cinema width: \(cinema.width)") // 2048 // Reference type behavior (class) let video = VideoMode(resolution: hd, frameRate: 60.0, name: "Sample") let alsoVideo = video // This shares the SAME instance alsoVideo.frameRate = 30.0 print("Video frameRate: \(video.frameRate)") // 30.0 (changed) print("AlsoVideo frameRate: \(alsoVideo.frameRate)") // 30.0 // Identity operators (classes only) if video === alsoVideo { print("video and alsoVideo refer to the same instance") } // Inheritance class Animal { var name: String init(name: String) { self.name = name } func makeSound() -> String { return "Some generic animal sound" } } class Dog: Animal { var breed: String init(name: String, breed: String) { self.breed = breed super.init(name: name) } override func makeSound() -> String { return "Woof! 🐕" } // Final method cannot be overridden final func fetch() -> String { return "\(name) fetches the ball!" } } let myDog = Dog(name: "Buddy", breed: "Golden Retriever") print(myDog.makeSound()) print(myDog.fetch())

📜 Protocols

🎯 Complete Definition

Protocols in Swift define a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. Protocols are similar to interfaces in other languages but more powerful. A protocol can be adopted by any class, struct, or enum to provide an actual implementation of those requirements. Swift is a protocol-oriented programming language - protocols are used extensively throughout Swift's standard library and are central to many design patterns. Protocols enable polymorphism without inheritance, composition over inheritance, and allow value types (structs, enums) to participate in polymorphic behavior.

🏗️ Protocol Requirements

  • Property requirements: var properties with get/set specifications
  • Method requirements: Instance and type methods
  • Mutating methods: For value types that need to modify self
  • Initializer requirements: Required initializers
  • Subscript requirements: For accessing elements by index/key
  • Associated types: Generic placeholders within protocols

⚡ Advanced Protocol Features

Protocols support protocol inheritance (multiple inheritance allowed). Protocol composition combines multiple protocols. Conditional conformance allows types to conform to protocols only under certain conditions. Protocol extensions provide default implementations. @objc protocols enable Objective-C interoperability. Existential types (Protocol) enable heterogeneous collections. Self requirements enable returning/conforming to the adopting type. Opaque types (some Protocol) hide concrete types while preserving type identity.

// Basic protocol protocol Vehicle { var numberOfWheels: Int { get } var maxSpeed: Double { get } func start() func stop() func description() -> String } // Protocol with mutating method protocol Toggleable { mutating func toggle() } // Protocol inheritance protocol ElectricVehicle: Vehicle { var batteryCapacity: Double { get } func charge(to percentage: Double) } // Protocol adoption by struct struct Car: Vehicle { let numberOfWheels: Int = 4 let maxSpeed: Double = 200.0 func start() { print("Car engine started 🚗") } func stop() { print("Car stopped") } func description() -> String { return "Car with \(numberOfWheels) wheels, max speed: \(maxSpeed) km/h" } } // Protocol adoption by class class Bicycle: Vehicle, Toggleable { var numberOfWheels: Int = 2 var maxSpeed: Double = 30.0 var isLocked: Bool = false func start() { print("Bicycle ready to ride 🚲") } func stop() { print("Bicycle stopped") } func description() -> String { return "Bicycle with \(numberWheels) wheels" } // Mutating method (struct would need mutating keyword) func toggle() { isLocked.toggle() print("Bicycle \(isLocked ? "locked" : "unlocked")") } } // Using protocols let myCar = Car() let myBike = Bicycle() let vehicles: [Vehicle] = [myCar, myBike] for vehicle in vehicles { print(vehicle.description()) vehicle.start() } // Protocol extensions extension Vehicle { func honk() { print("Beep beep! 🔊") } // Default implementation func description() -> String { return "Vehicle with \(numberOfWheels) wheels" } } myCar.honk() // Protocol with associated type (generic protocol) protocol Container { associatedtype Item var count: Int { get } mutating func append(_ item: Item) subscript(i: Int) -> Item { get } } struct IntStack: Container { typealias Item = Int private var items: [Item] = [] var count: Int { items.count } mutating func append(_ item: Item) { items.append(item) } subscript(i: Int) -> Item { return items[i] } } // Protocol composition protocol Named { var name: String { get } } protocol Aged { var age: Int { get } } struct Person: Named, Aged { let name: String let age: Int } func wishHappyBirthday(to celebrant: Named & Aged) { print("Happy birthday, \(celebrant.name), you're \(celebrant.age)! 🎂") } let birthdayPerson = Person(name: "CodeOrbitPro", age: 25) wishHappyBirthday(to: birthdayPerson) // Conditional protocol conformance extension Array: Container where Element: Equatable { typealias Item = Element mutating func append(_ item: Item) { self.append(item) } }

🔤 Generics

🎯 Complete Definition

Generics in Swift enable you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. Generics are one of Swift's most powerful features, used extensively throughout the standard library. They allow you to write code that avoids duplication while maintaining type safety. Generic code uses type parameters (placeholder types) that are specified when the code is used. Swift's generics system is similar to C++ templates but with type safety and without code bloat - generic code is compiled once and works with any type that meets the constraints.

🏗️ Generic Constructs

  • Generic Functions: Functions that work with any type
  • Generic Types: Classes, structs, enums that work with any type
  • Type Parameters: Placeholder types in angle brackets
  • Type Constraints: Require type parameters to conform to protocols or inherit from classes
  • Associated Types: Generic placeholders in protocols
  • Generic Where Clauses: Additional requirements on associated types

⚡ Advanced Generic Features

Swift supports opaque types (some Protocol) that hide concrete return types while preserving type identity. Existential types (any Protocol) enable heterogeneous collections. Primary associated types (Swift 5.7) simplify generic constraints. Type erasure patterns hide concrete types. Generic subscripts work with any type. Recursive generic constraints enable complex type relationships. The Swift compiler performs type inference for generic parameters at call sites. Generics enable polymorphism without inheritance.

// Generic function func swapTwoValues(_ a: inout T, _ b: inout T) { let temporaryA = a a = b b = temporaryA } var string1 = "hello" var string2 = "world" swapTwoValues(&string1, &string2) print("string1 is now \(string1), string2 is now \(string2)") var int1 = 3 var int2 = 107 swapTwoValues(&int1, &int2) print("int1 is now \(int1), int2 is now \(int2)") // Generic type struct Stack { private var items: [Element] = [] mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element? { return items.popLast() } func peek() -> Element? { return items.last } var isEmpty: Bool { return items.isEmpty } var count: Int { return items.count } } // Using generic type var intStack = Stack() intStack.push(1) intStack.push(2) intStack.push(3) print("Popped: \(intStack.pop() ?? 0)") // 3 var stringStack = Stack() stringStack.push("Swift") stringStack.push("Generics") print("Top item: \(stringStack.peek() ?? "")") // "Generics" // Type constraints func findIndex(of valueToFind: T, in array: [T]) -> Int? { for (index, value) in array.enumerated() { if value == valueToFind { return index } } return nil } let doubleIndex = findIndex(of: 9.3, in: [3.14159, 0.1, 0.25, 9.3]) let stringIndex = findIndex(of: "Swift", in: ["Objective-C", "Swift", "Kotlin"]) print("Double index: \(doubleIndex ?? -1)") print("String index: \(stringIndex ?? -1)") // Multiple constraints protocol Container { associatedtype Item var count: Int { get } mutating func append(_ item: Item) subscript(i: Int) -> Item { get } } func allItemsMatch (_ container1: C1, _ container2: C2) -> Bool where C1.Item == C2.Item, C1.Item: Equatable { if container1.count != container2.count { return false } for i in 0..(indices: Indices) -> [Element] where Indices.Iterator.Element == Int { var result: [Element] = [] for index in indices { if index < items.count { result.append(items[index]) } } return result } } // Using generic subscript intStack.push(10) intStack.push(20) intStack.push(30) let selectedItems = intStack[[0, 2]] print("Selected items: \(selectedItems)") // [10, 30] // Opaque return types (Swift 5.1+) protocol Shape { func draw() -> String } struct Square: Shape { var size: Int func draw() -> String { return "Square with size \(size)" } } struct FlippedShape: Shape { var shape: T func draw() -> String { return "Flipped \(shape.draw())" } } // Returns some Shape (opaque type) func makeTrapezoid() -> some Shape { let top = Square(size: 2) let middle = Square(size: 4) let bottom = Square(size: 6) // Combine shapes... return FlippedShape(shape: top) } let trapezoid = makeTrapezoid() print(trapezoid.draw())

⚠️ Error Handling

🎯 Complete Definition

Error handling in Swift provides a way to respond to and recover from error conditions in programs. Swift uses a type-safe error handling model that resembles exception handling in other languages but is both more explicit and integrated with the type system. Errors are represented by values of types that conform to the Error protocol. Four ways to handle errors in Swift: 1) Propagate errors using throws, 2) Handle with do-catch, 3) Convert to optional with try?, 4) Assert error won't occur with try!. Swift's error handling doesn't involve unwinding the call stack (unlike exceptions in some languages), making it more performant.

🏗️ Error Handling Constructs

  • Error protocol: Types that can represent error conditions
  • throwing functions: Functions that can throw errors
  • do-catch statements: Handle specific error patterns
  • try keyword: Call throwing functions
  • try? and try!: Convert errors to optionals or force unwrap
  • defer statements: Cleanup code that runs regardless of errors
  • Result type: Alternative error handling pattern

⚡ Advanced Error Handling

Swift supports rethrows for functions that only throw if their closure parameters throw. Local error propagation allows catching and rethrowing different errors. Error chaining preserves original error context. Custom error types can include additional data. Error localization via LocalizedError protocol. Recoverable errors patterns for user-facing errors. The Result type (Swift 5) provides functional error handling alternative. Async/await integrates with error handling seamlessly. Swift errors are not used for control flow (unlike some exception systems).

// Defining error types enum NetworkError: Error { case invalidURL case noConnection case timeout(seconds: Int) case serverError(statusCode: Int) case decodingError(message: String) } enum ValidationError: Error { case emptyField(fieldName: String) case invalidEmail case passwordTooShort(minLength: Int) case ageRestriction(minimumAge: Int) } // Throwing function func fetchData(from urlString: String) throws -> String { guard !urlString.isEmpty else { throw NetworkError.invalidURL } guard urlString.hasPrefix("http") else { throw NetworkError.invalidURL } // Simulate network conditions let randomNumber = Int.random(in: 1...10) switch randomNumber { case 1...3: throw NetworkError.noConnection case 4...5: throw NetworkError.timeout(seconds: 30) case 6...7: throw NetworkError.serverError(statusCode: 500) default: return "Sample data from \(urlString)" } } // do-catch error handling func loadUserData() { do { let data = try fetchData(from: "https://api.example.com") print("Success: \(data)") // Multiple throwing calls in same do block let moreData = try fetchData(from: "https://api2.example.com") print("More data: \(moreData)") } catch NetworkError.invalidURL { print("Error: Invalid URL provided") } catch NetworkError.noConnection { print("Error: No internet connection") } catch NetworkError.timeout(let seconds) { print("Error: Request timed out after \(seconds) seconds") } catch NetworkError.serverError(let statusCode) { print("Error: Server responded with status code \(statusCode)") } catch { print("An unexpected error occurred: \(error)") } } loadUserData() // try? converts errors to optionals let optionalData = try? fetchData(from: "https://bad-url") if let data = optionalData { print("Got data: \(data)") } else { print("Failed to get data (error converted to nil)") } // try! asserts error won't occur (crashes if it does) // let forcedData = try! fetchData(from: "https://api.example.com") // Dangerous! // defer statement for cleanup func processFile(filename: String) throws { print("Opening file: \(filename)") defer { print("Closing file: \(filename)") // This runs no matter how the function exits } defer { print("First defer runs last (LIFO)") } let data = try fetchData(from: "https://files.example.com/\(filename)") print("Processing: \(data)") // Even if error thrown above, defer still runs } // Using Result type for error handling func validateUser(email: String, password: String) -> Result { guard !email.isEmpty else { return .failure(.emptyField(fieldName: "email")) } guard email.contains("@") && email.contains(".") else { return .failure(.invalidEmail) } guard password.count >= 8 else { return .failure(.passwordTooShort(minLength: 8)) } return .success("User validated successfully") } // Handling Result let validationResult = validateUser(email: "user@example.com", password: "weak") switch validationResult { case .success(let message): print("Validation success: \(message)") case .failure(let error): switch error { case .emptyField(let field): print("Please fill in the \(field) field") case .invalidEmail: print("Please enter a valid email address") case .passwordTooShort(let minLength): print("Password must be at least \(minLength) characters") default: print("Validation failed") } } // Rethrowing functions func processWithRetry(attempts: Int, operation: () throws -> String) rethrows -> String { for attempt in 1...attempts { do { return try operation() } catch { print("Attempt \(attempt) failed: \(error)") if attempt == attempts { throw error } } } fatalError("Should never reach here") } // Using rethrows do { let result = try processWithRetry(attempts: 3) { return try fetchData(from: "https://api.example.com") } print("Final result: \(result)") } catch { print("All attempts failed: \(error)") }

🌀 Async/Await

🎯 Complete Definition

Async/await in Swift (introduced in Swift 5.5) is a modern concurrency model that provides a simple, safe way to write asynchronous code. It allows you to write asynchronous code that looks and behaves like synchronous code, eliminating "callback hell" and making concurrent programming more accessible. The model is built on structured concurrency principles ensuring tasks are properly managed and don't leak. Async/await works with Swift's existing error handling model (throws/try) and integrates with the actor model for data race safety. Under the hood, it uses continuations and the cooperative thread pool.

🏗️ Async/Await Components

  • async: Marks a function as asynchronous
  • await: Suspends execution until async operation completes
  • Task: Unit of concurrent execution
  • async let: Concurrently binds async expression results
  • TaskGroup: Manages dynamic number of child tasks
  • actors: Reference types that protect mutable state
  • @MainActor: Ensures code runs on main thread

⚡ Advanced Concurrency Features

Swift concurrency supports structured task hierarchies where parent tasks manage child tasks. Task cancellation propagates through task trees. Task priorities influence execution order. Async sequences enable iterating over asynchronous streams. Async properties and subscripts are supported. Sendable protocol ensures safe cross-actor value passing. Global actors provide custom execution contexts. The system uses cooperative cancellation requiring tasks to check cancellation status. Integration with Objective-C completion handlers via continuations.

// Basic async function func fetchUserProfile(userId: Int) async throws -> String { // Simulate network delay try await Task.sleep(nanoseconds: 1_000_000_000) // 1 second if userId == 1 { return "User Profile: CodeOrbitPro (Swift Developer)" } else { throw NetworkError.serverError(statusCode: 404) } } // Async function without throwing func calculateStatistics(scores: [Int]) async -> (min: Int, max: Int, average: Double) { // Simulate heavy computation await Task.yield() // Yield to allow other tasks to run guard !scores.isEmpty else { return (0, 0, 0.0) } let min = scores.min() ?? 0 let max = scores.max() ?? 0 let average = Double(scores.reduce(0, +)) / Double(scores.count) return (min, max, average) } // Using async/await Task { do { let profile = try await fetchUserProfile(userId: 1) print("Fetched: \(profile)") let scores = [85, 92, 78, 95, 88] let stats = await calculateStatistics(scores: scores) print("Stats - Min: \(stats.min), Max: \(stats.max), Avg: \(stats.average)") } catch { print("Error: \(error)") } } // Concurrent execution with async let func fetchMultipleData() async { async let userData = fetchUserProfile(userId: 1) async let productData = fetchProductInfo(productId: 42) async let settings = loadUserSettings() do { // All three fetches run concurrently let (user, product, settings) = try await (userData, productData, settings) print("User: \(user), Product: \(product), Settings: \(settings)") } catch { print("One of the fetches failed: \(error)") } } // Helper functions for above example func fetchProductInfo(productId: Int) async throws -> String { try await Task.sleep(nanoseconds: 500_000_000) return "Product #\(productId): Swift Programming Guide" } func loadUserSettings() async throws -> String { try await Task.sleep(nanoseconds: 300_000_000) return "Settings: Dark Mode, Notifications Enabled" } // Task groups for dynamic concurrency func downloadMultipleFiles(urls: [String]) async throws -> [String] { try await withThrowingTaskGroup(of: String.self) { group in var downloadedFiles: [String] = [] // Add child tasks for each URL for url in urls { group.addTask { try await downloadFile(from: url) } } // Collect results as they complete for try await file in group { downloadedFiles.append(file) print("Downloaded: \(file)") } return downloadedFiles } } // Helper for download func downloadFile(from url: String) async throws -> String { try await Task.sleep(nanoseconds: UInt64.random(in: 500_000_000...2_000_000_000)) return "File from \(url)" } // Actor for shared mutable state actor 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), Attempted: \(amount)") return false } } func getBalance() -> Double { return balance } } // Using actor Task { let account = BankAccount(accountNumber: "123456", initialBalance: 1000.0) // Must use await to interact with actor await account.deposit(amount: 500.0) let success = await account.withdraw(amount: 200.0) let currentBalance = await account.getBalance() print("Withdrawal successful: \(success), Current balance: \(currentBalance)") } // @MainActor for UI updates @MainActor class UserViewModel: ObservableObject { @Published var userName: String = "" @Published var isLoading: Bool = false func loadUser() async { isLoading = true // This runs on main thread automatically do { let profile = try await fetchUserProfile(userId: 1) userName = profile } catch { userName = "Error loading user" } isLoading = false } } // Async properties and subscripts struct AsyncDataLoader { subscript(index: Int) async throws -> String { try await Task.sleep(nanoseconds: 100_000_000) return "Data at index \(index)" } var firstItem: String { get async throws { try await self[0] } } } // Using async properties Task { let loader = AsyncDataLoader() let first = try await loader.firstItem print("First item: \(first)") let third = try await loader[2] print("Third item: \(third)") }

💾 Memory Management

🎯 Complete Definition

Memory management in Swift is primarily handled through Automatic Reference Counting (ARC), which automatically tracks and manages your app's memory usage. ARC works by counting how many references exist to each class instance (reference type). When the reference count reaches zero, ARC deallocates the instance and frees its memory. For value types (structs, enums), memory is managed automatically on the stack or inline within other types. Swift's memory management is deterministic and predictable - deallocation happens immediately when the last reference is removed, unlike garbage collection which runs at non-deterministic intervals.

🔧 ARC Mechanisms

  • Strong references: Default reference type, increments reference count
  • Weak references: Don't keep referenced object alive, become nil when object deallocates
  • Unowned references: Like weak but assumed to always have value, cause crash if accessed after deallocation
  • Reference cycles: When two objects hold strong references to each other
  • Weak/Unowned breaking cycles: Using weak/unowned to prevent memory leaks
  • Capture lists: Specify how values are captured in closures

⚡ Advanced Memory Features

Swift provides unowned(unsafe) for performance-critical code (no runtime checks). @unchecked Sendable for manually managed thread safety. withExtendedLifetime() extends object lifetime. withUnsafePointer() for low-level memory access. MemoryLayout type queries size/alignment/strides. Unsafe types (UnsafePointer, UnsafeBufferPointer) for C interop and performance. Copy-on-write optimization for value types. @autoclosure can affect memory behavior. The Swift runtime uses side tables for weak reference management. Actors provide data race safety for concurrent access.

// ARC Basics class 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 avoid cycle init(unit: String) { self.unit = unit print("Apartment \(unit) is being initialized") } deinit { print("Apartment \(unit) is being deinitialized") } } // Strong reference cycle example var john: Person? = Person(name: "John Appleseed") var unit4A: Apartment? = Apartment(unit: "4A") john!.apartment = unit4A unit4A!.tenant = john // Weak reference, doesn't create cycle // When we nil these, both deallocate john = nil // Prints: "John Appleseed is being deinitialized" unit4A = nil // Prints: "Apartment 4A is being deinitialized" // Strong reference cycle (problematic) class Customer { let name: String var creditCard: CreditCard? // Strong reference init(name: String) { self.name = name } deinit { print("\(name) is being deinitialized") } } class CreditCard { let number: String unowned let customer: Customer // Unowned reference (assumes customer exists longer) init(number: String, customer: Customer) { self.number = number self.customer = customer } deinit { print("Card #\(number) is being deinitialized") } } var customer: Customer? = Customer(name: "Jane Doe") customer!.creditCard = CreditCard(number: "1234-5678-9012-3456", customer: customer!) // Both deallocate properly (no cycle due to unowned) customer = nil // Prints both deinitialization messages // Closure capture lists class HTMLElement { let name: String let text: String? // Closure with strong reference to self lazy var asHTML: () -> String = { [weak self] in guard let self = self else { return "" } if let text = self.text { return "<\(self.name)>\(text)" } else { return "<\(self.name) />" } } init(name: String, text: String? = nil) { self.name = name self.text = text } deinit { print("\(name) is being deinitialized") } } var paragraph: HTMLElement? = HTMLElement(name: "p", text: "Hello, Swift!") print(paragraph!.asHTML()) paragraph = nil // Deallocates properly thanks to [weak self] // Unowned vs Weak class Country { let name: String var capitalCity: City! // Implicitly unwrapped optional init(name: String, capitalName: String) { self.name = name self.capitalCity = City(name: capitalName, country: self) } } class City { let name: String unowned let country: Country // Country will exist as long as City init(name: String, country: Country) { self.name = name self.country = country } } let country = Country(name: "Canada", capitalName: "Ottawa") print("\(country.name)'s capital is \(country.capitalCity.name)") // MemoryLayout for type information print("MemoryLayout.size: \(MemoryLayout.size)") // 8 on 64-bit print("MemoryLayout.alignment: \(MemoryLayout.alignment)") // 8 print("MemoryLayout.stride: \(MemoryLayout.stride)") // 8 struct Point3D { var x, y, z: Double } print("MemoryLayout.size: \(MemoryLayout.size)") // 24 (3 * 8) print("MemoryLayout.stride: \(MemoryLayout.stride)") // 24 // Unsafe memory operations (advanced) let count = 3 let pointer = UnsafeMutablePointer.allocate(capacity: count) pointer.initialize(repeating: 0, count: count) pointer[0] = 10 pointer[1] = 20 pointer[2] = 30 // Convert to buffer for safer access let buffer = UnsafeBufferPointer(start: pointer, count: count) for (index, value) in buffer.enumerated() { print("Element \(index): \(value)") } // Clean up pointer.deallocate() // Copy-on-write demonstration struct ImageData { private var _data: [UInt8] init(data: [UInt8]) { _data = data } // Copy-on-write: Only copy when mutated mutating func modifyPixel(at index: Int, value: UInt8) { // Ensure we have a unique copy before modifying if !isKnownUniquelyReferenced(&_data) { _data = _data.copy() } _data[index] = value } var data: [UInt8] { return _data } } var image1 = ImageData(data: [0, 128, 255]) var image2 = image1 // Shares underlying array initially image2.modifyPixel(at: 0, value: 255) // Now makes a copy print("Image1: \(image1.data), Image2: \(image2.data)")

⚡ Advanced Swift

🎯 Complete Definition

Advanced Swift encompasses the sophisticated language features and patterns that distinguish expert Swift development. This includes metaprogramming capabilities, advanced type system features, performance optimization techniques, and interoperability patterns. Swift's advanced features enable building high-performance, type-safe, and expressive systems while maintaining safety guarantees. These features include property wrappers, result builders, dynamic member lookup, key paths, and more. Advanced Swift techniques leverage the full power of Swift's type system while maintaining the language's core principles of safety, clarity, and performance.

🔬 Advanced Features

  • Property Wrappers: Reusable property behavior abstraction
  • Result Builders: Domain-specific languages for declarative UI
  • Dynamic Member Lookup: Runtime property/method access
  • Key Paths: Type-safe references to properties
  • Custom Operators: Define new operators with custom precedence
  • @autoclosure: Automatically wrap expressions in closures
  • @inlinable: Suggest function inlining to compiler
  • #available: Platform/version availability checking

⚡ Cutting-Edge Swift

Swift 5.9+ introduces macros for code generation at compile time. Parameter packs enable variable generic arity. Non-copyable types prevent copying for resource management. Borrowing/consuming parameters optimize value semantics. Custom actor executors provide fine-grained concurrency control. Observation framework replaces Combine for many use cases. C++ interoperability enables direct C++ calls. The Swift compiler continues to evolve with ownership model improvements, whole module optimization, and diagnostic enhancements.

// Property Wrappers @propertyWrapper struct Clamped { private var value: T private let range: ClosedRange var wrappedValue: T { get { value } set { value = min(max(range.lowerBound, newValue), range.upperBound) } } init(wrappedValue: T, _ range: ClosedRange) { self.range = range self.value = min(max(range.lowerBound, wrappedValue), range.upperBound) } } struct GameCharacter { @Clamped(0...100) var health: Int = 100 @Clamped(0...100) var mana: Int = 50 } var character = GameCharacter() character.health = 150 // Clamped to 100 character.mana = -10 // Clamped to 0 print("Health: \(character.health), Mana: \(character.mana)") // Result Builders (Domain Specific Languages) @resultBuilder struct HTMLBuilder { static func buildBlock(_ components: String...) -> String { return components.joined(separator: "\n") } static func buildArray(_ components: [String]) -> String { return components.joined(separator: "\n") } static func buildOptional(_ component: String?) -> String { return component ?? "" } static func buildEither(first component: String) -> String { return component } static func buildEither(second component: String) -> String { return component } } @HTMLBuilder func makePage(title: String, showHeader: Bool) -> String { "" "\(title)" "" if showHeader { "

\(title)

" } "
" "

Welcome to our site!

" "
" for item in ["Home", "About", "Contact"] { "\(item)" } "" "" } let page = makePage(title: "CodeOrbitPro", showHeader: true) print("Generated HTML page:\n\(page)") // Dynamic Member Lookup @dynamicMemberLookup struct DynamicObject { private var storage: [String: Any] = [:] subscript(dynamicMember member: String) -> Any? { get { storage[member] } set { storage[member] = newValue } } } var obj = DynamicObject() obj.name = "Swift" // Dynamically creates property obj.version = 5.9 obj.isAwesome = true print("Object: \(obj.name ?? ""), Version: \(obj.version ?? 0)") // Key Paths struct Student { let name: String var grade: Double var passed: Bool { grade >= 60.0 } } let students = [ Student(name: "Alice", grade: 95.0), Student(name: "Bob", grade: 58.5), Student(name: "Charlie", grade: 72.0) ] // Sort by grade using key path let sortedByGrade = students.sorted(by: \.grade) print("Sorted by grade: \(sortedByGrade.map { $0.name })") // Map to names using key path let names = students.map(\.name) print("Names: \(names)") // Filter by passed using key path let passingStudents = students.filter(\.passed) print("Passing students: \(passingStudents.count)") // Custom Operators infix operator ^^: ExponentiationPrecedence precedencegroup ExponentiationPrecedence { higherThan: MultiplicationPrecedence associativity: right } func ^^(base: Double, exponent: Double) -> Double { return pow(base, exponent) } let result = 2.0 ^^ 3.0 // 8.0 print("2 ^ 3 = \(result)") // @autoclosure func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = "") { #if DEBUG if !condition() { fatalError("Assertion failed: \(message())") } #endif } let x = 5 assert(x > 0, "x must be positive") // No need for closure syntax // #available for API availability if #available(iOS 15.0, macOS 12.0, *) { print("Can use iOS 15/macOS 12 APIs") } else { print("Need to use older APIs") } // Custom string interpolation extension String.StringInterpolation { mutating func appendInterpolation(_ value: Date, style: DateFormatter.Style) { let formatter = DateFormatter() formatter.dateStyle = style appendLiteral(formatter.string(from: value)) } mutating func appendInterpolation(repeat str: String, _ count: Int) { appendLiteral(String(repeating: str, count: count)) } } let today = Date() print("Today is \(today, style: .long)") print("Cheers! \(repeat: "🎉", 3)") // Protocol with primary associated types (Swift 5.7) protocol Storage { associatedtype Element func store(_ item: Element) func retrieve() -> Element? } struct Box: Storage { private var item: Item? func store(_ item: Item) { // Implementation } func retrieve() -> Item? { return item } } // Opaque parameter types func processStorage(_ storage: some Storage) { // Can work with any Storage that stores Ints } // Advanced pattern matching enum AdvancedPattern { case point(x: Int, y: Int) case circle(radius: Double) case rectangle(width: Double, height: Double) } let shape = AdvancedPattern.point(x: 10, y: 20) if case .point(let x, _) = shape, x > 5 { print("Point with x > 5: \(x)") } // Using ~= operator for custom pattern matching struct RangePattern { let lower: T let upper: T } func ~= (pattern: RangePattern, value: T) -> Bool { return value >= pattern.lower && value <= pattern.upper } let score = 85 let gradeRange = RangePattern(lower: 90, upper: 100) switch score { case gradeRange: print("Grade: A") case RangePattern(lower: 80, upper: 89): print("Grade: B") case RangePattern(lower: 70, upper: 79): print("Grade: C") default: print("Grade: F") }