Swift Interview Questions and Answers


What is Swift?
  • Swift is a powerful and intuitive programming language developed by Apple for building apps for iOS, macOS, watchOS, tvOS, and beyond. It's designed to be safe, fast, and modern, with a focus on developer productivity and code readability.
Who developed Swift?
  • Swift was developed by Apple Inc., led by Chris Lattner.
What are the key features of Swift?
  • **Safety:** Features like Optionals, strong typing, and automatic reference counting (ARC) help prevent common programming errors like null pointer exceptions and memory leaks.
  • **Speed:** Designed for performance, using a high-performance LLVM compiler and modern programming constructs.
  • **Modern:** Incorporates modern programming concepts like closures, generics, type inference, and functional programming patterns.
  • **Expressive:** Clean and readable syntax.
  • **Interoperability:** Seamlessly interoperates with Objective-C code.
  • **Open Source:** Swift is open source, with contributions from a large community.
What is the difference between Swift and Objective-C?
  • **Syntax:** Swift has a cleaner, more concise syntax compared to Objective-C's verbose syntax with square brackets and semicolons.
  • **Safety:** Swift has built-in safety features like Optionals and strong typing, which are not present in Objective-C.
  • **Memory Management:** Both use ARC, but Swift's implementation is generally more robust. Objective-C also supports manual memory management.
  • **Performance:** Swift is generally faster due to its modern compiler and optimizations.
  • **Mutability:** Swift requires explicit keywords (`let` for constants, `var` for variables) for mutability, while Objective-C uses conventions (`NSString` vs `NSMutableString`).
  • **Frameworks:** Both can use the same Apple frameworks (Cocoa, Cocoa Touch).
What are Optionals in Swift? Why are they used?
  • An Optional is a type that can either hold a value or hold `nil` (meaning no value). They are declared by appending a `?` to the type (e.g., `String?`).
  • They are used to handle the absence of a value safely, preventing runtime errors that occur when trying to access a variable that doesn't hold a valid object (like null pointer exceptions in other languages).
How do you unwrap an Optional? What are the different ways?
  • **Forced Unwrapping:** Using `!` (e.g., `myOptional!`). This is dangerous and should only be used when you are absolutely certain the Optional contains a value, as it will cause a runtime crash if the value is `nil`.
  • **Optional Binding:** Using `if let` or `guard let`. This safely unwraps the Optional and binds the value to a temporary constant or variable only if it's not `nil`.
    if let unwrappedValue = myOptional {
        // Use unwrappedValue here
    }
  • **Optional Chaining:** Using `?` to safely access properties, methods, or subscripts on a potentially `nil` Optional. The entire expression evaluates to `nil` if any part of the chain is `nil`.
    let roomCount = john.residence?.numberOfRooms
  • **Nil-Coalescing Operator:** Using `??`. Provides a default value if the Optional is `nil`.
    let defaultName = name ?? "Guest"
  • **Implicitly Unwrapped Optionals:** Using `!` (e.g., `String!`). Acts like a regular Optional but doesn't require unwrapping before use. Still crashes if accessed when `nil`. Use sparingly, typically for properties that are guaranteed to be initialized later (like IBOutlets).
What is the difference between `let` and `var`?
  • `let`: Declares a constant. The value cannot be changed after it is initialized.
  • `var`: Declares a variable. The value can be changed after it is initialized.
  • Use `let` whenever possible to make your code safer and clearer about intent.
What is Type Inference in Swift?
  • Type Inference is the ability of the Swift compiler to deduce the type of a variable or constant automatically based on the value it's initialized with, without requiring explicit type annotation.
    let meaningOfLife = 42 // Compiler infers Int
    var pi = 3.14159 // Compiler infers Double
What is Automatic Reference Counting (ARC)?
  • ARC is Swift's memory management system. It automatically tracks and manages the memory usage of your app's instances. When an instance is no longer needed (i.e., there are no strong references to it), ARC deallocates the memory used by that instance. This eliminates most memory management issues like memory leaks and dangling pointers.
What are Strong, Weak, and Unowned references? When would you use them?
  • **Strong Reference:** The default reference type. A strong reference keeps an instance in memory, preventing ARC from deallocating it.
  • **Weak Reference:** A reference that does not keep a strong hold on the instance it refers to. When the instance it refers to is deallocated, the weak reference is automatically set to `nil`. Declared with the `weak` keyword, must be an Optional type. Used to break Strong Reference Cycles, typically when a parent object has a strong reference to a child object, and the child object needs a reference back to the parent.
  • **Unowned Reference:** Similar to a weak reference in that it doesn't keep a strong hold. However, an unowned reference is assumed to *always* have a value (it's not an Optional). If you try to access an unowned reference that points to a deallocated instance, it will cause a runtime error. Used when you know the other instance will always have the same or a longer lifetime than your instance, and a nil value is not expected (e.g., a parent object having a strong reference to a child, and the child having an unowned reference back to the parent, assuming the child cannot exist without the parent).
What is a Strong Reference Cycle? How do you resolve it?
  • A Strong Reference Cycle occurs when two or more instances hold strong references to each other, preventing them from being deallocated by ARC even when they are no longer needed by the rest of the application.
  • You resolve a strong reference cycle by changing one of the strong references to a `weak` or `unowned` reference.
What is a Struct in Swift?
  • A Struct (structure) is a value type used to group related data and functionality. Structs are passed by value (a copy is made when assigned or passed to a function).
    struct Point {
        var x: Double
        var y: Double
    }
What is a Class in Swift?
  • A Class is a reference type used to define blueprints for objects. Classes support inheritance, polymorphism, and deinitializers. Class instances are passed by reference (multiple variables can refer to the same instance).
    class Person {
        var name: String
        init(name: String) {
            self.name = name
        }
        deinit {
            // cleanup code
        }
    }
What is the difference between a Struct and a Class?
  • **Type:** Structs are value types; Classes are reference types.
  • **Memory:** Structs are typically stored on the stack (for small, short-lived instances) or within the memory of the containing type. Classes are stored on the heap, and references to them are stored on the stack.
  • **Inheritance:** Classes support inheritance; Structs do not.
  • **Deinitializers:** Classes can have deinitializers; Structs cannot.
  • **Identity:** Class instances have identity (multiple references can point to the same instance). Struct instances do not (each variable holds its own copy).
  • **Mutability of Instances:** For a `var` instance of a struct, you can change its properties. For a `let` instance of a struct, you *cannot* change its properties. For both `var` and `let` instances of a class, you *can* change its `var` properties (you just can't reassign the `let` variable to a different instance).
When would you choose a Struct over a Class?
  • When you need to model simple data values.
  • When the instance's lifetime is short.
  • When you don't need inheritance.
  • When you want value semantics (copying behavior).
  • When working with small data structures.
What is a Protocol in Swift?
  • A Protocol defines a blueprint of methods, properties, and other requirements that a class, struct, or enum can conform to. It specifies a contract that conforming types must fulfill.
    protocol Drivable {
        var speed: Double { get set }
        func start()
        func stop()
    }
What is Protocol-Oriented Programming?
  • Protocol-Oriented Programming is a programming paradigm that emphasizes the use of protocols and protocol extensions to achieve code reuse and flexibility. Instead of relying heavily on class inheritance, you define behavior requirements using protocols and provide default implementations using protocol extensions. This often leads to more modular and less brittle code.
What is a Protocol Extension? Why use it?
  • A Protocol Extension allows you to provide default implementations for methods, computed properties, or subscripts defined in a protocol.
  • Use Protocol Extensions to:
    • Provide default behavior for protocol requirements.
    • Add new functionality (methods, computed properties) to conforming types without modifying the original types.
    • Promote code reuse and avoid repeating implementation details in every conforming type.
What is a Closure in Swift?
  • A Closure is a self-contained block of functionality that can be passed around and used in your code. Closures can capture and store references to any constants and variables from the context in which they are defined (this is called closing over those constants and variables).
    let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    let sortedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
        return s1 < s2
    })
What are Trailing Closures?
  • Trailing closures are a syntax sugar in Swift. If the last argument to a function is a closure, you can write the closure outside the function's parentheses.
    let sortedNames = names.sorted { s1, s2 in s1 < s2 }
What are Capture Lists in Closures? Why use them?
  • A Capture List in a closure explicitly specifies which variables/constants from the surrounding scope the closure should capture and how. It's written inside square brackets `[]` before the parameter list.
  • Use Capture Lists to:
    • Control whether a variable is captured as a strong, weak, or unowned reference.
    • Avoid Strong Reference Cycles between a closure and an instance (e.g., `[weak self]`).
    • Change the mutability of captured values.
    class MyClass {
        var value = 0
        func doSomething() {
            let closure = { [weak self] in
                self?.value += 1
            }
            closure()
        }
    }
What is Generics in Swift? Why use them?
  • Generics allow you to write flexible, reusable functions and types that can work with any type, while still providing type safety at compile time. They avoid the need for casting and improve code clarity.
    func swapTwoValues(_ a: inout T, _ b: inout T) {
        let temporaryA = a
        a = b
        b = temporaryA
    }
What is the purpose of the `Any` and `AnyObject` types?
  • `Any`: Represents an instance of *any* type, including function types and optional types. Use with caution as it loses type information and requires casting.
  • `AnyObject`: Represents an instance of *any* class type. Used when you need to work with instances of arbitrary classes. Also requires casting.
  • Prefer using specific types or protocols whenever possible over `Any` and `AnyObject` for better type safety.
What is Error Handling in Swift?
  • Swift's error handling model allows you to define, throw, and catch errors. Errors are represented by types that conform to the `Error` protocol.
  • Key keywords:
    • `throw`: Indicates that a function or method can throw an error.
    • `throws`: Used in a function's signature to indicate it can throw an error.
    • `do-catch`: A block for handling potential errors.
    • `try`: Used when calling a throwing function within a non-throwing context (must be inside a `do` block or another throwing function).
    • `try?`: Attempts to call a throwing function and returns an Optional. If an error is thrown, it returns `nil`.
    • `try!`: Forces a throwing function to succeed. Crashes if an error is thrown. Use with extreme caution.
What is the difference between `try?` and `try!`?
  • `try?`: A safe way to call a throwing function. Returns an Optional value on success and `nil` on error.
  • `try!`: An unsafe way to call a throwing function. Returns the value on success but causes a runtime crash if an error is thrown.
What is a Deinitializer? When is it called?
  • A Deinitializer (`deinit`) is a special method defined in a class that is called just before an instance of that class is deallocated by ARC. It's used to perform any necessary cleanup before the instance is removed from memory (e.g., closing file handles, releasing resources).
  • It is automatically called when ARC determines that there are no more strong references to the instance.
What is the purpose of Extensions in Swift?
  • Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes adding computed properties, instance and type methods, initializers, subscripts, and making an existing type conform to a protocol. You cannot override existing functionality or add stored properties with extensions.
What is the difference between an Extension and a Subclass?
  • **Extension:** Adds new functionality to an *existing* type without modifying its original source code. Cannot override existing methods or add stored properties. Works for classes, structs, enums, and protocols.
  • **Subclass:** Creates a new class that *inherits* characteristics from a parent class. Can add new functionality, override inherited methods, and add stored properties. Only works for classes.
What is a Computed Property? Give an example.
  • A Computed Property calculates its value each time it is accessed, rather than storing a value directly. They are defined using `get` and optional `set` blocks.
    struct Rectangle {
        var width: Double
        var height: Double
        var area: Double {
            get {
                return width * height
            }
            // Optional setter
            set(newArea) {
                // Adjust width or height based on newArea
                width = sqrt(newArea)
                height = sqrt(newArea)
            }
        }
    }
What is a Stored Property? Give an example.
  • A Stored Property is a variable or constant that stores a value as part of an instance of a class or struct.
    struct Person {
        var name: String // Stored property
        let age: Int // Stored property
    }
What is the difference between a Computed Property and a Stored Property?
  • **Stored Property:** Stores a value directly in memory for each instance.
  • **Computed Property:** Calculates its value on demand using a getter (and optionally a setter). Does not store a value directly.
What are Property Observers? Give an example.
  • Property Observers (`willSet` and `didSet`) are special blocks that are called just before and immediately after the value of a stored property is set.
    var counter: Int = 0 {
        willSet(newValue) {
            print("About to set counter to \(newValue)")
        }
        didSet(oldValue) {
            print("Just set counter from \(oldValue) to \(counter)")
        }
    }
What is a Subscript? Why use it?
  • A Subscript allows instances of a type (class, struct, or enum) to be queried by writing values in square brackets immediately after the instance name, like calling a function but with bracket syntax. They are a shortcut for accessing elements of a collection, list, or sequence.
  • Use Subscripts to provide a clean and concise way to access elements of a type using index-based or key-based lookup.
What is the difference between a Method and a Function?
  • In Swift, the term "Function" generally refers to a standalone block of code that performs a task.
  • A "Method" is a function that is associated with a specific type (a class, struct, or enum). Methods are called on an instance of that type.
  • Essentially, a method is a function defined within the context of a type.
What is the difference between a Type Method and an Instance Method?
  • **Instance Method:** A method that is called on a specific *instance* of a type. It can access and modify instance properties.
  • **Type Method:** A method that is called on the *type itself*, not on an instance. Declared with the `static` or `class` keyword. Cannot access instance properties. Useful for factory methods or utility functions related to the type.
What is the difference between `static` and `class` keywords for type methods/properties?
  • `static`: Used for type properties and methods in both classes, structs, and enums. Static methods in classes cannot be overridden by subclasses. Static properties cannot be overridden.
  • `class`: Used only for type methods and computed type properties in *classes*. Class methods *can* be overridden by subclasses. Class stored properties are not allowed.
What is an Enum in Swift?
  • An Enum (enumeration) defines a common type for a group of related values. Enums in Swift are more powerful than in many other languages; they can have associated values, raw values, computed properties, and methods.
    enum CompassPoint {
        case north
        case south
        case east
        case west
    }
What are Raw Values and Associated Values in Enums?
  • **Raw Values:** Pre-populated values of the same type for each case of an enumeration. They are defined when the enumeration is declared.
    enum Planet: Int {
        case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
    }
  • **Associated Values:** Allow you to store custom values of any type alongside each case of an enumeration. The type of the associated value can be different for each case. Associated values are defined when you create the enumeration instance.
    enum Barcode {
        case upc(Int, Int, Int, Int)
        case qrCode(String)
    }
What is Pattern Matching in Swift?
  • Pattern matching is the process of checking for the structure of a value, such as the value of a variable or constant, or the elements of a complex type like a tuple or enum case, to see if it matches a certain pattern.
  • It's commonly used with `switch` statements and `if case` statements.
    let somePoint = (1, 1)
    switch somePoint {
    case (0, 0):
        print("Origin")
    case (_, 0):
        print("On the x-axis")
    case (0, _):
        print("On the y-axis")
    case (-2...2, -2...2):
        print("Inside the box")
    default:
        print("Outside the box")
    }
What is `defer` in Swift?
  • `defer` is a statement used to execute a block of code just before the current scope is exited. It's often used for cleanup tasks like closing file handles or releasing resources, ensuring that the cleanup happens regardless of how the scope is exited (e.g., normally or via an error being thrown).
    func processFile(filename: String) throws {
        let file = open(filename) // Assume open returns a handle
        defer {
            close(file) // This will be called when the function exits
        }
        // Process file...
        // If an error is thrown here, close(file) is still called
    }
What is the difference between `guard` and `if`?
  • `if`: Executes a block of code if a condition is true. The code after the `if` continues regardless of the condition.
  • `guard`: Requires a condition to be true to *continue execution*. If the condition is false, the `else` block must exit the current scope (using `return`, `break`, `continue`, or `throw`). `guard` is often used for early exit when preconditions are not met, making the main path of execution clearer.
    func process(user: User?) {
        guard let validUser = user else {
            print("Invalid user")
            return // Must exit
        }
        // Use validUser here
        print("Processing \(validUser.name)")
    }
What is the purpose of the `inout` keyword?
  • `inout` is used in a function's parameter list to indicate that the parameter is passed "in-out". This means the value is passed into the function, it can be modified *within* the function, and those modifications are persistent even after the function returns. It allows functions to modify the original value of an argument that would otherwise be passed by value (like structs or enums).
What is the difference between pass by value and pass by reference?
  • **Pass by Value:** A copy of the variable's value is passed to the function. Changes made to the parameter inside the function do not affect the original variable outside the function. Structs, enums, and standard types (Int, String, Bool, etc.) are passed by value.
  • **Pass by Reference:** A reference to the variable's location in memory is passed to the function. Changes made to the parameter inside the function *do* affect the original variable outside the function. Class instances are passed by reference. Using the `inout` keyword allows value types to be passed by reference for modification.
What is Method Overloading?
  • Method Overloading is the ability to define multiple functions or methods with the same name within the same scope, but with different parameter lists (different number of parameters, different parameter types, or different parameter labels). The compiler determines which specific function/method to call based on the arguments provided at the call site.
What is Method Overriding?
  • Method Overriding is the ability of a subclass to provide its own implementation for a method or property that is already defined in its superclass. In Swift, you use the `override` keyword to indicate that you are overriding a method, which helps prevent accidental overrides and ensures the compiler checks that the superclass indeed has a method with that signature.
What is the purpose of the `required` initializer?
  • The `required` keyword is used in a class initializer to indicate that every subclass of this class must implement this initializer. This is often used when defining initializers in protocols that classes must conform to, ensuring that any class adopting the protocol and its subclasses provide the necessary initializer.
What is the difference between `convenience` and `designated` initializers? (For classes)
  • **Designated Initializers:** The primary initializers for a class. They ensure that all properties introduced by the class are initialized and call up to a superclass designated initializer. Every class must have at least one designated initializer.
  • **Convenience Initializers:** Secondary initializers that must call a designated initializer from the *same* class. They are used to provide alternative, often simpler, ways to initialize an instance. They are marked with the `convenience` keyword.
  • Delegation rules:
    • A designated initializer must call up a designated initializer from its immediate superclass.
    • A convenience initializer must call *another* initializer from the *same* class.
    • A convenience initializer must ultimately call a designated initializer.
What is Failable Initializer? Give an example.
  • A Failable Initializer is an initializer that can potentially fail during the initialization process and return `nil`. It is declared by adding a `?` or `!` after the `init` keyword (`init?` or `init!`).
  • Use failable initializers when the conditions for creating a valid instance might not always be met (e.g., initializing a number from a string that might not be a valid number).
    struct Animal {
        let species: String
        init?(species: String) {
            if species.isEmpty { return nil }
            self.species = species
        }
    }
What is a Tuple in Swift?
  • A Tuple is a compound type that groups zero or more values of different types into a single compound value. You can access the values using their index or by giving them names.
    let http404Error = (404, "Not Found") // Tuple with unnamed elements
    let http200Status = (statusCode: 200, description: "OK") // Tuple with named elements
What is the difference between a Tuple and a Struct?
  • **Tuple:** Lightweight grouping of related values, typically used for temporary or simple structures. Elements are accessed by index or name. No custom methods or properties beyond the stored values.
  • **Struct:** Defines a formal type with a name. Can have stored properties, computed properties, methods, and conform to protocols. Provides stronger type safety and encapsulation for more complex data structures.
What is the purpose of the `typealias` keyword?
  • `typealias` is used to define an alias (an alternative name) for an existing type. This can improve code readability by providing more meaningful names for complex or frequently used types.
    typealias AudioSample = UInt16
    var maxAmplitudeFound = AudioSample.min
What is Optional Chaining? (Duplicate, but important)
  • Optional Chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be `nil`. If the optional contains a value, the property, method, or subscript call succeeds; if the optional is `nil`, the call gracefully fails and the entire expression evaluates to `nil`.
What is the Nil-Coalescing Operator? (Duplicate, but important)
  • The nil-coalescing operator (`a ?? b`) unwraps an optional `a` if it contains a value, or returns a default value `b` if `a` is `nil`. The expression `a` must be an Optional type, and `b` must be of the same type as the value wrapped inside `a`.
What is the purpose of the `convenience` keyword for initializers? (Duplicate)
  • (Already covered) Secondary initializers that must call a designated initializer from the same class.
What is a Value Type? Give examples.
  • A Value Type is a type whose instance's value is copied when it's assigned to a new variable or constant, or when it's passed to a function. Each variable or constant holds its own independent copy of the data.
  • Examples: Structs, Enums, Tuples, standard types like `Int`, `String`, `Bool`, `Array`, `Dictionary`, `Set`.
What is a Reference Type? Give examples.
  • A Reference Type is a type whose instance is stored in memory, and variables or constants hold a reference (a pointer) to that single instance. When you assign a reference type instance to a new variable or pass it to a function, a copy of the reference is made, but both references point to the *same* instance in memory.
  • Examples: Classes, Functions, Closures.
What is the difference between Value Types and Reference Types? (Duplicate, but important)
  • Value Types are copied when assigned or passed; Reference Types are referenced. Changes to a copied value type instance do not affect the original; changes to a referenced instance affect all references pointing to it.
What is Copy-on-Write?
  • Copy-on-Write (CoW) is an optimization used by Swift for certain value types like `Array`, `Dictionary`, and `Set`. Instead of making a full copy immediately when a value type instance is assigned or passed, only a reference is copied initially. The actual copying of the underlying data only happens when the instance is *mutated*. This improves performance by avoiding unnecessary copies.
What is the purpose of the `mutating` keyword?
  • The `mutating` keyword is used in the definition of an instance method within a `struct` or `enum` to indicate that the method can modify the properties of the instance. Because structs and enums are value types, their properties cannot be modified by default within instance methods unless the method is marked as `mutating`.
    struct Point {
        var x: Double
        var y: Double
        mutating func moveBy(x deltaX: Double, y deltaY: Double) {
            x += deltaX
            y += deltaY
        }
    }
What is a Higher-Order Function? Give examples in Swift.
  • A Higher-Order Function is a function that either takes one or more functions as arguments or returns a function as its result (or both).
  • Examples in Swift: `map`, `filter`, `reduce`, `sorted(by:)`.
    let numbers = [1, 2, 3, 4, 5]
    let squaredNumbers = numbers.map { $0 * $0 } // map is a higher-order function
Explain `map`, `filter`, and `reduce`.
  • `map`: Transforms each element of a collection according to a closure you provide and returns a new collection containing the transformed elements.
  • `filter`: Returns a new collection containing only the elements from the original collection for which a closure you provide evaluates to `true`.
  • `reduce`: Combines all elements in a collection into a single value, using an initial value and a combining closure.
What is Guard Let? (Duplicate, but important)
  • `guard let someValue = optionalValue else { ... }` is a control flow statement used for early exit. It safely unwraps an optional and assigns its value to `someValue` if it's not `nil`. If `optionalValue` is `nil`, the `else` block is executed, which *must* contain code to exit the current scope (`return`, `break`, `continue`, or `throw`). It makes the happy path of the code clearer.
What is If Let? (Duplicate, but important)
  • `if let someValue = optionalValue { ... }` is a conditional statement that safely unwraps an optional. If `optionalValue` is not `nil`, its value is assigned to `someValue`, and the code inside the `if` block is executed. If `optionalValue` is `nil`, the `if` block is skipped.
What is the difference between `Any` and `AnyObject`? (Duplicate, but important)
  • `Any`: Any type (struct, enum, class, function, optional).
  • `AnyObject`: Any class type.
What is Type Casting? How is it done in Swift?
  • Type Casting is a way to check the type of an instance and/or to treat that instance as a different superclass or subclass type.
  • Operators:
    • `is`: Checks if an instance is of a certain type. Returns `Bool`.
    • `as?`: Attempts to downcast an instance to a subclass type. Returns an Optional of the target type (`nil` if the cast fails). Safe casting.
    • `as!`: Forces downcasting an instance to a subclass type. Returns the instance of the target type or causes a runtime crash if the cast fails. Unsafe casting.
    • `as`: Used for upcasting (casting to a superclass, which is always safe) or for bridging between Swift and Objective-C types.
What is the difference between `as?` and `as!`?
  • `as?`: Safe downcasting. Returns an Optional. Use when you are not sure the cast will succeed.
  • `as!`: Forced downcasting. Returns the unwrapped type or crashes. Use only when you are absolutely certain the cast will succeed.
What is the purpose of the `associatedtype` keyword in Protocols?
  • `associatedtype` is used to declare a placeholder type name as part of a protocol's definition. The actual type to be used for that placeholder is specified by the type that conforms to the protocol. This allows the protocol to define requirements that use this placeholder type, making the protocol more flexible.
    protocol Container {
        associatedtype Item
        mutating func append(_ item: Item)
        var count: Int { get }
        subscript(i: Int) -> Item { get }
    }
What is Opaque Return Types (`some Protocol`)?
  • Opaque Return Types (using the `some` keyword before the return type) allow a function or method to return a value of a specific type, but without revealing the exact type to the caller. The caller only knows that the returned value conforms to a certain protocol. This is useful for hiding implementation details while still guaranteeing protocol conformance.
What is the difference between Opaque Return Types and returning a Protocol type?
  • **Returning `some Protocol` (Opaque Return Type):** The function returns a value of *a specific, concrete type* that conforms to the protocol, but the caller doesn't know the exact type. All calls to the function will return the *same* concrete type.
  • **Returning `Protocol`:** The function can return a value of *any type* that conforms to the protocol. The caller receives an existential container that can hold any conforming type.
  • Opaque return types offer better performance because the compiler knows the underlying concrete type, allowing for static dispatch.
What is the purpose of the `Codable` protocol?
  • `Codable` is a type alias for the `Encodable` and `Decodable` protocols. Conforming to `Codable` allows an object to be easily converted to and from external representations (like JSON or Property Lists) using encoders and decoders. Most standard Swift types are already `Codable`. For custom types, if all their stored properties are `Codable`, the compiler can often automatically synthesize the `Codable` conformance.
What is the difference between `Encodable` and `Decodable`?
  • `Encodable`: A protocol that allows a type to encode itself into an external representation (e.g., converting a Swift struct to JSON).
  • `Decodable`: A protocol that allows a type to decode itself from an external representation (e.g., converting JSON into a Swift struct).
  • `Codable` is the combined protocol.
What is Property Wrapper (`@propertyWrapper`)?
  • A Property Wrapper adds a layer of separation between code that manages how a property is stored or defined and the code that uses the property. You define a custom type that provides the storage and behavior for a property, and then apply it to properties using the `@` syntax. This is useful for encapsulating common property behaviors like lazy initialization, thread-safety, or data validation.
    @propertyWrapper
    struct Clamped {
        private var value: Int
        private let range: ClosedRange
    
        init(wrappedValue: Int, _ range: ClosedRange) {
            self.range = range
            self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
        }
    
        var wrappedValue: Int {
            get { value }
            set { value = min(max(newValue, range.lowerBound), range.upperBound) }
        }
    }
    
    struct MyData {
        @Clamped(1...100) var progress: Int = 0
    }
What is Result Type (`Result`)?
  • `Result` is an enum that represents either a success or a failure. It has two cases: `.success(Success)` which holds a value of the `Success` type, and `.failure(Failure)` which holds a value of the `Failure` type (which must conform to the `Error` protocol). It's a way to handle operations that can either succeed or fail without using exceptions or Optionals alone.
    func fetchData() -> Result {
        // ... operation that might succeed or fail
        if success {
            return .success(data)
        } else {
            return .failure(error)
        }
    }
What is the difference between using `Result` and `throws`?
  • `throws`: Indicates a function can throw an error that must be handled by the caller using `try`/`do-catch`. Best suited for synchronous operations where failure is an exceptional circumstance.
  • `Result`: Explicitly represents either a successful outcome or a specific failure type as the return value. Often used for asynchronous operations (like network requests) where the outcome might be delivered later, or when you want to return a specific error type as part of the function signature.
What is Async/Await in Swift?
  • Async/Await is Swift's structured concurrency model. It provides a way to write asynchronous code that looks and behaves more like synchronous code, making it easier to read, write, and reason about.
    • `async`: Marks a function or method that can perform asynchronous work and might suspend its execution.
    • `await`: Marks a point where the execution of an `async` function might suspend while waiting for an asynchronous operation to complete.
    func fetchUserData(id: Int) async throws -> User {
        let data = try await downloadData(from: urlForUser(id))
        let user = try decodeUser(from: data)
        return user
    }
What is a Task in Swift Concurrency?
  • A `Task` is the basic unit of work in Swift's structured concurrency. It represents an asynchronous operation that can be executed concurrently. You can create new tasks to run code asynchronously or await the results of existing tasks.
What is an Actor in Swift Concurrency?
  • An `Actor` is a reference type that allows for safe concurrent access to its mutable state. Actors provide isolation, ensuring that only one task can access an actor's mutable properties at a time, preventing data races. Accessing an actor's properties or methods from outside the actor is done asynchronously using `await`.
What is a Data Race? How do Actors help prevent them?
  • A Data Race occurs when multiple threads or tasks access the same mutable data simultaneously, and at least one of the accesses is a write. This can lead to unpredictable behavior and crashes.
  • Actors prevent data races by enforcing mutual exclusion: only one task can execute code inside an actor at any given time, ensuring that the actor's mutable state is accessed safely.
What is the purpose of the `@Sendable` attribute?
  • `@Sendable` is a marker attribute that indicates a closure or function can be safely passed across concurrency domains (e.g., between tasks or actors). Types captured by a `@Sendable` closure must also be `Sendable`. Value types are typically `Sendable`. Reference types need explicit conformance if they manage mutable state.
What is a Result Builder (`@resultBuilder`)?
  • A Result Builder is a type that transforms a sequence of component results into a single combined result. They are used with custom attributes (`@resultBuilder`) applied to functions or closures. SwiftUI uses result builders extensively to build view hierarchies.
    @resultBuilder
    struct MyBuilder {
        static func buildBlock(_ components: Int...) -> Int {
            components.reduce(0, +)
        }
    }
    
    @MyBuilder func buildSum() -> Int {
        1
        2
        3
    }
    let total = buildSum() // total is 6
What is SwiftUI?
  • SwiftUI is a declarative UI framework developed by Apple for building user interfaces across all Apple platforms with a single set of tools and APIs. You declare what your UI should look like based on the current state, and SwiftUI automatically updates the UI when the state changes.
What is the difference between SwiftUI and UIKit?
  • **Paradigm:** SwiftUI is declarative; UIKit is imperative. In SwiftUI, you describe the desired UI state. In UIKit, you write code to manipulate UI elements step-by-step.
  • **Platform:** SwiftUI is cross-platform (iOS, macOS, watchOS, tvOS) with a unified API. UIKit is platform-specific (iOS/tvOS vs AppKit for macOS).
  • **Code:** SwiftUI uses pure Swift code. UIKit primarily uses Objective-C or Swift with Objective-C interop.
  • **Views:** SwiftUI views are lightweight structs. UIKit views are heavier classes.
  • **State Management:** SwiftUI has built-in state management tools (`@State`, `@Binding`, `@ObservedObject`, `@EnvironmentObject`). UIKit relies on manual state management and patterns like MVC/MVVM.
What is `@State` in SwiftUI?
  • `@State` is a property wrapper used in SwiftUI to manage simple, local state within a single view. When a property marked with `@State` changes, SwiftUI automatically re-renders the view and any dependent views. State properties should be marked as `private`.
What is `@Binding` in SwiftUI?
  • `@Binding` is a property wrapper used in SwiftUI to create a two-way connection between a value and a view that displays and changes it. It allows a child view to modify a value that is owned by a parent view (or another source of truth) without the child view owning the data itself.
What is `@ObservedObject` in SwiftUI?
  • `@ObservedObject` is a property wrapper used in SwiftUI to subscribe to an observable object (a class that conforms to the `ObservableObject` protocol and uses `@Published` for its properties). When a `@Published` property on an `@ObservedObject` changes, SwiftUI automatically updates the view. Used for managing state that is shared between multiple views or has a longer lifetime than a single view.
What is `@EnvironmentObject` in SwiftUI?
  • `@EnvironmentObject` is a property wrapper used in SwiftUI to access an observable object that has been placed into the view hierarchy's environment. It provides a convenient way to share data across multiple views without explicitly passing it down through every level of the view hierarchy.
What is the difference between `@State`, `@Binding`, `@ObservedObject`, and `@EnvironmentObject`?
  • `@State`: Simple, local value types owned by a single view.
  • `@Binding`: Two-way connection to a value owned elsewhere. Doesn't own the data.
  • `@ObservedObject`: Reference to an observable object (class). Subscribes to changes. Owned by the view or a parent.
  • `@EnvironmentObject`: Reference to an observable object placed in the environment. Accessible by any view in that environment subtree without explicit passing.
What is `@Published` in SwiftUI?
  • `@Published` is a property wrapper used within a class that conforms to the `ObservableObject` protocol. When a property marked with `@Published` changes, it automatically emits a signal to its subscribers (views using `@ObservedObject` or `@EnvironmentObject`), triggering a view update.
What is the purpose of the `Identifiable` protocol in SwiftUI?
  • The `Identifiable` protocol requires a type to provide a stable identity for its instances, typically via an `id` property. This is crucial for SwiftUI lists and other views that display collections of data, as it allows SwiftUI to efficiently identify and update individual elements when the collection changes.
What is the difference between `struct SomeView: View` and `class SomeViewController: UIViewController`?
  • `struct SomeView: View`: A SwiftUI view. It's a value type, lightweight, and describes a piece of the UI based on state. SwiftUI manages its lifecycle.
  • `class SomeViewController: UIViewController`: A UIKit view controller. It's a reference type, manages a screen or section of the UI, handles user interactions, and manages the lifecycle of its views. You manually manage its lifecycle and updates.
What is the purpose of the `$` prefix in SwiftUI?
  • The `$` prefix is used to access the projected value of a property wrapper. For `@State` and `@ObservedObject`, the projected value is a `Binding` to the wrapped value. This allows you to pass a binding to a child view that needs to modify the state owned by the parent.
    @State private var isOn = false
    // Pass a binding to a Toggle view
    Toggle("Status", isOn: $isOn)
What is Swift Package Manager (SPM)?
  • Swift Package Manager is a tool for managing the distribution of Swift code. It's integrated into the Swift build system and helps automate the process of downloading, compiling, and linking dependencies for your projects.
What is the purpose of a Package.swift file?
  • `Package.swift` is the manifest file for a Swift Package. It describes the package's name, products (libraries, executables), targets (source code units), and dependencies on other packages.
What is the difference between a Library product and an Executable product in SPM?
  • **Library:** A module containing code intended to be imported and used by other targets.
  • **Executable:** A module that produces a runnable program. It typically has a `main.swift` file or a file with a top-level entry point.
What is Unit Testing in Swift? How is it done?
  • Unit Testing involves testing individual units of source code (functions, methods, classes, structs) in isolation to ensure they work correctly.
  • In Xcode, you use the XCTest framework to write unit tests. You create a test class that subclasses `XCTestCase`, write test methods starting with `test`, and use assertion functions (`XCTAssertEqual`, `XCTAssertNil`, etc.) to verify the expected behavior.
What is UI Testing in Swift? How is it done?
  • UI Testing involves testing the user interface of your application from the user's perspective. It simulates user interactions (taps, swipes, text input) and verifies that the UI responds correctly and displays the expected elements.
  • In Xcode, you also use the XCTest framework for UI testing. You record user interactions or write test code using `XCUIApplication` and `XCUIElement` APIs to interact with UI elements and make assertions about their state.
What is Continuous Integration (CI) in the context of Swift development?
  • Continuous Integration is a development practice where developers frequently integrate their code changes into a shared repository (e.g., Git). Automated builds and tests are run after each integration to detect integration issues early. CI systems (like Jenkins, CircleCI, GitHub Actions, GitLab CI) can be configured to build and test Swift projects.
What is Continuous Deployment (CD) in the context of Swift development?
  • Continuous Deployment is a practice that extends CI. After code changes are integrated and pass automated tests, they are automatically deployed to production or staging environments without manual intervention. While less common for mobile apps (due to app store review processes), CD principles can be applied to internal testing builds or server-side Swift applications.
What is the purpose of the `Codable` protocol? (Third time's the charm!)
  • (Already covered) A type alias for `Encodable` and `Decodable`, enabling easy conversion to/from external data formats like JSON.
What is the difference between an Optional and a non-Optional variable?
  • **Non-Optional:** Guaranteed to always hold a value of its declared type. Accessing its value is direct.
  • **Optional:** May or may not hold a value. Requires unwrapping before accessing its value to handle the possibility of `nil`.
What is the purpose of the `guard` statement? (Duplicate)
  • (Already covered) Provides early exit from a scope if a condition is false, making the happy path clearer.
What is the difference between `if let` and `guard let`? (Duplicate)
  • (Already covered) `if let` executes a block if unwrapping succeeds; `guard let` requires unwrapping to succeed to continue execution, otherwise it exits the scope.
What is the purpose of the `defer` statement? (Duplicate)
  • (Already covered) Executes a block of code just before the current scope exits, useful for cleanup.
What is the difference between a Protocol and an Abstract Class (in languages that support them)?
  • Swift doesn't have abstract classes in the same way some other languages do.
  • **Protocol:** Defines a *contract* of requirements. Conforming types (classes, structs, enums) provide their own implementation. No shared state is defined in the protocol itself (though protocol extensions can add computed properties). Supports multiple inheritance of behavior via protocol conformance.
  • **Abstract Class:** A class that cannot be instantiated directly. Defines common behavior and properties (including stored properties and implemented methods) that subclasses inherit. Can define abstract methods that subclasses *must* implement. Supports single inheritance.
  • Protocols are generally preferred in Swift for defining shared behavior due to their flexibility and applicability to value types.
What is the purpose of the `open` and `public` access control levels?
  • **`public`:** Entities declared as `public` can be accessed from any source file within their defining module and from any source file in another module that imports the defining module. They can be subclassed or overridden only *within* their defining module.
  • **`open`:** Applies only to classes and class members. Entities declared as `open` can be accessed from any source file within their defining module and from any source file in another module that imports the defining module. Additionally, `open` classes can be subclassed, and `open` class members can be overridden by subclasses in *any* module. `open` is the least restrictive access level and is typically used for framework APIs intended for subclassing/overriding outside the framework.
What is the difference between `internal` and `fileprivate` access control levels?
  • **`internal`:** The default access level. Entities declared as `internal` can be accessed from any source file *within* their defining module, but not from outside that module.
  • **`fileprivate`:** Entities declared as `fileprivate` can be accessed only from within their own defining source file.
What is the purpose of the `private` access control level?
  • `private`: Entities declared as `private` can be accessed only from within the scope they are defined (e.g., within a class or struct definition). Code within the same source file but outside the scope cannot access it.
Explain Swift's String type. Is it a value type or a reference type?
  • Swift's `String` type is a **value type**. When you copy a string, a new independent copy of the string data is conceptually made. However, Swift uses Copy-on-Write optimization for efficiency, so the actual copying might be delayed until the string is modified.
Explain Swift's Array type. Is it a value type or a reference type?
  • Swift's `Array` type is a **value type**. Like `String`, it uses Copy-on-Write optimization.
Explain Swift's Dictionary type. Is it a value type or a reference type?
  • Swift's `Dictionary` type is a **value type**. It also uses Copy-on-Write optimization.
What is the purpose of the `lazy` keyword?
  • `lazy` is a property modifier. A `lazy` stored property's initial value is not calculated until the first time it is accessed. This is useful for properties whose initial value is computationally expensive or requires other parts of the instance to be fully initialized.
    class DataManager {
        lazy var expensiveData: [Int] = {
            print("Calculating expensiveData")
            return (1...1000000).map { $0 * 2 }
        }() // Closure is executed only when expensiveData is first accessed
    }
What is the difference between `nil` and `.None`?
  • There is no practical difference. `nil` is the preferred and more readable way to indicate the absence of a value for an Optional. `.None` is the corresponding case name within the `Optional` enumeration type itself. `nil` is syntax sugar for `.None`.
What is the purpose of the `isEmpty` property on collections?
  • The `isEmpty` property is a boolean property available on collections like `Array`, `Dictionary`, and `Set`. It efficiently checks if the collection contains any elements. It's generally preferred over checking `count == 0` as it can be more performant, especially for collections that don't need to calculate their full count to determine if they are empty.
What is a Range operator? Give examples.
  • Range operators define a range of values.
    • **Closed Range Operator (`a...b`):** Defines a range that includes both `a` and `b`.
      for index in 1...5 { ... } // Includes 1, 2, 3, 4, 5
    • **Half-Open Range Operator (`a..for index in 1..<5 { ... } // Includes 1, 2, 3, 4
    • **One-Sided Ranges (`a...`, `...b`, `..array[2...] // From index 2 to the end array[...4] // From the start up to and including index 4 array[..<4] // From the start up to but not including index 4
What is the purpose of the `@escaping` attribute for closures?
  • `@escaping` is an attribute applied to a closure parameter in a function's signature. It indicates that the closure may be stored and executed *after* the function that takes the closure as an argument returns. Non-escaping closures (the default) are executed within the function's body and return before the function returns.
  • Use `@escaping` when a closure is stored in a property, added to a dispatch queue, or used in asynchronous operations.
What is a Result Type? (Duplicate)
  • (Already covered) An enum representing either a success or a failure, often used for operations that might fail.
What is the difference between `fatalError()` and `precondition()`?
  • `fatalError(_:file:line:)`: Stops execution unconditionally and prints a message. Used when the state is unrecoverable and the app cannot continue. Always crashes in both debug and release builds.
  • `precondition(_:_:file:line:)`: Checks a condition. If the condition is false, it stops execution and prints a message. Used to check conditions that *must* be true for the code to function correctly. Crashes in both debug and release builds if the condition is false.
What is the difference between `assert()` and `precondition()`?
  • `assert(_:_:file:line:)`: Checks a condition. If the condition is false, it stops execution and prints a message. Used for debugging purposes to check conditions that *should* be true but might indicate a programming error if false. Only checked in *debug* builds; ignored in release builds.
  • `precondition(_:_:file:line:)`: Checked in *both* debug and release builds. Use for conditions that, if false, indicate a serious unrecoverable error.
What is the purpose of the `AnyHashable` type?
  • `AnyHashable` is a type-erased wrapper that allows you to store values of different types that conform to the `Hashable` protocol within the same collection (like a `Set` or as keys in a `Dictionary`). You can wrap a value of any `Hashable` type inside an `AnyHashable` instance.
What is the purpose of the `Codable` protocol? (Fourth time's the charm!)
  • (Already covered) Enables easy encoding and decoding of types to/from external data formats.
What are Key Paths? Give an example.
  • Key Paths (`\Type.property`) are references to a property of a type. They allow you to refer to a property dynamically without knowing its name at compile time.
  • Use Key Paths for:
    • Generic functions that operate on properties.
    • Accessing properties through a variable.
    • Observing property changes (e.g., with KVO or Combine).
    struct Person {
        var name: String
        var age: Int
    }
    
    let person = Person(name: "Alice", age: 30)
    let nameKeyPath = \Person.name
    
    let name = person[keyPath: nameKeyPath] // Accessing property using key path
What is the difference between a WritableKeyPath and a ReferenceWritableKeyPath?
  • `WritableKeyPath`: A key path to a writable property of any type (`Root`), including value types. When used with a value type instance, accessing or modifying the property via the key path operates on a copy of the instance.
  • `ReferenceWritableKeyPath`: A key path to a writable property of a *reference type* (`Root`, must be a class). When used with a reference type instance, accessing or modifying the property via the key path operates directly on the instance itself, modifying the original.
What is the purpose of the `Combine` framework?
  • Combine is Apple's declarative framework for handling asynchronous events over time. It provides a way to process values from publishers (sources of values) and respond to them using operators and subscribers. It's often used for handling UI events, network responses, and other asynchronous streams of data.
What are Publishers and Subscribers in Combine?
  • **Publisher:** A type that can emit a sequence of values over time. Publishers can emit zero or more values, followed by either a completion event (success) or a failure event (error).
  • **Subscriber:** A type that can receive values and completion/failure events from a publisher. Subscribers initiate the flow of data by requesting values from a publisher.
What is the purpose of Operators in Combine?
  • Operators are methods defined on Publishers that transform or process the values emitted by the publisher. They receive values from an upstream publisher, apply a transformation or behavior, and then emit values to a downstream subscriber (or another operator). Examples: `map`, `filter`, `debounce`, `throttle`, `sink`, `assign`.
What is the purpose of the `sink` operator in Combine?
  • `sink(receiveCompletion:receiveValue:)` is a subscriber operator. It creates a subscriber that receives values and completion/failure events from a publisher and executes closures when those events occur. It's a common way to attach closures to the end of a Combine pipeline to handle the final output.
What is the purpose of the `assign(to:on:)` operator in Combine?
  • `assign(to:on:)` is another subscriber operator. It automatically assigns the values received from a publisher to a specified key path of a given object. This is often used to bind a publisher's output to a property of an `@ObservedObject` or `@State` property in SwiftUI.
What is the difference between `async let` and `Task`?
  • `async let`: Creates a child task that starts immediately and implicitly awaits its result later in the function. Used when you want to run multiple asynchronous operations concurrently within a structured context and wait for all of them. Provides structured concurrency.
  • `Task`: Creates a new, potentially unstructured task. `Task.init` creates a detached task (unstructured). `Task.detached` also creates a detached task. You can use `Task` to bridge between synchronous and asynchronous code or create long-running background tasks. Less structured than `async let`.
What is the purpose of the `Sendable` protocol? (Duplicate)
  • (Already covered) Marks a type as safe to pass across concurrency domain boundaries.
What is the purpose of the `GlobalActor` attribute?
  • `@GlobalActor` is an attribute used to define a global actor. A global actor is a singleton actor that can be used to protect global mutable state or provide a single point of synchronization for a specific domain (e.g., `@MainActor` for the main thread). Code marked with the global actor attribute is automatically executed on that actor.
What is the `@MainActor` attribute?
  • `@MainActor` is a global actor provided by the Swift standard library. It represents the main thread. You use `@MainActor` to mark functions, properties, or entire types that must always be executed on the main thread to ensure thread safety for UI updates or other main-thread-only operations. Accessing `@MainActor` code from another actor or task requires using `await`.
What is the purpose of the `actor` keyword? (Duplicate, but important)
  • (Already covered) Used to define an actor, a reference type that provides isolated mutable state for safe concurrent programming.
What is the difference between a `class` and an `actor`?
  • `class`: A general-purpose reference type. Does not inherently protect its mutable state from concurrent access, requiring manual synchronization (locks, queues) to avoid data races.
  • `actor`: A specialized reference type for concurrency. Automatically protects its mutable state, ensuring that only one task can access it at a time. Accessing an actor's isolated state from outside the actor is done asynchronously using `await`.
What is the purpose of the `async sequence`?
  • An Async Sequence is a sequence that can produce elements asynchronously over time. You can iterate over an async sequence using a `for await in` loop. This is useful for processing streams of data that arrive over time, such as lines from a file, network packets, or events. Types conforming to the `AsyncSequence` protocol produce elements asynchronously.
What is the purpose of the `CheckedContinuation` and `UnsafeContinuation` types?
  • These types are used to bridge between asynchronous code written using older completion-handler-based APIs and Swift's new `async/await` structured concurrency. They allow you to suspend an `async` function and resume it later when the completion handler of the older API is called. `CheckedContinuation` performs runtime checks to ensure it's resumed exactly once, while `UnsafeContinuation` does not and is less safe but potentially faster in very specific scenarios.
What is the purpose of the `TaskGroup`?
  • A `TaskGroup` allows you to create and manage a dynamic set of child tasks. You can add new child tasks to the group and await their results as they complete. Task groups are useful when you have a variable number of concurrent operations to perform and want to collect their results. They provide structured concurrency, ensuring all tasks within the group complete before the group finishes.
What is the difference between `async let` and `TaskGroup`?
  • `async let`: Used for a *fixed, known* number of concurrent child tasks. The tasks start immediately. You await each task's result individually.
  • `TaskGroup`: Used for a *dynamic, variable* number of concurrent child tasks. You add tasks to the group as needed and can iterate over the results as they become available.
What is the purpose of the `@MainActor` attribute? (Duplicate)
  • (Already covered) Ensures code runs on the main thread for UI updates and other main-thread operations.
What is the purpose of the `Sendable` protocol? (Fifth time's the charm!)
  • (Already covered) Marks a type as safe to pass across concurrency domain boundaries. Essential for preventing data races in concurrent code.
What is the difference between `final class` and a class without `final`?
  • `final class`: A class marked with `final` cannot be subclassed. This is an optimization hint to the compiler and can prevent unintended inheritance.
  • Class without `final`: Can be subclassed.
What is the purpose of the `override` keyword? (Duplicate)
  • (Already covered) Indicates that a method or property in a subclass is providing its own implementation for something defined in the superclass. Required for safety.
What is the purpose of the `required` initializer? (Duplicate)
  • (Already covered) Forces all subclasses to implement a specific initializer.
What is the difference between `nil` and empty string (`""`)?
  • `nil`: Means the Optional variable currently holds *no value* at all.
  • Empty String (`""`): Means the String variable holds a valid `String` value, but that string contains zero characters.
  • They represent different states: absence of a value vs. presence of an empty value.
What is the purpose of the `where` clause in Swift?
  • The `where` clause is used to add constraints to:
    • Generic type parameters (e.g., requiring a generic type to conform to a protocol or be a subclass of a specific class).
    • Associated types in protocols.
    • Pattern matching in `switch` statements or `if case` statements (to add additional conditions).
    func process(_ container: C) where C.Item: Equatable {
        // ...
    }
What is the purpose of the `fallthrough` keyword in switch statements?
  • `fallthrough` is used in a `switch` case to explicitly transfer control to the first statement of the *next* case, without checking the condition for that next case. Unlike in some other languages, Swift's `switch` statements do not fall through by default. Use with caution as it can make code harder to read.