Tech

Swift

Swift is Apple's modern, type-safe language for iOS, macOS, watchOS, and tvOS development. Known for its clean syntax, optionals for null safety, and protocol-oriented design, it has rapidly replaced Objective-C as the primary language for Apple platforms. Interview questions cover optionals, value vs. reference types, memory management via ARC, generics, error handling, and the paradigms that make Swift both powerful and approachable for developers across experience levels.

What you get

Questions

20

Difficulty

3 levels

Answer Formats

2

Use the toggle on each card to move between an interview-ready answer and a simpler explanation. Questions are sorted from beginner to advanced, and the keywords are highlighted. You can also blur the answers to practice recalling them from memory.

Questions

Practice the answers out loud.

All topics

Question 1

What are the key features of Swift and how does it differ from Objective-C?

Beginner

How to answer in an interview

Swift is Apple's modern, type-safe language designed for iOS, macOS, watchOS, and tvOS development. Key features include optionals for null safety, closures with simplified syntax, protocol-oriented programming, and memory management via ARC. Compared to Objective-C, Swift offers cleaner syntax, stronger type inference, no pointer arithmetic by default, and better performance through LLVM optimization. Swift also provides playgrounds for rapid prototyping and is fully interoperable with existing Objective-C codebases.

Question 2

What is the difference between `let` and `var` in Swift?

Beginner

How to answer in an interview

`let` declares a constant — once assigned, its value cannot be changed. `var` declares a variable — its value can be reassigned. Swift encourages using `let` by default for immutability, which improves safety, clarity, and enables compiler optimizations. Constants also make intent explicit: code reading a `let` knows the value won't change. Note that for reference types assigned with `let`, the reference is constant but the object's properties may still be mutable.

Question 3

What are optionals in Swift and why are they important?

Intermediate

How to answer in an interview

Optionals are Swift's mechanism for handling the absence of a value. An optional type (e.g., `String?`) can hold either a value or `nil`. This eliminates null pointer exceptions at compile time. You safely unwrap optionals using optional binding (`if let`, `guard let`), optional chaining, or the nil-coalescing operator (`??`). Force unwrapping (`!`) is discouraged because it crashes if the value is nil. Optionals enforce explicit handling of missing values, making code more robust and self-documenting.

Question 4

Explain the difference between structs and classes in Swift.

Intermediate

How to answer in an interview

Structs are value types — when assigned or passed, they are copied, so changes to one copy don't affect the original. Classes are reference types — multiple variables can refer to the same instance, and changes through one reference are visible to all. Swift encourages using structs by default for simplicity and thread safety. Use classes when you need inheritance, identity (reference semantics), or deinitializers. Both support properties, methods, initializers, and extensions. Structs automatically get a memberwise initializer, while classes require explicit initializer definitions.

Question 5

What are closures in Swift and how do they differ from functions?

Intermediate

How to answer in an interview

Closures are self-contained blocks of code that can be passed around. Unlike functions, closures can capture and store references to variables from their enclosing scope (capturing values). Swift provides shorthand syntax for closures: implicit returns, shortened parameter names, and trailing closure syntax. Closures are classified as non-escaping (called within the function's scope, default) or escaping (stored or called after the function returns, requiring `@escaping`). Closures are fundamental to APIs like `map`, `filter`, `sort`, and asynchronous handlers.

Question 6

Explain the concept of extensions in Swift.

Intermediate

How to answer in an interview

Extensions add new functionality to existing types — including types you don't own — without subclassing. You can add computed properties, methods, initializers, subscripts, and even make existing types conform to new protocols. Extensions cannot add stored properties or designated initializers. They are widely used in Swift to organize code (e.g., separating protocol conformance into dedicated extensions) and to extend standard library or framework types with project-specific utilities.

Question 7

How does error handling work in Swift?

Intermediate

How to answer in an interview

Swift models errors as values conforming to the `Error` protocol (typically enums). Functions that can throw errors are marked with `throws`, and callers use `do-try-catch` to handle them. The `try` keyword attempts the call, and `catch` blocks handle specific error cases. For asynchronous contexts, the `Result<Success, Failure>` type or `async throws` (Swift 5.5+) provide structured error handling. Swift's approach is explicit — unhandled errors won't compile — encouraging robust error management without exceptions.

Question 8

What is the difference between `map`, `compactMap`, and `flatMap` in Swift?

Intermediate

How to answer in an interview

`map` transforms each element in a sequence using a closure and returns an array of the same size. `compactMap` is like `map` but automatically unwraps and removes `nil` results, producing a smaller array. `flatMap` has two uses: flattening nested sequences (e.g., `Int` into `[Int]`) and, in older Swift, performing optional chaining across multiple levels (now largely replaced by `compactMap`). Choose `map` when every input produces a valid output, `compactMap` when some outputs might be nil, and `flatMap` when working with nested collections.

Question 9

What is the difference between `weak` and `unowned` references?

Intermediate

How to answer in an interview

Both `weak` and `unowned` prevent strong reference cycles, but they differ in semantics. `weak` references are optional — they become `nil` when the referenced instance is deallocated, so you must safely unwrap. `unowned` references are non-optional — they must not outlive the referenced instance, and accessing them after deallocation causes a crash. Use `weak` when the referenced object may be deallocated first (e.g., delegate patterns). Use `unowned` when you are certain the reference will not outlive the object (e.g., a closure that definitely completes before the object is freed).

Question 10

What is protocol-oriented programming in Swift?

Advanced

How to answer in an interview

Protocol-oriented programming (POP) is Swift's paradigm that favors composing behavior through protocols and their extensions over class inheritance. Protocols define contracts, and protocol extensions can provide default implementations. This enables code reuse without the fragility of class hierarchies. POP works with both structs and classes, supports protocol composition (`ProtocolA & ProtocolB`), and reduces coupling. Swift's standard library is heavily protocol-oriented (e.g., `Equatable`, `Codable`, `Hashable`). POP leads to more modular, testable, and flexible codebases compared to traditional OOP approaches.

Question 11

How does memory management work in Swift?

Advanced

How to answer in an interview

Swift uses Automatic Reference Counting (ARC) to manage memory. Each instance has a reference count; when the count drops to zero, the instance is deallocated. Strong references increase the count. To prevent retain cycles (where two objects hold strong references to each other, preventing deallocation), use `weak` or `unowned` references. `weak` references are optional and become nil when the instance is deallocated; `unowned` references are non-optional and must not outlive the instance. Instruments and the Memory Graph Debugger help identify memory leaks.

Question 12

What are generics in Swift and when should you use them?

Advanced

How to answer in an interview

Generics allow you to write flexible, reusable code that works with any type while preserving type safety. You define a generic function, struct, class, or protocol with a type parameter (e.g., `<T>`). Constraints (e.g., `T: Equatable`) restrict which types can be used. Associated types in protocols provide generic-like behavior within protocols. Generics eliminate code duplication for algorithms and data structures, and are used extensively in Swift's standard library (e.g., `Array<T>`, `Dictionary<Key, Value>`).

Question 13

What are property wrappers in Swift?

Advanced

How to answer in an interview

Property wrappers encapsulate reusable storage logic for properties. A property wrapper is a struct or class annotated with `@propertyWrapper` that implements `wrappedValue` (the value accessed) and optionally `projectedValue` (the `$` prefix value, often used for bindings in SwiftUI). Common built-in examples include `@State`, `@Binding`, `@ObservedObject`, and `@Environment`. Custom property wrappers can enforce validation, logging, lazy initialization, or persistence. They reduce boilerplate and centralize access control logic.

Question 14

Explain Swift's approach to concurrency with async/await.

Advanced

How to answer in an interview

Swift 5.5 introduced native concurrency with `async/await` — functions marked `async` can suspend without blocking threads, and `await` marks suspension points. Structured concurrency groups async work into `Task` hierarchies, ensuring proper cancellation and error propagation. Actors provide thread-safe isolation for shared mutable state, eliminating data races without locks. `AsyncSequence` and `AsyncStream` handle asynchronous streams of values. This model replaces callback-based patterns and Combine for many use cases, producing more readable and maintainable asynchronous code.

Question 15

What are result builders in Swift?

Advanced

How to answer in an interview

Result builders (formerly function builders) enable declarative, DSL-like syntax by transforming a sequence of expressions into a single return value. The `@resultBuilder` attribute marks a type that provides `buildBlock`, `buildOptional`, `buildEither`, and other methods. SwiftUI uses `@ViewBuilder` extensively — allowing multiple child views to be written as a flat list that the builder assembles into a `TupleView`. Result builders power not only UI code but also SQL query builders, HTML generators, and configuration DSLs.

Question 16

How does type erasure work in Swift?

Advanced

How to answer in an interview

Type erasure wraps a concrete type behind a protocol-conforming wrapper so it can be stored in collections or passed where only the protocol type is known. Swift provides built-in type erasers: `AnySequence`, `AnyPublisher`, and `AnyView`. Without type erasure, you cannot store protocol existentials in arrays when the protocol has associated types. Custom type erasure involves creating a private struct that holds the concrete instance and forwards protocol method calls. It trades compile-time type information for runtime flexibility.

Question 17

What are the copy-on-write semantics in Swift?

Advanced

How to answer in an interview

Swift's standard library collections (Array, Dictionary, Set, String) use copy-on-write (COW) internally. When a collection is copied, no actual duplication occurs — both references share the same storage. A deep copy only happens when one of the copies is mutated. This gives value-type semantics (independent copies) with reference-type performance (shared storage until mutation). You can implement COW in custom types by checking `isKnownUniquelyReferenced` before mutating shared buffer storage.

Question 18

What are macros in Swift?

Advanced

How to answer in an interview

Swift 5.9 introduced macros — compile-time code generation that transforms syntax using explicit macro definitions. Macros are declared with `macro` keyword and expanded by macro implementations (written in Swift using the SwiftSyntax library). They come in three roles: `expression` (replace with an expression), `declaration` (add declarations), and `accessor` (add property accessors). Examples include `#stringify` (returns an expression and its string representation) and `@Observable` (auto-generates observation plumbing). Macros replace some use cases of code generation tools and reduce boilerplate while remaining type-safe.

Question 19

How do you handle threading and synchronization in Swift?

Advanced

How to answer in an interview

Swift offers multiple concurrency mechanisms: Grand Central Dispatch (GCD) with `DispatchQueue` for queue-based threading, `Operation` and `OperationQueue` for complex task dependencies, and the modern actor model (Swift 5.5+) for thread-safe isolation. `@MainActor` guarantees execution on the main thread for UI updates. Actors use mutual exclusion — only one task can access an actor's state at a time — eliminating data races without manual locking. Combine `async/await` with actors for clean, race-free concurrent code. Traditional locks (`NSLock`) are still available but discouraged in favor of actors.

Question 20

What is the difference between opaque types and existentials in Swift?

Advanced

How to answer in an interview

Opaque types (`some Protocol`) hide the concrete type from the caller while preserving it at compile time — the compiler knows the exact type and can optimize accordingly. Existentials (`any Protocol`) erase the type entirely, storing a box with dynamic dispatch — more flexible but with performance overhead and restrictions (e.g., cannot use associated types without `any`). Opaque types are preferred when the return type is fixed but you want to hide implementation details (common in SwiftUI). Existentials are used when you genuinely need to store different conforming types in the same variable.

More in Tech

Keep browsing related topics.

Browse all