112 Swift Interview Questions and Answers (2026)

Blog / 112 Swift Interview Questions and Answers (2026)
Swift interview questions and answers

Swift isn't just for iOS anymore. As AI workloads push teams toward fast, type-safe backends, more companies now lean on Swift to ship Python-adjacent APIs and ML services, and interviewers expect real fluency, not memorized buzzwords. Walk in shaky on optionals, ARC, or concurrency and you'll lose the offer to someone who didn't.

This guide fixes that. You get 112 questions with concise, interview-ready answers, code where it actually helps, worked from Junior to Mid to Senior so you build from fundamentals up to actors, type erasure, and strict concurrency. Work through it and you'll walk in ready to explain, not guess.

Q1.
Explain 'Optional Chaining' vs. 'Nil-Coalescing'.

Junior

Optional chaining safely reaches into a possibly-nil value and short-circuits to nil if any link is nil; nil-coalescing supplies a default when an optional is nil. They are complementary: chain to navigate, coalesce to provide a fallback.

  • Optional chaining (?.):

    • Calls a property/method/subscript only if the base is non-nil; otherwise the whole expression evaluates to nil.

    • Its result is always optional.

  • Nil-coalescing (??):

    • a ?? b returns a unwrapped if non-nil, else b.

    • It unwraps and removes the optionality, producing a concrete value.

  • They combine naturally: Chain to dig in, then coalesce to land on a guaranteed value.

swift

let count = user?.profile?.name.count ?? 0 // chaining yields Int?, ?? turns it into a non-optional Int

Q2.
What does the defer keyword do, and what is its execution order in a nested scope?

Junior

defer schedules a block to run when the current scope exits, no matter how it exits (normal return, throw, or early return). It is ideal for cleanup like closing files or releasing locks. When multiple defer blocks exist, they run in reverse (LIFO) order.

  • Guarantees execution on scope exit: Runs even when the function throws or returns early, so cleanup is never skipped.

  • LIFO ordering:

    • Within one scope, the last defer registered runs first, mirroring stack unwinding.

    • In nested scopes, each scope's defers run as that inner scope exits, before the outer scope's defers.

  • Scoped to its enclosing block: A defer inside a loop body or if runs at the end of that iteration/block, not the whole function.

swift

func demo() { defer { print("A") } defer { print("B") } print("body") } // prints: body, B, A (reverse order)

Q3.
What is the difference between guard and if let, and why is guard preferred for early exits?

Junior

Both unwrap optionals, but if let binds the value only inside its {} block, while guard let binds it for the rest of the enclosing scope and requires you to exit on failure. guard is preferred for early exits because it keeps the happy path unindented and validates preconditions up front.

  • if let:

    • The unwrapped binding is scoped to the if body only.

    • No mandatory else; nesting several can create a 'pyramid of doom'.

  • guard let:

    • The binding stays valid in the surrounding scope after the guard.

    • Its else must leave the scope (return, throw, break, continue), enforced by the compiler.

  • Why guard is preferred for early exits:

    • Handles the failure case first and flatly, leaving the main logic at the top indentation level.

    • Communicates intent: this is a precondition that must hold to continue.

swift

func greet(_ name: String?) { guard let name = name, !name.isEmpty else { return } print("Hello, \(name)") // name usable here, no nesting }

Q4.
How do you define a custom error type in Swift, and how does the Error protocol work?

Junior

You define a custom error by creating a type (usually an enum) that conforms to the Error protocol. Error is an empty marker protocol: conforming to it simply lets a type be thrown and caught.

  • Enums are the idiomatic choice: Each case models a distinct failure, and associated values carry context.

  • The Error protocol has no requirements: It's a marker that integrates the type with throw, try, and catch.

  • Add LocalizedError for user-facing messages: Provide errorDescription so the error renders nicely.

  • Any type can conform: structs and classes work too, not just enums.

swift

enum NetworkError: Error { case badURL case statusCode(Int) } func load() throws { throw NetworkError.statusCode(404) } do { try load() } catch NetworkError.statusCode(let code) { print("Failed with \(code)") }

Q5.
What are the primary differences between a struct and a class in Swift?

Junior

The core difference is that structs are value types (copied on assignment) while classes are reference types (shared by reference). This single distinction drives most of their other behavioral differences.

  • Copy vs. reference: Assigning or passing a struct copies it; each holder gets an independent value. Classes share one instance.

  • Inheritance: Classes support inheritance; structs do not (they use protocols and composition instead).

  • Identity: Classes have identity comparable with ===; structs only have value equality.

  • Mutability: Struct methods that change state need mutating; classes mutate freely even when held in a let.

  • Memory and lifetime: Structs typically live on the stack and have no ARC overhead; classes live on the heap and are reference-counted.

  • Defaults: Structs get a memberwise initializer automatically; classes do not.

Q6.
Why would you choose a struct over a class for a data model?

Junior

Choose a struct for a data model when the data represents a value rather than a shared entity with identity: structs give you safe copying, predictable mutation, and less concurrency risk by default. Swift's guidance is to prefer structs unless you specifically need class features.

  • Value semantics by default: Passing the model around can't cause spooky action at a distance; mutations are local.

  • Thread safety: No shared mutable instance means fewer data races without locks.

  • Free conveniences: Automatic memberwise initializer, easy Equatable/Hashable/Codable synthesis for plain data.

  • Performance: No ARC overhead for small models; often stack-allocated.

  • When to use a class instead: You need shared identity (one instance observed everywhere), inheritance, or interop with Objective-C and reference-based frameworks.

Q7.
What are tuples in Swift, and when would you use one instead of a struct?

Junior

A tuple groups multiple values into a single compound value without defining a named type; reach for one for lightweight, throwaway groupings and a struct when the grouping is meaningful and reused.

  • What tuples are:

    • Ad hoc combinations of values, optionally with element labels: (code: Int, message: String).

    • Value types you can destructure: let (x, y) = point.

  • Use a tuple when:

    • Returning multiple values from a function (e.g. minMax() -> (min: Int, max: Int)).

    • The grouping is local, temporary, and doesn't need a name.

  • Prefer a struct when:

    • The type is reused across the codebase or part of a public API.

    • You need methods, computed properties, protocol conformance, or mutability control.

    • There are more than 2-3 elements: named fields read far better than positional ones.

Q8.
Explain the difference between a function and a closure in Swift.

Junior

Functions and closures are the same kind of thing in Swift (both are self-contained blocks of callable code), but a closure is the more general form: functions are just named closures, while closures more commonly refer to unnamed blocks that can capture surrounding state.

  • They share a type system: Both have function types like (Int) -> String and can be passed, stored, and returned as first-class values.

  • Functions:

    • Declared with func, have a name, and use named parameters.

    • Are technically a special case of closures.

  • Closures (closure expressions):

    • Written inline with { (params) -> Return in ... }, often anonymous.

    • Capture and store references to variables/constants from their defining context (hence "closure": they close over their environment).

    • Support shorthand: type inference, $0 argument names, implicit return, trailing syntax.

Q9.
Explain 'Trailing Closure Syntax' and why it is used.

Junior

Trailing closure syntax lets you write a closure that is the last argument of a function outside and after the parentheses, making call sites read more naturally, especially for callbacks and DSL-like APIs.

  • How it works:

    • When the final parameter is a closure, you can move it out of the (); if it's the only argument, you can omit the parentheses entirely.

    • Multiple trailing closures (Swift 5.3+) label the additional ones: the first is unlabeled.

  • Why it's used:

    • Readability: long closure bodies don't get buried inside an argument list.

    • It's idiomatic in the standard library (map, filter) and in SwiftUI's declarative syntax.

swift

// Without trailing closure let sorted = numbers.sorted(by: { $0 < $1 }) // With trailing closure (parentheses dropped) let sorted2 = numbers.sorted { $0 < $1 } // Multiple trailing closures UIView.animate(withDuration: 0.3) { view.alpha = 0 } completion: { _ in view.removeFromSuperview() }

Q10.
What is a 'Higher-Order Function'? Give examples from the Swift Standard Library.

Junior

A higher-order function is one that takes another function/closure as a parameter and/or returns a function; it lets you abstract over behavior, not just data, which is the foundation of functional-style code in Swift.

  • Definition: Possible because functions are first-class values in Swift, so they can be passed and returned like any other value.

  • Standard library examples:

    • map: transforms each element into a new value.

    • filter: keeps elements matching a predicate.

    • reduce: combines all elements into a single value.

    • compactMap, flatMap, sorted(by:), forEach.

  • Why they matter: Express intent declaratively and avoid manual loop boilerplate and mutable accumulator state.

swift

let nums = [1, 2, 3, 4] let doubled = nums.map { $0 * 2 } // [2, 4, 6, 8] let evens = nums.filter { $0 % 2 == 0 } // [2, 4] let total = nums.reduce(0, +) // 10

Q11.
What is the Comparable protocol, and how does it relate to Equatable?

Junior

The Comparable protocol defines types whose values have an ordering, so they can be compared with <, >, <=, and >=. It builds on Equatable, which only defines equality (==).

  • Inheritance relationship: Comparable refines Equatable: any Comparable type is also Equatable.

  • What you implement:

    • You only need to provide static func < ; the standard library derives >, <=, and >= from it.

    • You also need == for Equatable, which the compiler can synthesize for structs/enums.

  • Why it matters: Conforming unlocks free functionality: sorted(), min(), max(), and range operators.

  • Consistency contract: Your < must define a strict total order that agrees with ==, otherwise sorting and lookups behave unpredictably.

swift

struct Version: Comparable { let major: Int, minor: Int static func < (lhs: Version, rhs: Version) -> Bool { (lhs.major, lhs.minor) < (rhs.major, rhs.minor) } // == is synthesized automatically }

Q12.
What is the difference between Any and AnyObject?

Junior

Both are special protocol-like types representing values of unknown type, but Any can hold an instance of absolutely any type while AnyObject only holds instances of class (reference) types.

  • Any:

    • Can represent any value: structs, enums, functions, closures, optionals, and class instances.

    • Useful for heterogeneous collections like [Any].

  • AnyObject:

    • Only reference types (class instances); value types don't conform unless bridged.

    • Used in protocols restricted to classes: protocol P: AnyObject enables weak references.

  • Practical takeaway: Prefer concrete types or generics; reach for Any/AnyObject only when interoperating with untyped APIs (e.g. Objective-C).

Q13.
What is the difference between the as?, as!, and is operators when type casting?

Junior

All three deal with type relationships at runtime: is tests membership, as? attempts a safe cast returning an optional, and as! force-casts and crashes on failure.

  • is: Returns a Bool: checks whether an instance is of a given type or conforms to a protocol.

  • as? (conditional cast): Returns an optional: the value if the cast succeeds, nil otherwise. Safe; pairs well with if let.

  • as! (forced cast): Returns a non-optional but triggers a runtime crash if the cast fails. Use only when failure is a programmer error.

  • Note: plain as (no ?/!) is for guaranteed upcasts/bridging known at compile time.

swift

if pet is Dog { /* ... */ } if let dog = pet as? Dog { dog.bark() } // safe let dog = pet as! Dog // crashes if not a Dog

Q14.
What is a 'Retain Cycle,' and what are the two primary ways to break one?

Junior

A retain cycle (strong reference cycle) occurs when two or more objects hold strong references to each other so their reference counts never reach zero, leaking memory; you break it by making one of the references weak or unowned.

  • The problem: ARC frees an object when its strong count hits 0; mutual strong references keep each count at 1, so neither is ever deallocated.

  • Break it with weak: Optional, automatically set to nil when the target deallocates. Use when the reference can legitimately become absent (e.g. a delegate).

  • Break it with unowned: Non-optional, no nil-ing. Use when the referenced object is guaranteed to outlive this one.

  • Choosing: If lifetimes are independent, prefer weak; if the dependency is strict and the cycle's partner can't outlive the owner, unowned avoids optionals.

Q15.
What is the purpose of deinit, and when is it called?

Junior

deinit is a deinitializer: a block that runs automatically just before a class instance is deallocated, letting you perform cleanup. It exists only on classes (reference types), not structs or enums.

  • Purpose: release resources: Close files, sockets, observers (NotificationCenter), cancel timers, or free non-ARC resources.

  • When it's called:

    • Automatically by ARC when the instance's reference count drops to zero.

    • You never call deinit directly; you cannot deallocate manually.

  • Class-only: Value types are copied, not reference-counted, so they have no deinitializer.

  • Common use: diagnosing leaks: If deinit never fires, you likely have a retain cycle (fix with weak or unowned).

swift

class FileHandleWrapper { let handle: FileHandle init(handle: FileHandle) { self.handle = handle } deinit { handle.closeFile() } // runs before deallocation }

Q16.
What is the difference between a 'Lazy' property and a 'Computed' property?

Junior

A lazy property is stored: it computes its value once on first access and keeps it. A computed property stores nothing: it runs its getter (and optional setter) every time it's accessed. So the difference is caching versus recomputation.

  • Lazy property:

    • Declared with lazy var; value is initialized once and reused.

    • Best when the value is expensive but constant after creation.

    • Must be a var; not thread-safe on first access.

  • Computed property:

    • No backing storage; the body runs on every access, always reflecting current state.

    • Best when the value derives from other properties that change.

    • Can be let-like read-only, or read-write with a set block.

  • Quick rule: Compute once and cache: lazy. Always reflect latest inputs: computed.

swift

struct Circle { var radius: Double lazy var initialLabel = "Circle r=\(radius)" // fixed after first read var area: Double { .pi * radius * radius } // recomputed each access }

Q17.
What are property observers (willSet and didSet), and when are they triggered?

Junior

Property observers are blocks that run code in response to changes to a stored property's value: willSet fires just before the new value is stored, and didSet fires immediately after. They let you react to changes without writing a full computed property.

  • willSet: Called before the value changes; the incoming value is available as newValue (default name).

  • didSet: Called after the value changes; the previous value is available as oldValue.

  • When they trigger:

    • On any assignment, even if the new value equals the old.

    • NOT during initialization (setting the property in an init does not fire them).

    • On stored properties only; can also be added when overriding an inherited property.

  • Gotcha: Setting the property again inside didSet does not re-trigger the observer (avoids infinite loops).

swift

var score: Int = 0 { willSet { print("changing to \(newValue)") } didSet { print("changed from \(oldValue) to \(score)") } }

Q18.
Explain the Codable protocol and the difference between Encodable and Decodable.

Junior

Codable is a type alias for Encodable & Decodable, the protocols that let Swift types convert to and from external representations (JSON, plist, etc.). Encodable handles serialization out; Decodable handles deserialization in.

  • Encodable: Requires encode(to: Encoder); used by encoders like JSONEncoder to turn an instance into data.

  • Decodable: Requires init(from: Decoder); used by decoders like JSONDecoder to build an instance from data.

  • Why split them: Some types only need one direction: a response model you only decode can be Decodable only; a request body you only send can be Encodable only.

  • Automatic synthesis: The compiler generates both conformances for free when every stored property is itself Codable; you override only when mapping or logic is custom.

Q19.
What does the reduce function do, and how does it differ from map and filter?

Junior

reduce combines all elements of a sequence into a single accumulated value using a closure, whereas map and filter return a new collection of the same or fewer elements.

  • reduce(_:_:):

    • Takes an initial value and a closure (accumulator, element) -> accumulator; folds many values into one.

    • Result type can differ from the element type (e.g. sum to Int, join to String).

  • map: Transforms each element 1-to-1, returning an array of the same count.

  • filter: Keeps elements matching a predicate, returning a same-or-smaller array.

  • Relationship:

    • reduce is the most general: map and filter can both be expressed as reductions.

    • Use reduce(into:_:) when accumulating into a mutable collection to avoid repeated copies.

swift

let nums = [1, 2, 3, 4] let sum = nums.reduce(0, +) // 10 (many -> one) let doubled = nums.map { $0 * 2 } // [2,4,6,8] (1-to-1) let evens = nums.filter { $0 % 2 == 0 } // [2,4] (subset)

Q20.
What are 'Associated Values' vs. 'Raw Values' in Swift Enums?

Junior

Raw values are fixed, compile-time constants of one type attached to every case; associated values are per-instance data of potentially different types stored when a case is created. They are mutually exclusive: an enum uses one or the other, not both.

  • Raw values:

    • Declared with a backing type, e.g. enum Suit: String; each case maps to one literal known at compile time.

    • Give you a failable initializer init?(rawValue:) and a .rawValue property, useful for serialization.

  • Associated values:

    • Attach payload data per instance, e.g. case success(Data) or case error(code: Int, message: String).

    • Extracted via pattern matching in switch or if case.

swift

enum Planet: Int { case mercury = 1, venus, earth } // raw values enum Result { // associated values case success(Data) case failure(Error) }

Q21.
How does Swift implement Optionals under the hood?

Mid

An Optional<Wrapped> is just an enum with two cases, .none and .some(Wrapped): the compiler and runtime layer syntactic sugar and storage optimizations on top of it.

  • It is a standard-library enum:

    • Defined roughly as enum Optional<Wrapped> { case none; case some(Wrapped) }.

    • Syntax like Int?, nil, and x! are sugar for this enum, its .none case, and forced .some extraction.

  • Storage is not always a separate flag:

    • For most value types it stores the payload plus a discriminator bit/tag.

    • For reference types and other types with spare bit patterns the compiler applies enum layout optimization: it reuses an invalid value (like the all-zero pointer) to mean .none, so ClassType? is the same size as ClassType.

  • Unwrapping is pattern matching: if let and switch simply match the two cases; ! traps if the case is .none.

Q22.
Explain how Optional Chaining works. What happens to the return type of a function call if you use optional chaining on the instance?

Mid

Optional chaining lets you call into an optional with ?.: if the instance is non-nil the call proceeds, and if it is nil the whole chain short-circuits and returns nil without crashing. The key rule is that the result is always wrapped in an Optional, even if the method itself returns a non-optional.

  • How it works:

    • Evaluation stops at the first nil link and yields nil for the entire expression.

    • You can chain multiple levels; a single ? propagates optionality through the rest.

  • Effect on return type:

    • A method returning T becomes T? when called through chaining.

    • A method already returning T? stays T? (it does not become T??): the optionals are flattened.

    • A Void-returning call becomes Void?, which you can compare against nil to check if it ran.

swift

// name() returns String (non-optional) let n = user?.name() // type is String? if user?.save() != nil { /* save() ran because user was non-nil */ }

Q23.
What is an implicitly unwrapped optional, and when is it appropriate to use one?

Mid

An implicitly unwrapped optional (T!) is an optional that may be nil but is automatically force-unwrapped on access, trapping if it happens to be nil. It is appropriate when a value is logically guaranteed to be set shortly after initialization but can't be set in the initializer itself.

  • What it is:

    • Still an Optional under the hood; the ! just tells the compiler to unwrap automatically at each use site.

    • Accessing it while nil crashes, exactly like force unwrapping.

  • When it's appropriate:

    • Interface Builder outlets (@IBOutlet), which are connected after init.

    • Two-phase / dependency-injected setup where the property is wired up immediately after construction.

    • Bridging APIs that are typed as nullable but are known to be non-nil by contract.

  • When to avoid it: Any value that genuinely might be absent: use a normal optional and unwrap safely instead.

Q24.
What is the difference between throws and rethrows? When would a function need to be marked as rethrows?

Mid

throws marks a function that can itself produce errors; rethrows marks a function that only throws if a closure argument passed to it throws. rethrows lets the compiler treat the function as non-throwing when called with non-throwing closures.

  • throws: The function may originate errors on its own; callers must use try.

  • rethrows:

    • The function never throws by itself; it only forwards errors thrown by a closure parameter.

    • Called with a non-throwing closure, it is treated as non-throwing, so no try is required.

  • When to use rethrows:

    • Higher-order functions that take a closure and merely propagate its errors, like map or filter.

    • It gives the cleanest call site: callers pay the try cost only when their closure can actually throw.

swift

func transform<T>(_ x: Int, _ f: (Int) throws -> T) rethrows -> T { return try f(x) } let a = transform(2) { $0 * 2 } // no try needed let b = try transform(2) { try parse($0) } // try required

Q25.
How does the Result type improve error handling compared to try-catch?

Mid

Result<Success, Failure> makes success and failure explicit values you can store, pass, and return, rather than control flow you must immediately handle. It is especially useful for asynchronous callbacks and deferred error handling where try/catch can't span across closures.

  • Errors become first-class values: A Result can be returned, stored in a property, or held in an array; an error thrown by try must be handled at the throw site.

  • Fits async callbacks: A completion handler can deliver one Result parameter instead of an awkward value-plus-error pair where both could be nil.

  • Type-safe failures: The Failure type is part of the signature, documenting exactly what can go wrong.

  • Composable: map, flatMap, and mapError let you transform the chain without unwrapping, and get() bridges back to try when convenient.

swift

func load(completion: (Result<Data, NetworkError>) -> Void) { ... } load { result in switch result { case .success(let data): handle(data) case .failure(let error): report(error) } }

Q26.
What is the difference between try, try?, and try! when calling a throwing function?

Mid

All three call a throwing function, but they differ in how errors are handled: try propagates the error, try? converts it to an optional, and try! asserts no error will occur and crashes if one does.

  • try: Used inside a do/catch block or a throwing function; if the call throws, the error propagates up to the nearest handler.

  • try?:

    • Turns the result into an optional: you get the value on success, nil on failure, discarding the error.

    • Good when you only care whether it worked, not why it failed.

  • try!:

    • Disables error propagation: a thrown error becomes a runtime crash.

    • Only use when failure is genuinely impossible (e.g. loading a known bundled resource).

swift

let a = try parse(text) // propagates error let b = try? parse(text) // b is optional, nil on failure let c = try! parse(text) // crashes if it throws

Q27.
What does the mutating keyword do in a struct, and why isn't it needed for classes?

Mid

The mutating keyword marks a method on a value type (struct or enum) that modifies the instance's properties. It's required because value-type methods can't change self by default; classes don't need it because they're reference types and mutate shared state through a pointer.

  • Structs are value types: Their methods are non-mutating by default; the compiler forbids changing properties unless you opt in with mutating.

  • What mutating actually does:

    • It allows the method to reassign properties or even reassign self entirely.

    • You can't call a mutating method on a let constant, since that instance is immutable.

  • Why classes don't need it: A class instance is shared by reference; mutating a property changes the object everyone points to, so there's nothing to opt into.

swift

struct Counter { var count = 0 mutating func increment() { count += 1 } }

Q28.
Explain the concept of Value Semantics and why it is safer for multi-threaded environments.

Mid

Value semantics means each variable owns an independent copy of its data, so mutating one never affects another. This is safer for concurrency because there's no shared mutable state to race over: each thread can hold its own copy.

  • What value semantics guarantees: Passing a value to another function or thread gives it a logically separate instance; changes don't propagate back.

  • Why it helps multithreading:

    • Data races require shared mutable state. With copies, two threads can't accidentally observe each other's mutations.

    • You avoid whole classes of bugs without locks for the data itself.

  • Copy-on-write keeps it cheap: Standard library types (Array, Dictionary, String) share storage until one side mutates, then copy.

  • Caveat: A struct only has true value semantics if all its stored properties do; a class reference inside breaks it.

Q29.
What are the memory and performance tradeoffs of using a struct (value type) versus a class (reference type)?

Mid

Structs are usually cheaper and faster for small data because they avoid heap allocation and reference counting, while classes are better when an instance is large or must be shared, since copying it would be expensive or semantically wrong.

  • Struct advantages:

    • Often stack-allocated, no heap allocation cost.

    • No ARC retain/release traffic, so no atomic reference-counting overhead.

    • Better optimizer and cache locality for small, fixed-size data.

  • Struct costs:

    • Copying a large struct duplicates all its bytes; passing big structs around can be expensive.

    • Copy-on-write mitigates this for collections but adds a uniqueness check.

  • Class advantages:

    • Copying a reference is just copying a pointer, regardless of object size.

    • Natural fit when many parts of the app must share and observe one instance.

  • Class costs: Heap allocation plus ARC overhead, and risk of retain cycles and shared-mutation bugs.

  • Rule of thumb: Small, copyable data: struct. Large or shared-identity data: class.

Q30.
What is the difference between an @escaping closure and a non-escaping closure?

Mid

A non-escaping closure is guaranteed to run before the function returns, while an @escaping closure may outlive the call (stored or run later), which forces different memory and capture semantics.

  • Non-escaping (the default):

    • Called synchronously and discarded before the function returns.

    • Can stay on the stack: no extra heap allocation, and you don't need self. qualification.

  • Escaping:

    • Stored for later (a property, an array) or run after return (async callbacks, completion handlers).

    • Must be marked @escaping; the compiler heap-allocates the captured state to keep it alive.

    • References to self are captured strongly, so risk of retain cycles: use a [weak self] capture list.

  • Why the distinction matters: Non-escaping is the optimizer-friendly default; marking @escaping is an explicit signal that the closure may outlive the call.

swift

var handlers: [() -> Void] = [] func store(_ handler: @escaping () -> Void) { handlers.append(handler) // outlives the call: must be @escaping } func run(_ work: () -> Void) { work() // runs now: non-escaping is fine }

Q31.
What is an @autoclosure, and when would you use one?

Mid

An @autoclosure automatically wraps an argument expression in a closure, so the caller passes a plain expression but it isn't evaluated until (and unless) the function actually invokes it: this enables lazy evaluation with clean syntax.

  • What it does:

    • Turns f(someExpression) into f({ someExpression }) behind the scenes.

    • The body runs only when the function calls the parameter.

  • When to use it:

    • Deferring potentially expensive work: assert(condition, message) only builds the message if the assertion fails.

    • Short-circuit operators like && and || are built with autoclosures.

  • Use sparingly: It hides that an argument is a closure, which can surprise readers; reserve it for genuine lazy-evaluation cases.

swift

func logIfNeeded(_ message: @autoclosure () -> String, enabled: Bool) { if enabled { print(message()) } // expensive string only built when enabled } logIfNeeded(expensiveDescription(), enabled: false) // not evaluated

Q32.
What is a capture list in a closure, and what is the difference between capturing a value as a constant vs. a reference?

Mid

A capture list is the bracketed list at the start of a closure (e.g. [weak self, count]) that explicitly controls how outside variables are captured; capturing as a value snapshots it at definition time, while capturing a reference keeps a live link to the object.

  • Default capture: Without a capture list, closures capture variables by reference, seeing later mutations.

  • Capturing a value (constant):

    • Listing a value type in the brackets ([count]) copies it at the moment the closure is created.

    • Later changes to the original variable don't affect the captured copy.

  • Capturing a reference:

    • For class instances, the closure holds the object; [weak self] or [unowned self] avoid strong retain cycles.

    • weak yields an optional that becomes nil if deallocated; unowned assumes it still exists (crashes if not).

swift

var x = 1 let byValue = { [x] in print(x) } // captures snapshot: 1 let byRef = { print(x) } // captures variable x = 99 byValue() // prints 1 byRef() // prints 99

Q33.
What is Protocol-Oriented Programming (POP), and how does it differ from Object-Oriented Programming?

Mid

POP is a paradigm that models behavior and abstractions through protocols and protocol extensions rather than class inheritance, favoring composition of small protocols and value types over deep class hierarchies.

  • Core idea: Define capabilities as protocols, give shared behavior via protocol extensions, and let any type (including struct and enum) adopt them.

  • Differences from OOP:

    • Composition over inheritance: a type can conform to many protocols, but a class has a single superclass.

    • Works with value types, so you get value semantics rather than shared reference state.

    • Retroactive conformance: you can extend types you don't own to adopt a protocol.

  • Why it matters: Avoids the fragile base class and diamond problems, and keeps abstractions small and testable.

Q34.
Explain the concept of Protocol-Oriented Programming. How do protocol extensions allow for 'default implementations'?

Mid

Protocol-Oriented Programming builds abstractions from protocols, and protocol extensions let you attach concrete method/property implementations to a protocol so conforming types get that behavior for free unless they override it.

  • How default implementations work:

    • A protocol declares a requirement; an extension of that protocol provides the body.

    • Conforming types inherit the implementation automatically and may supply their own to override it.

  • Static vs dynamic dispatch caveat:

    • If a method is declared in the protocol, the conforming type's override is called via dynamic dispatch through the protocol.

    • If it exists ONLY in the extension (not a protocol requirement), it's dispatched statically by the variable's declared type, which can surprise you.

  • Constrained extensions: Use where clauses to provide defaults only when associated types meet a constraint.

swift

protocol Greeter { func greet() } extension Greeter { func greet() { print("Hello") } // default implementation } struct Friendly: Greeter {} // gets greet() for free

Q35.
Explain the difference between Protocol Inheritance and Protocol Composition.

Mid

Protocol inheritance means one protocol extends another so conformers must satisfy both, while protocol composition means requiring a value to conform to several independent protocols at once at a use site, without creating a new protocol.

  • Protocol inheritance:

    • Declared as protocol B: A; B is a permanent supertype relationship and any conformer to B must also satisfy A.

    • Defines a richer protocol once, reusable as a named type.

  • Protocol composition:

    • Written ad hoc with A & B (e.g. a parameter type), combining requirements only where needed.

    • No new type is created and the relationship isn't permanent.

  • When to use: Inherit when the combined concept is a reusable named abstraction; compose when you just need a one-off intersection of capabilities.

swift

protocol Named { var name: String { get } } protocol Aged { var age: Int { get } } protocol Person: Named, Aged {} // inheritance: new named protocol func describe(_ x: Named & Aged) { } // composition: ad hoc

Q36.
How do you use type constraints in generics to ensure a type conforms to multiple protocols (e.g., Equatable & Codable)?

Mid

You apply multiple protocol requirements to a generic placeholder using composition with & or a where clause, so the type must conform to all listed protocols before the code compiles.

  • Composition syntax: Write <T: Equatable & Codable> directly in the angle brackets.

  • where clause:

    • Equivalent and clearer for many constraints: <T> where T: Equatable, T: Codable.

    • Also lets you constrain associated types, e.g. where T.Element: Hashable.

  • Effect: Inside the function you can use members from every required protocol, and conformance is checked at compile time with static dispatch.

swift

func store<T: Equatable & Codable>(_ value: T, equals other: T) throws { if value == other { // from Equatable let data = try JSONEncoder().encode(value) // from Codable save(data) } } // equivalent with a where clause func store2<T>(_ value: T) where T: Equatable, T: Codable { }

Q37.
How do you make a custom type conform to Hashable, and what is the role of the hash(into:) method?

Mid

A type conforms to Hashable so its values can be used as dictionary keys or in sets. You implement hash(into:), which feeds the type's essential properties into a Hasher to produce a hash value, while Equatable (a requirement of Hashable) defines equality.

  • Role of hash(into:):

    • You call hasher.combine(_:) on each property that matters; the Hasher mixes them into a single value.

    • Replaced the older hashValue property because it lets the standard library control the hashing algorithm (with per-run seeding).

  • The core invariant:

    • Equal values must produce equal hashes: every property used in == should also be fed into hash(into:).

    • Including fewer properties is allowed (causes more collisions); including extra ones can violate the invariant.

  • Synthesis: For structs/enums with all-Hashable members, the compiler synthesizes both == and hash(into:) automatically.

swift

struct User: Hashable { let id: UUID let name: String func hash(into hasher: inout Hasher) { hasher.combine(id) // identity is what matters } static func == (l: User, r: User) -> Bool { l.id == r.id } }

Q38.
What is the difference between Static Dispatch and Dynamic Dispatch?

Mid

Static dispatch resolves which method to call at compile time, while dynamic dispatch resolves it at runtime via a lookup. Static is faster and inlinable; dynamic enables polymorphism and overriding.

  • Static (direct) dispatch:

    • Compiler knows the exact function, so it can inline and optimize.

    • Used for value types (structs/enums), final classes/methods, and static/global functions.

  • Dynamic dispatch:

    • Class methods use a vtable (witness table for protocols); the implementation is chosen at runtime based on the actual type.

    • Objective-C interop via @objc dynamic uses message passing (even slower, but supports KVO/swizzling).

  • Why it matters: Dynamic dispatch has overhead and blocks inlining; mark classes/methods final or use value types for performance-critical code.

Q39.
Explain the difference between weak and unowned references. In what specific scenario would you choose unowned over weak?

Mid

Both weak and unowned are non-owning references that break retain cycles, but weak is optional and auto-nils when the target deallocates, whereas unowned is non-optional and assumes the target always outlives it.

  • weak:

    • Must be an Optional var; ARC sets it to nil when the referenced object is freed, so it's always safe to access.

    • Use when the other object can legitimately become nil or have a shorter/independent lifetime (delegates).

  • unowned:

    • Non-optional, not nil-tracked; accessing it after the target is deallocated crashes (dangling reference).

    • Slightly faster and lets you avoid optional unwrapping.

  • When to choose unowned: When the referenced object is guaranteed to live at least as long as the reference: e.g. a Customer owns a CreditCard, and the card holds an unowned reference back because a card never exists without its customer.

Q40.
How do closures cause memory leaks, and how does a capture list (e.g., [weak self]) prevent a retain cycle?

Mid

Closures are reference types that strongly capture the variables they use. A leak (retain cycle) happens when an object holds a closure and that closure strongly captures the same object back, so neither's reference count ever reaches zero. A capture list like [weak self] breaks the cycle by capturing weakly.

  • How the cycle forms: e.g. a view controller stores a closure property, and the closure references self; each keeps the other alive forever.

  • [weak self]:

    • Captures self as an optional weak reference that becomes nil when the object deallocates, so the closure no longer keeps it alive.

    • Often paired with guard let self else { return } to safely unwrap.

  • [unowned self]: Also breaks the cycle but is non-optional; use only when the closure can't outlive self.

  • Note: not every capturing closure leaks; only retained closures (stored properties, escaping ones held elsewhere) that capture back create a cycle.

swift

networkClient.fetch { [weak self] result in guard let self else { return } self.update(result) // no retain cycle }

Q41.
Swift uses Automatic Reference Counting (ARC). How does this conceptually differ from the Garbage Collection used in languages like Java or Kotlin?

Mid

ARC and tracing garbage collection both free unused memory, but ARC counts references and frees objects deterministically the instant their count hits zero, with work done at compile time, whereas a GC periodically scans the heap at runtime to find unreachable objects.

  • Timing / determinism:

    • ARC: deallocation happens immediately when the last strong reference goes away, so deinit timing is predictable.

    • GC: collection runs on its own schedule, so object lifetimes (and finalizers) are non-deterministic.

  • Runtime cost:

    • ARC spreads small retain/release costs throughout execution; no separate collector thread or pause.

    • GC can introduce pauses (stop-the-world) and uses extra memory headroom, but amortizes bookkeeping.

  • Cycle handling:

    • Tracing GCs reclaim reference cycles automatically.

    • ARC cannot: cycles leak unless broken manually with weak or unowned.

Q42.
What happens to an unowned reference if the object it points to is deallocated?

Mid

An unowned reference does not keep the object alive and is not set to nil when the object deallocates, so accessing it after deallocation is a runtime error (a crash), not a graceful failure.

  • No automatic nil-ing: Unlike weak, the reference still points at the (now freed) memory location.

  • Access after dealloc traps: Safe unowned (the default) keeps a side-table check and triggers a deterministic crash, which is safer than reading garbage.

  • Variants: unowned(unsafe) skips the check and can read freed memory (undefined behavior), like a dangling pointer.

  • Implication for use: Only use unowned when the reference is guaranteed to outlive or match the lifetime of the holder.

Q43.
How do you identify and resolve a retain cycle in a closure?

Mid

A closure retain cycle happens when a closure captures self strongly while the object also holds the closure strongly; you break it with a capture list using [weak self] or [unowned self].

  • Identify it:

    • Look for a stored closure property (or escaping closure) that references self and whose object's deinit never runs.

    • Common with completion handlers, Combine sinks, and Timer callbacks stored on the object.

  • Resolve with a capture list:

    • [weak self]: self becomes optional; guard let self else { return } to use it safely.

    • [unowned self]: only when self is guaranteed alive for the closure's lifetime.

  • Note: non-escaping closures don't cause cycles: They run and release before the function returns, so no capture list is needed (e.g. map).

swift

loadData { [weak self] result in guard let self else { return } self.update(result) }

Q44.
In what specific scenario would you prefer unowned over weak?

Mid

Prefer unowned when the referenced object is guaranteed to exist for the entire lifetime of the reference, so it can never be nil and you want to avoid the cost and ceremony of unwrapping an optional.

  • Strict lifetime dependency: Classic case: a child object that cannot outlive its parent/owner, e.g. Customer and CreditCard where a card always belongs to a customer.

  • Avoids optional handling: No guard let or optional chaining, so access is cleaner where nil is logically impossible.

  • Slight performance edge: weak requires side-table lookups and zeroing; unowned is cheaper.

  • The trade-off: If you're wrong about the lifetime guarantee, you get a crash; when in doubt, use weak.

Q45.
What is a retain cycle, and how do you identify one without using instruments?

Mid

A retain cycle is when objects strongly reference each other (directly or via a captured closure) so ARC never deallocates them; you can spot it without Instruments mainly by reasoning about the reference graph and confirming deinit is never called.

  • Use deinit as a probe: Add a print in deinit; if it never fires when the object should go away, something is retaining it.

  • Audit strong references:

    • Look for two objects each holding the other strongly, or a stored/escaping closure capturing self without [weak self].

    • Common culprits: delegates declared as strong, parent/child back-pointers, timers, and completion handlers.

  • Memory Graph Debugger: Xcode's Debug Memory Graph (not Instruments) visually shows the retain relationships and flags leaks with purple warnings.

  • Fix: Make one side weak or unowned so the cycle is broken.

Q46.
When is it dangerous to use unowned?

Mid

It's dangerous to use unowned whenever the referenced object can be deallocated before the reference is accessed, because reading a stale unowned reference traps the program.

  • Independent or uncertain lifetimes: If the target can outlive or die before the holder, you have no guarantee, so a crash is possible.

  • Asynchronous / escaping closures: A network callback or delayed work may run after self is gone; [unowned self] there is a common crash source: use [weak self].

  • unowned(unsafe): Skips runtime checks entirely, reading freed memory: undefined behavior rather than a clean crash.

  • Rule of thumb: Use unowned only for a provable, strict lifetime relationship; otherwise default to weak.

Q47.
What is the difference between a 'Designated Initializer' and a 'Convenience Initializer'?

Mid

A designated initializer is a primary initializer that fully initializes all of a class's stored properties and calls its superclass init; a convenience initializer is a secondary helper that must ultimately delegate to a designated one. Designated init is the backbone; convenience init is sugar on top.

  • Designated initializer:

    • Initializes every stored property the class introduces and delegates up to the superclass's designated init.

    • Every class needs at least one (sometimes inherited).

  • Convenience initializer:

    • Marked with the convenience keyword; provides shortcuts/defaults.

    • Must call another initializer in the same class and eventually a designated one.

  • Delegation rules: Designated delegates UP (to superclass); convenience delegates ACROSS (within the same class).

swift

class Food { let name: String init(name: String) { self.name = name } // designated convenience init() { self.init(name: "Unnamed") } // convenience -> across }

Q48.
What are Property Wrappers, and what problem do they solve?

Mid

A property wrapper is a reusable type that adds custom behavior (storage, validation, transformation) to a property, letting you factor out boilerplate that would otherwise be repeated in every getter/setter. You define the logic once and apply it with an @ attribute.

  • The problem they solve: Repeated access patterns: clamping, thread-safety locks, UserDefaults persistence, lazy loading, change tracking.

  • How you declare one: A struct/class annotated @propertyWrapper that exposes a wrappedValue property.

  • Familiar examples: SwiftUI's @State, @Published, @Binding are all property wrappers.

  • Optional projected value: A projectedValue (accessed via $) can expose extra API, like a binding.

swift

@propertyWrapper struct Clamped { private var value: Int let range: ClosedRange<Int> init(wrappedValue: Int, _ range: ClosedRange<Int>) { 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 Volume { @Clamped(0...100) var level = 50 }

Q49.
When would you use a lazy stored property, and what are the thread-safety implications of using one?

Mid

Use a lazy stored property when its initial value is expensive to compute or depends on other state, and may not always be needed: it's computed only on first access and then stored. The key caveat is that it is not thread-safe.

  • When to use it:

    • Costly setup (large object, heavy computation, file/network read) you want to defer.

    • Value depends on self and can't be set at init time.

  • Rules:

    • Must be a var (its value is set after init, so it can't be let).

    • Initialized exactly once, on first read; the closure/expression runs lazily.

  • Thread-safety implications:

    • No built-in synchronization: if two threads first-access it simultaneously, the initializer may run more than once (race), producing duplicate/inconsistent values.

    • Guard concurrent access yourself (a lock, serial queue, or initialize before sharing across threads).

swift

class Report { lazy var data: [String] = expensiveLoad() // runs on first access only func expensiveLoad() -> [String] { /* heavy work */ [] } }

Q50.
What is an inout parameter, and how does it work under the hood?

Mid

An inout parameter lets a function mutate a caller's variable and have those changes persist after the call returns. It is not pass-by-reference in the C sense: Swift uses copy-in copy-out semantics.

  • Syntax and call site:

    • Declare with func f(_ x: inout Int) and pass with an ampersand: f(&value).

    • The argument must be a mutable l-value (a var, not a let or literal).

  • Under the hood: copy-in, copy-out:

    • The value is copied into the parameter, the function mutates the local copy, and on return the copy is written back to the original.

    • The compiler may optimize this to pass the address directly when safe, but you should reason about it as copy-in/copy-out, not guaranteed aliasing.

  • Restrictions:

    • Cannot be combined with default values or variadics.

    • Passing the same variable to two inout parameters causes exclusive-access (overlapping access) violations.

  • Use it sparingly: prefer returning a new value; reach for inout when in-place mutation is clearer or cheaper.

Q51.
What is a failable initializer, and when would you use init?() versus init()?

Mid

A failable initializer can return nil when initialization cannot succeed, producing an Optional instance. You write it as init?(); the plain init() must always succeed and produce a fully initialized value.

  • init?() (failable):

    • Returns nil on invalid input, so the caller gets T?.

    • Use when valid construction depends on the arguments: validating ranges, parsing strings, enum raw values (Direction(rawValue:)).

  • init() (non-failable):

    • Guarantees a valid instance every time, so callers need no unwrapping.

    • Use when any inputs are acceptable or you can supply sensible defaults.

  • There is also init!(), an implicitly unwrapped failable init: prefer init?() unless interop forces the implicitly unwrapped form.

  • Rule of thumb: if failure is a normal, expected outcome, make it failable; if failure means a programmer error, use a non-failable init and validate elsewhere.

swift

struct Temperature { let celsius: Double init?(kelvin: Double) { guard kelvin >= 0 else { return nil } // physically invalid celsius = kelvin - 273.15 } } Temperature(kelvin: -5) // nil

Q52.
What is a required initializer, and why is it needed in a class hierarchy?

Mid

A required initializer is one that every subclass must implement (or inherit), guaranteeing the initializer exists for all types in the hierarchy. It is essential for polymorphic construction and for protocol conformances that demand a specific initializer.

  • Declaration and override:

    • Mark it required init() on the class; subclasses must write required init() again (not override).

    • A subclass can satisfy it automatically if it inherits the initializer without adding new required init logic.

  • Why it's needed:

    • Construction through a metatype (type.init() where type is dynamic) requires the initializer to exist on every concrete subclass.

    • Protocols with initializer requirements force conforming classes to mark that init required so subclasses also satisfy the protocol.

  • Contrast with a normal initializer, which a subclass may simply not have once it defines its own initializers.

swift

protocol Makeable { init() } class Base: Makeable { required init() {} // required by the protocol } class Sub: Base { required init() { super.init() } // must restate required }

Q53.
What is a 'KeyPath' in Swift and how does it differ from a direct property access?

Mid

A KeyPath is a type-safe, reusable reference to a property (or chain of properties) that you can store, pass around, and apply to instances later. Direct property access happens immediately and is fixed at the call site; a KeyPath is a first-class value representing "which property" without naming a specific instance.

  • Syntax: Written with a backslash: \Person.name; apply with subscript person[keyPath: \.name].

  • Family of types: KeyPath (read-only), WritableKeyPath (value-type mutation), ReferenceWritableKeyPath (mutate via a class reference).

  • Why it differs from direct access:

    • It defers the access: you decide which property now and on which instance later.

    • Enables generic, data-driven APIs: sorted(by:) helpers, map(\.name), SwiftUI bindings, key-value observing.

  • Type-safe: the compiler checks the property exists and infers root and value types, unlike string-based key paths.

swift

struct Person { let name: String; var age: Int } let people = [Person(name: "Ann", age: 30)] let names = people.map(\.name) // ["Ann"] let kp: KeyPath<Person, Int> = \.age people[0][keyPath: kp] // 30

Q54.
What is the difference between map, flatMap, and compactMap?

Mid

All three transform elements of a sequence (or optional), but they differ in how they handle the result shape. map transforms one-to-one, flatMap transforms then flattens nested sequences, and compactMap transforms and drops nil results.

  • map:

    • Applies a closure to each element, returning a collection of the same count.

    • [1,2,3].map { $0 * 2 }[2,4,6].

  • compactMap:

    • Closure returns an Optional; nil results are discarded and the rest unwrapped.

    • ["1","x","3"].compactMap { Int($0) }[1,3].

  • flatMap:

    • Closure returns a sequence; results are concatenated into one flat collection.

    • [[1,2],[3]].flatMap { $0 }[1,2,3].

  • Note on Optionals: The old flatMap that removed nil was renamed to compactMap; Optional and Publisher types still have their own flatMap for monadic chaining.

Q55.
How does the Codable protocol handle the mapping between JSON keys and Swift property names when they don't match?

Mid

When JSON keys differ from Swift property names, you provide a nested CodingKeys enum that maps each property to its JSON string. The synthesized Codable implementation reads that enum instead of using property names directly.

  • CodingKeys enum:

    • A String-backed enum conforming to CodingKey; cases match property names, raw values match JSON keys.

    • You only need to override the cases that differ, but you must list every property you want encoded/decoded.

  • Key strategies on the coder: For a uniform style, set keyDecodingStrategy = .convertFromSnakeCase (and the encoding counterpart) instead of writing every key by hand.

  • Full custom control: For complex mismatches (nested containers, computed values), implement init(from:) and encode(to:) manually using keyed containers.

swift

struct User: Codable { let firstName: String let id: Int enum CodingKeys: String, CodingKey { case firstName = "first_name" // remapped case id // matches JSON "id" } }

Q56.
What is the difference between Equatable and Hashable, and why does one inherit from the other?

Mid

Equatable defines when two values are considered equal (==); Hashable additionally provides a hash value so values can be used in sets and as dictionary keys. Hashable inherits from Equatable because hashing only makes sense alongside a consistent notion of equality.

  • Equatable: Requires static func == ; answers "are these two the same value?".

  • Hashable: Adds hash(into:); enables Set membership and Dictionary keys via fast bucket lookup.

  • Why one inherits the other:

    • Hash-based collections first compare hashes, then fall back to == to resolve collisions, so equality is mandatory.

    • The contract: equal values must produce equal hashes (the reverse need not hold). Violating it breaks set/dictionary behavior.

  • Synthesis: the compiler generates both automatically when all stored properties conform, so you rarely write them by hand.

Q57.
Explain the performance differences between an Array, a Set, and a Dictionary.

Mid

Each picks a different trade-off: Array gives ordered, indexed storage with fast access by position; Set and Dictionary are hash-based, giving fast membership and key lookup but no defined order.

  • Array (ordered, contiguous):

    • Index access and append at the end are O(1) amortized.

    • Searching by value (contains) or inserting/removing in the middle is O(n).

  • Set (unordered, unique, hashed):

    • Insert, remove, and membership (contains) are O(1) average.

    • Requires elements to be Hashable; no ordering and no duplicates.

  • Dictionary (unordered, hashed key/value):

    • Lookup, insert, and delete by key are O(1) average.

    • Keys must be Hashable; iteration order is not guaranteed.

  • Rule of thumb:

    • Need order or index access: use Array. Need fast uniqueness/membership: use Set. Need keyed lookup: use Dictionary.

    • Hash O(1) is average case: collisions or bad hashing can degrade it, and constant factors are higher than a plain array.

Q58.
What are subscripts in Swift, and how do you define a custom subscript?

Mid

A subscript lets you access an element of a type with bracket syntax (instance[key]), and you define one with the subscript keyword, optionally with a getter and setter.

  • Syntax:

    • Declared like a computed property but with parameters: subscript(index: Int) -> Element.

    • Provide get (read) and optionally set (write); read-only can omit get.

  • Flexible:

    • Can take any number of parameters of any type (e.g. a 2D grid grid[row, col]).

    • Can be overloaded by parameter type, and defined as static type subscripts.

  • Used by the standard library: Array and Dictionary expose their access through subscripts.

swift

struct Grid { var storage: [[Int]] subscript(row: Int, col: Int) -> Int { get { storage[row][col] } set { storage[row][col] = newValue } } } var g = Grid(storage: [[1,2],[3,4]]) g[0, 1] = 9 // calls the setter

Q59.
How do you overload an operator or define a custom operator in Swift?

Mid

You overload an existing operator by writing a static function named after the operator on your type; you define a brand-new operator by first declaring it with operator and a precedence group, then implementing it.

  • Overloading an existing operator:

    • Implement a static func with the operator symbol as the name, e.g. static func + (lhs: Vector, rhs: Vector) -> Vector.

    • Conform to protocols like Equatable or AdditiveArithmetic rather than reinventing semantics.

  • Defining a custom operator:

    • Declare it: infix operator, prefix operator, or postfix operator.

    • Infix operators may specify a precedencegroup controlling precedence and associativity.

  • Caution: Custom operators hurt readability if cryptic; prefer named methods unless the operator is genuinely intuitive.

swift

struct Vector { var x, y: Double } static func + (lhs: Vector, rhs: Vector) -> Vector { Vector(x: lhs.x + rhs.x, y: lhs.y + rhs.y) } infix operator .*: MultiplicationPrecedence static func .* (lhs: Vector, rhs: Vector) -> Double { lhs.x * rhs.x + lhs.y * rhs.y // dot product }

Q60.
What does the final keyword do, and how does it affect performance?

Mid

final prevents a class, method, or property from being overridden or subclassed, which both expresses intent and lets the compiler replace dynamic dispatch with faster static or inlined dispatch.

  • What it does:

    • On a class: forbids subclassing. On a member: forbids overriding in subclasses.

    • Compile-time error if anything tries to override a final declaration.

  • Performance:

    • Non-final class methods use dynamic dispatch via a vtable; final guarantees the implementation, enabling direct (static) dispatch and inlining.

    • The win is usually small but real in hot paths; it removes the indirection cost per call.

  • Notes:

    • Structs and enums can't be subclassed anyway, so final applies to classes.

    • Whole-module optimization can sometimes infer finality, but marking it is explicit and reliable.

Q61.
What is the difference between the static and class keywords for type-level members in a class?

Mid

Both declare a member on the type itself rather than on instances, but class members can be overridden by subclasses while static members cannot. In effect, static is the non-overridable form and class enables polymorphism for type-level members.

  • static:

    • Final by definition: subclasses cannot override it. Works in classes, structs, and enums.

    • Required for stored type properties; class stored properties are not allowed.

  • class:

    • Only valid in classes; applies to computed properties and methods, and can be overridden by subclasses.

    • A subclass overrides it with override class; it can also be redeclared as static in a subclass to seal further overriding.

swift

class Base { static func a() {} // cannot be overridden class func b() {} // can be overridden } class Sub: Base { override class func b() {} // OK // override static func a() {} // error }

Q62.
What are the access control levels in Swift (private, fileprivate, internal, public, open), and how do they differ?

Mid

Swift's five access levels control how widely a declaration is visible, ranging from the most restrictive (private) to the most permissive (open). The key distinction at the top is between public and open, which differ on subclassing/overriding across modules.

  • private: Visible only within the enclosing declaration and its extensions in the same file.

  • fileprivate: Visible anywhere within the same source file.

  • internal: The default: visible throughout the defining module but not outside it.

  • public: Visible to other modules, but classes can't be subclassed and members can't be overridden outside the defining module.

  • open: Like public but also allows subclassing and overriding from other modules; only applies to classes and their members.

Q63.
What is an Actor, and how does it differ from a Class?

Mid

An actor is a reference type, like a class, but it protects its mutable state from data races by guaranteeing that only one task accesses that state at a time. It provides actor isolation: external access to its mutable members must be asynchronous and is serialized automatically.

  • Shared with classes: Reference semantics, can have stored/computed properties, methods, and initializers.

  • What makes it different:

    • State isolation: the compiler serializes access, so concurrent mutation can't cause data races.

    • Cross-actor access is async: you must await to reach its properties/methods from outside.

    • Inside the actor, code accesses its own state synchronously.

    • Actors don't support inheritance (unlike classes).

  • nonisolated members: Mark members that touch no mutable state with nonisolated so they can be called synchronously.

swift

actor Counter { private var value = 0 func increment() { value += 1 } // isolated, synchronous inside } let c = Counter() await c.increment() // await required from outside

Q64.
How does async/await improve upon traditional completion handlers or GCD?

Mid

async/await replaces nested callbacks and manual dispatch with linear, synchronous-looking code that the compiler checks for correctness and the runtime schedules efficiently.

  • Eliminates callback nesting: Sequential await calls read top-to-bottom instead of forming a "pyramid of doom" of nested closures.

  • Compiler-enforced correctness:

    • You must return or throw on every path; with completion handlers it was easy to forget to call the handler on one branch.

    • Errors flow through try/throws instead of a separate Result or error parameter.

  • Efficient thread use: At an await the function suspends and frees the thread, versus GCD where a blocked thread is tied up; the runtime uses a bounded cooperative pool.

  • Safer data handling: Actors and Sendable checking catch data races at compile time, which GCD left entirely to the developer.

Q65.
What is the fundamental difference between Swift Concurrency (async/await) and Grand Central Dispatch (GCD)?

Mid

GCD is a low-level threading/queue API where you manually dispatch closures and reason about concurrency yourself; Swift Concurrency is a higher-level, compiler-integrated model that proves data-race safety and schedules work cooperatively.

  • Abstraction level:

    • GCD: you choose queues and call async/sync on them manually.

    • Swift Concurrency: you write async/await and the runtime manages scheduling.

  • Thread model:

    • GCD can spawn many threads and suffer thread explosion when queues block.

    • Swift Concurrency uses a bounded cooperative pool (roughly one thread per core); suspended tasks free their thread.

  • Safety:

    • GCD provides no compile-time race protection; correctness is on you.

    • Actors and Sendable let the compiler catch data races.

  • Cancellation & structure: GCD has no built-in cancellation propagation; structured concurrency gives automatic cancellation and error flow.

Q66.
What is the difference between a Task and a TaskGroup, and when would you use one over the other?

Mid

A Task represents a single unit of async work; a TaskGroup coordinates a dynamic number of child tasks running in parallel and collects their results within a scope.

  • Task: one async job:

    • Use it to launch a single piece of async work, often to bridge from sync code.

    • For a fixed, small number of parallel sub-tasks, async let is the simpler structured option.

  • TaskGroup: many parallel children:

    • Use when the count is dynamic (e.g. one download per item in an array) and you want them to run concurrently.

    • Structured: the group awaits all children before returning, and cancellation/errors propagate to siblings.

  • Choosing: Single job: Task. Fixed few: async let. Variable many with aggregation: withTaskGroup.

swift

func fetchAll(_ ids: [Int]) async throws -> [Data] { try await withThrowingTaskGroup(of: Data.self) { group in for id in ids { group.addTask { try await fetch(id) } } return try await group.reduce(into: []) { $0.append($1) } } }

Q67.
What is 'Structured Concurrency' and how does it differ from traditional GCD?

Mid

Structured concurrency organizes async tasks into a parent-child hierarchy with well-defined lifetimes: child tasks cannot outlive their parent scope, and cancellation and errors propagate automatically. This contrasts with GCD's unstructured DispatchQueue.async closures, which have no inherent relationship, lifetime, or cancellation linkage.

  • Task tree with scoped lifetimes:

    • Child tasks created in an async let or withTaskGroup must complete before the scope returns.

    • The compiler guarantees no task is silently leaked or orphaned.

  • Automatic cancellation and error propagation: Cancelling a parent cancels all children; a thrown error cancels siblings and surfaces to the parent.

  • GCD is unstructured:

    • Blocks dispatched to queues have no parent, no return value into the caller, and no built-in cancellation.

    • You manually coordinate with DispatchGroup, semaphores, and flags, which is error-prone.

  • Result: clearer code and safety: Concurrency follows the lexical scope, so reasoning about lifetime, errors, and cancellation is local and compiler-checked.

swift

func loadAll() async throws -> (User, [Photo]) { async let user = fetchUser() // child task async let photos = fetchPhotos() // child task return try await (user, photos) // both must finish before return }

Q68.
What is the Sendable protocol, and when does a type automatically conform to it?

Mid

Sendable marks types whose values are safe to pass across concurrency boundaries. Many types conform automatically (implicitly) when the compiler can prove their contents are themselves safe to share, so you often don't write the conformance yourself.

  • Automatic for value types with Sendable members:

    • A struct or enum gets implicit conformance if all stored properties/associated values are Sendable.

    • This applies to non-public types; public ones may need explicit conformance to commit to it as API.

  • Built-in Sendable types:

    • Primitives like Int, String, Bool, and collections of Sendable elements.

    • Actors are implicitly Sendable (their state is isolated).

  • Not automatic:

    • Most classes: a non-final class or one with mutable stored properties won't conform.

    • A final class with only immutable Sendable stored properties can conform explicitly.

    • Use @unchecked Sendable when you guarantee safety manually (e.g. internal locking).

Q69.
What is an 'Indirect Enum' and when would you need to use one?

Mid

An indirect enum is one whose cases may reference the enum type itself (recursion). The indirect keyword tells the compiler to store those cases behind a layer of indirection (a pointer), so the type has a finite, known size despite being recursive.

  • Why it's needed: Enums are value types laid out inline; a directly self-containing case would have infinite size. Indirection boxes the recursive payload on the heap.

  • How to apply it: Mark the whole enum indirect enum, or mark only the recursive cases with indirect case.

  • When you'd use one: Modeling recursive data: trees, linked lists, expression/AST nodes.

swift

indirect enum Expr { case value(Int) case add(Expr, Expr) case multiply(Expr, Expr) }

Q70.
How do Swift enums differ from enums in C or Java? Explain how associated values make them more powerful for state management.

Mid

In C an enum is essentially a named integer, and in Java an enum is a fixed set of singleton objects. Swift enums are full first-class types that can carry per-case data (associated values), conform to protocols, and have methods, making them ideal for modeling state precisely.

  • C enums: Just integer constants; no type safety, no payload, freely interchangeable with Int.

  • Java enums: Object-like with methods and fields, but each constant is a fixed instance; you can't attach different per-use data at the call site.

  • Swift enums add power:

    • Associated values let each case carry distinct data, so a single type captures both the state and the data that state implies.

    • Combined with exhaustive switch, impossible states become unrepresentable and the compiler forces you to handle every case.

swift

enum LoadState { case idle case loading(progress: Double) case loaded([Item]) case failed(Error) } // One value holds the state AND its relevant data, with no invalid combinations.

Q71.
Why is String not indexed by integers in Swift, and how does String.Index relate to Unicode grapheme clusters?

Mid

Swift strings are not integer-indexed because a String is a collection of grapheme clusters (user-perceived characters) whose underlying UTF-8 bytes vary in length per character. There is no constant-time way to jump to the "nth character," so Swift uses an opaque String.Index that encodes a real byte position instead of an integer offset.

  • Variable-width storage: A character may be 1 byte (a) or many (an emoji with modifiers), so str[5] can't be O(1) like an array.

  • Grapheme clusters: One Character can be several Unicode scalars combined (base letter + combining marks); indexing must respect those boundaries.

  • String.Index: An opaque position you derive from the string, e.g. startIndex then advance with index(_:offsetBy:), preventing landing mid-character.

swift

let s = "Café👍" let i = s.index(s.startIndex, offsetBy: 3) print(s[i]) // "é", a valid grapheme boundary, not a raw byte

Q72.
What is the difference between a Character, a Unicode scalar, and a grapheme cluster in Swift?

Mid

They are three layers of the Unicode model: a Unicode scalar is a single code point, a grapheme cluster is one or more scalars that combine into one user-perceived symbol, and in Swift a Character is exactly that grapheme cluster.

  • Unicode scalar: A single code point (Unicode.Scalar), e.g. U+0065 e or U+0301 (combining acute accent). Access via String.unicodeScalars.

  • Grapheme cluster: One or more scalars rendered as a single symbol, e.g. e + combining accent = é, or a flag emoji built from two regional-indicator scalars.

  • Character: Swift's Character type IS a grapheme cluster, so iterating a String gives human-meaningful characters, not raw code points.

  • Why it matters: "é" can be count == 1 as characters but two unicodeScalars: choose the right view for the task.

Q73.
How does pattern matching work in a switch statement, and what does the where clause add?

Mid

A switch matches a value against patterns top to bottom and runs the first case that fits; patterns can match literals, ranges, tuples, enum cases, and bind associated values to names. The where clause adds a boolean condition a case must also satisfy to match.

  • Kinds of patterns:

    • Value/range: case 0, case 1...9.

    • Tuple and wildcard: case (let x, 0), with _ ignoring a component.

    • Enum with binding: case .success(let data) extracts associated values.

  • Exhaustiveness: Swift requires every possible value be covered, often via a default case; this catches missed enum cases at compile time.

  • The where clause: Refines a pattern with a runtime predicate, often using values bound earlier in the same pattern.

swift

switch point { case let (x, y) where x == y: print("on the diagonal") case let (x, y) where x > 0 && y > 0: print("first quadrant") default: print("somewhere else") }

Q74.
Why does putting a class inside a struct potentially break value semantics, and how do you fix it?

Senior

A struct only has value semantics if all its properties do. Storing a class reference inside means copies of the struct still share that same object, so mutating it through one copy is visible through the others. The fix is to ensure the referenced data is immutable or to copy it on write.

  • The problem: Copying the struct copies the reference (the pointer), not the object it points to, so both copies alias one class instance.

  • Fixes:

    • Make the referenced class immutable (only let properties) so sharing is harmless.

    • Replace the class with a struct if the data doesn't need reference identity.

    • Implement copy-on-write: check isKnownUniquelyReferenced(&storage) in a mutating setter and deep-copy before mutating.

swift

struct Wrapper { private var storage: Box // Box is a class var value: Int { get { storage.value } set { if !isKnownUniquelyReferenced(&storage) { storage = Box(storage.value) // copy before mutating } storage.value = newValue } } }

Q75.
What are the performance trade-offs between stack allocation and heap allocation in Swift?

Senior

Stack allocation is fast and automatic (just moving a pointer), while heap allocation is slower because it requires dynamic memory management and reference counting; Swift puts value types on the stack and reference types on the heap.

  • Stack allocation:

    • Used for value types (struct, enum, tuples) with known size and lifetime.

    • Allocation/deallocation is essentially free: increment or decrement the stack pointer, freed automatically when scope exits.

    • No reference counting overhead.

  • Heap allocation:

    • Used for reference types (class, closures) and dynamically sized data.

    • Slower: requires finding/managing free memory and thread-safe bookkeeping.

    • Adds ARC retain/release traffic, which can be a hidden cost in hot loops.

  • Caveats:

    • A struct that captures a reference type still incurs heap costs for that member.

    • Large structs may be copied frequently; copy-on-write types (Array, String) use heap storage but defer copies.

Q76.
What is the difference between the some keyword and the any keyword? When should you use an opaque type versus an existential type?

Senior

Both expose a value by the protocol it conforms to, but some is an opaque type (one fixed concrete type known to the compiler, hidden from the caller), while any is an existential (a boxed value whose concrete type can vary at runtime).

  • some : opaque type:

    • The compiler knows the single underlying type and preserves type identity, so it's statically dispatched and zero-cost.

    • Lets you use protocols with associated types (PATs) as a return type without exposing the concrete type.

  • any : existential type:

    • Wraps the value in a box; the concrete type can differ between values and is resolved at runtime (dynamic dispatch).

    • Required when you genuinely need heterogeneity, e.g. an array of differing conforming types.

  • When to use which:

    • Prefer some for a single consistent type (returning a value, a SwiftUI some View): cheaper and keeps type info.

    • Use any only when the type must vary or be stored heterogeneously, accepting the boxing overhead.

swift

func makeShape() -> some Shape { Circle() } // one fixed hidden type let shapes: [any Shape] = [Circle(), Square()] // mixed types, boxed

Q77.
What is an associatedtype in a protocol, and how does it differ from a standard generic type parameter?

Senior

An associatedtype is a placeholder type declared inside a protocol that each conforming type fills in, letting the protocol be generic over a type the adopter chooses; it differs from a generic parameter in who picks the type and where it lives.

  • associatedtype : the conformer decides:

    • Declared with associatedtype Element; the adopting type binds it (often implicitly via type inference or a typealias).

    • It makes the protocol a PAT, which can't be used as a plain type without any or some.

  • Generic parameter : the caller decides: A <T> on a function or type is chosen at each use site by the caller.

  • Rule of thumb: Use an associated type when the type is an intrinsic part of the conformer's identity (e.g. Collection.Element); use a generic parameter when the caller should choose.

swift

protocol Container { associatedtype Item mutating func append(_ item: Item) var count: Int { get } } struct IntBox: Container { // Item inferred as Int private var items: [Int] = [] mutating func append(_ item: Int) { items.append(item) } var count: Int { items.count } }

Q78.
What is 'Type Erasure' in Swift, and why might you need it when working with protocols?

Senior

Type erasure is wrapping a value behind a concrete type that hides (erases) its underlying generic/associated-type details, so you can store or pass protocols with associated types where the concrete type can't otherwise be named.

  • Why you need it:

    • A PAT can't be used as a variable type or array element directly, so you can't write [SomePAT] in older Swift.

    • A type eraser exposes only the protocol's interface while forwarding calls to the hidden value.

  • How it's built:

    • Create a wrapper (by convention AnyXxx) that captures the operations as closures or holds the value via an internal box.

    • The standard library does this with AnySequence, AnyHashable, AnyView.

  • Modern alternative: In recent Swift, any Protocol existentials often remove the need for hand-written erasers.

swift

struct AnyContainer<Item>: Container { private let _append: (Item) -> Void private let _count: () -> Int init<C: Container>(_ c: C) where C.Item == Item { var c = c _append = { c.append($0) } _count = { c.count } } mutating func append(_ item: Item) { _append(item) } var count: Int { _count() } }

Q79.
What is a Protocol with Associated Types (PAT), and what was the 'Self or associated type' limitation?

Senior

A PAT is a protocol that has one or more associatedtype requirements (or uses Self), like Equatable or Collection. The classic limitation was that such a protocol could not be used as a standalone existential type.

  • The 'Self or associated type' error:

    • Older Swift gave: "protocol can only be used as a generic constraint because it has Self or associated type requirements."

    • Reason: the compiler couldn't make a uniform box, since the associated type / Self differs per conformer (e.g. == needs two values of the same concrete type).

  • Workarounds: Use it as a generic constraint (func f<T: Collection>(_ x: T)) or build a type eraser.

  • Modern Swift: You can now write any Collection (existentials with associated types), and some / primary associated types like any Collection<Int> ease the old restrictions, though equality-style requirements still need generics.

Q80.
What are the Sequence and IteratorProtocol protocols, and how would you make a custom type iterable?

Senior

Sequence represents a series of values you can iterate over with for-in, and IteratorProtocol is the stateful machine that actually produces those values one at a time. To make a type iterable you conform to Sequence and supply an iterator via makeIterator().

  • IteratorProtocol:

    • Has one requirement: mutating func next() -> Element?, returning nil when exhausted.

    • It holds the iteration state (current position).

  • Sequence:

    • Requires makeIterator() -> some IteratorProtocol; for-in calls it then loops on next().

    • Conforming gives you free methods like map, filter, reduce, and contains.

  • Shortcut: A type can conform to both protocols itself if it is its own iterator, but separating them lets you iterate the same sequence multiple times independently.

swift

struct Countdown: Sequence { let start: Int func makeIterator() -> AnyIterator<Int> { var current = start return AnyIterator { guard current > 0 else { return nil } defer { current -= 1 } return current } } } for n in Countdown(start: 3) { print(n) } // 3, 2, 1

Q81.
Explain the difference between Static Dispatch, Table Dispatch (Witness Tables), and Message Dispatch. Which one is the fastest and why?

Senior

These are the three ways Swift decides which implementation of a method to call. Static dispatch resolves the call at compile time, table dispatch looks it up in a per-type table at runtime, and message dispatch resolves it dynamically through the Objective-C runtime. Static dispatch is the fastest because the address is known at compile time and can be inlined.

  • Static dispatch:

    • The exact function address is resolved at compile time (used for final, static, value types, and most extensions).

    • Fastest: no lookup, and the optimizer can inline the body.

  • Table dispatch (witness/v-tables):

    • Each class has a v-table and each protocol conformance a witness table; the call indexes into the table at runtime.

    • One pointer indirection: slower than static, but enables overriding and protocol polymorphism.

  • Message dispatch:

    • Goes through the Objective-C runtime (objc_msgSend); used for @objc dynamic members.

    • Slowest, but fully dynamic: enables swizzling and KVO.

Q82.
What is a 'Witness Table' (or Protocol Witness Table), and how does it facilitate polymorphism?

Senior

A Protocol Witness Table (PWT) is a runtime lookup table that maps a protocol's requirements to a specific type's concrete implementations. When you call a method through a protocol type, Swift uses the witness table to find and call the right implementation, which is how protocol-based polymorphism works.

  • What it contains:

    • One entry per protocol requirement, pointing to the conforming type's implementation of that requirement.

    • Generated once per type-conforms-to-protocol pair.

  • How it enables polymorphism:

    • A value held as a protocol type (any Drawable) carries a pointer to its witness table.

    • Calling a protocol method indexes into that table at runtime, so the correct concrete method runs regardless of static type.

  • Existential containers: An any value is boxed with its data plus the witness table (and value-witness table for memory management).

Q83.
What is a Witness Table and how does it differ from a V-Table?

Senior

Both are runtime dispatch tables of function pointers, but a V-Table belongs to a class and drives inheritance-based overriding, while a Witness Table belongs to a type's conformance to a protocol and drives protocol-based polymorphism. The key difference is what they describe: class hierarchy versus protocol conformance.

  • V-Table (virtual method table):

    • Attached to a class; one per class.

    • Lets a subclass override methods: the call indexes into the instance's class v-table.

    • Only relevant to reference types with inheritance.

  • Witness Table:

    • Attached to a (type, protocol) conformance; one per conformance.

    • Works for structs, enums, and classes alike, since protocols are independent of inheritance.

    • Passed alongside values in generics and any existentials.

  • Shared trait: Both add one indirection (a pointer lookup), so both are slower than static dispatch but enable polymorphism.

Q84.
When should you prefer some over any for performance reasons?

Senior

Prefer some (an opaque type) whenever a single concrete type is used at a given call site, because it lets the compiler know the exact type and use static dispatch, avoiding the boxing and witness-table indirection that any (an existential) requires.

  • some is one fixed underlying type:

    • The type is hidden from the caller but known to the compiler, so calls can be specialized, inlined, and statically dispatched.

    • No heap boxing; the value is stored directly.

  • any is a dynamic box:

    • Uses an existential container that carries the value plus witness tables; method calls go through table dispatch.

    • May allocate on the heap for larger values, adding overhead.

  • When you still need any: Heterogeneous storage: a single [any Shape] holding mixed concrete types, which some cannot express.

  • Rule of thumb: Reach for some by default; use any only when you genuinely need type erasure at runtime.

Q85.
Why does an extension on a class use static dispatch by default, while a method in the main declaration uses table dispatch?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q86.
Explain 'Copy-on-Write' (CoW). Which standard library types use it, and how would you implement it for a custom type?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q87.
How does Automatic Reference Counting (ARC) work under the hood?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q88.
Explain how 'Copy-on-Write' (COW) works in the Swift Standard Library.

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q89.
Explain the concept of 'Side Tables' in the Swift runtime's implementation of ARC.

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q90.
Explain 'Two-Phase Initialization' in Swift and why it is required.

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q91.
What is a 'Property Wrapper,' and how does it work under the hood?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q92.
What is a lazy sequence in Swift, and what are the benefits and pitfalls of using one?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q93.
What are noncopyable structs or enums (~Copyable), and what problem do they solve regarding resource ownership?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q94.
Explain the difference between a generic constraint and an opaque return type: who decides the type, the caller or the implementation?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q95.
Explain the difference between 'Static' and 'Dynamic' linking in the context of the Swift standard library.

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q96.
Conceptually, how do Swift Macros work during the compilation process, and how do they differ from C-style macros?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q97.
What is a generic where clause, and how does it differ from a simple type constraint?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q98.
What is the @MainActor attribute, and how does it change how code is dispatched compared to manually calling DispatchQueue.main.async?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q99.
What is 'reentrancy' in the context of Swift Actors, and what problems can it cause?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q100.
Explain the difference between Task and Task.detached. When would you prefer one over the other in terms of priority and inheritance?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q101.
Explain the difference between 'Structured Concurrency' (TaskGroups/async-let) and 'Unstructured Concurrency' (Task).

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q102.
How does async/await handle suspension points, and what happens to the thread during an await?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q103.
Why can't you always just use @MainActor for everything?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q104.
What is the continuation pattern (withCheckedContinuation) and when is it necessary?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q105.
What is an actor in Swift, and how does it conceptually guarantee thread safety for its mutable state?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q106.
Explain the concept of 'Actor Isolation' and how 'nonisolated' keywords are used.

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q107.
Explain 'Region-based Isolation' and how it allows non-Sendable types to be passed across isolation boundaries.

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q108.
When would you use nonisolated or nonisolated(unsafe)?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q109.
What is the Sendable protocol, and why is it critical for Swift 6's strict concurrency checking?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q110.
Explain the concept of 'Strict Concurrency' in Swift 6 and why it matters.

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q111.
What is a 'Data Race,' and how does the Swift compiler detect them at compile-time?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q112.
What are the biggest conceptual hurdles when migrating a codebase from Swift 5 to Swift 6's strict concurrency mode?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.