iosNudge

iOS knowledge interviews

Chapter 6 of 10 7 min read

In this round the interviewer asks about Swift and the iOS platform. It sounds easy: "just questions". But the follow-up questions go deep fast. Memorised definitions are not enough.

How the round works

There are two common styles.

1. Rapid questions. Many short questions on different topics.

"What is the difference between a struct and a class?" "What does @escaping mean?" "What is @MainActor?"

2. Deep dive. One topic, deeper and deeper.

"What is ARC?" → "What is a retain cycle?" → "Show me one in code." → "How do you find one in a real app?"

Many interviewers also ask about your resume: "You wrote that you used Core Data. Why Core Data and not SwiftData?"

The answer formula: definition → example → trade-off

Use three parts for every answer. It is short, clear, and shows real experience.

  1. Definition: one or two sentences. What is it?
  2. Example: a small piece of code or a real case from your work.
  3. Trade-off: when you would use it, when not, or what it costs.

Aim for 1–2 minutes. Then stop and let the interviewer ask more.

Example 1: "What is the difference between a struct and a class?"

Definition:

"A struct is a value type. When you assign it, you get a copy. A class is a reference type. When you assign it, both variables point to the same object."

Example:

struct PointS { var x = 0 }
final class PointC { var x = 0 }

var a = PointS()
var b = a
b.x = 5
print(a.x)  // 0, a is a separate copy

let c = PointC()
let d = c
d.x = 5
print(c.x)  // 5, same object

Trade-off:

"I use structs by default for models, because copies make code easier to reason about and are safer with concurrency. I use a class when I need shared identity, like a view model or a cache, or when I need inheritance or deinit."

Example 2: "When do you use weak vs unowned?"

Definition:

"Both break retain cycles. weak becomes nil when the object is freed, so it is always optional. unowned is not optional and assumes the object is still alive."

Example:

final class PlayerViewController: UIViewController {
    private let player = VideoPlayer()

    override func viewDidLoad() {
        super.viewDidLoad()
        player.onFinish = { [weak self] in
            self?.showReplayButton()
        }
    }

    private func showReplayButton() { /* ... */ }
}

Trade-off:

"unowned crashes if the object is gone. So I use weak in almost all closures. I use unowned only when the other object always lives longer, for example a child that cannot exist without its parent."

Example 3: "What is an actor?"

Definition:

"An actor is a reference type that protects its mutable state. Only one task can run code inside the actor at a time, so there are no data races."

Example:

actor ImageCache {
    private var images: [URL: Data] = [:]

    func image(for url: URL) -> Data? { images[url] }
    func store(_ data: Data, for url: URL) { images[url] = data }
}

// From outside, calls need await
let data = await cache.image(for: url)

Trade-off:

"Actors make thread safety simple. The cost is that every call from outside is async. Also, actors are reentrant: when the actor awaits something, other calls can run in between, so state can change across an await. I check state again after each await."

The reentrancy point is a strong signal. Many candidates miss it.

What makes an answer weak

WeakWhyBetter
"GCD is for threads."Too short, no detailSay what queues are, show DispatchQueue.main.async, compare with async/await.
A 5-minute speechThe interviewer cannot steerStop after 1–2 minutes.
Only theoryNo proof of experienceAdd "In my last project, I…".
Guessing with confidenceWrong answers hurt trustSay "I am not sure, but I think…" and reason.
"Always use X"No nuanceSay when you would not use X.

How to handle "I don't know"

You will not know everything. Nobody does. Use this pattern:

  1. Be honest.
  2. Say what you do know that is close.
  3. Reason out loud.

"I have not used NSCache in production. I know it is a key-value cache that can remove items when memory is low. So I would use it for images. I would check whether it copies the keys, like NSDictionary does."

This is much better than a wrong guess or silence.

Show experience, not just knowledge

Interviewers want to know if you have done the work. Small real details help a lot.

  • "I found this retain cycle with the Memory Graph Debugger in Xcode."
  • "We saw this crash in our crash reports only on older devices."
  • "Thread Sanitizer showed a data race in our analytics code."
  • "Instruments showed 40% of launch time was in one SDK setup call."

If you have no production experience with a topic, say what you built to learn it.

Top topics checklist

Use this list to find gaps. For each item, can you give a definition, an example and a trade-off?

Swift language

  • Value vs reference types, copy-on-write
  • Optionals, if let, guard let, optional chaining
  • Protocols, protocol extensions, default implementations
  • Generics, associated types, some vs any
  • Closures, @escaping, capture lists
  • Enums with associated values
  • Error handling: throws, Result, typed throws
  • Access control: private, fileprivate, internal, public, open
  • Property wrappers and macros (what they do, not how to write them)

Memory

  • ARC and reference counting
  • Retain cycles in closures, delegates, timers
  • weak vs unowned
  • Finding leaks: Memory Graph Debugger, Instruments

Concurrency

  • GCD: main queue, global queues, serial vs concurrent
  • async/await, Task, task cancellation
  • Structured concurrency: async let, task groups
  • Actors, actor reentrancy, @MainActor
  • Sendable and Swift 6 strict concurrency checking
  • Data races and how to find them (Thread Sanitizer)

UIKit

  • View controller lifecycle (viewDidLoad, viewWillAppear, and so on)
  • Auto Layout, intrinsic content size, content hugging and compression resistance
  • Table and collection views, cell reuse, diffable data sources
  • Delegation and the responder chain

SwiftUI

  • @State, @Binding, @Environment, @Observable
  • View identity and why it matters (id, ForEach)
  • When the body is re-evaluated
  • .task vs .onAppear
  • Mixing SwiftUI and UIKit (UIHostingController, UIViewRepresentable)

Architecture

  • MVC, MVVM, and one more (for example TCA, VIPER or Clean)
  • Dependency injection with protocols
  • Navigation: coordinators, NavigationStack
  • Modularisation with Swift packages

Networking and data

  • URLSession, Codable, error handling
  • Caching (HTTP cache, in-memory, disk)
  • Persistence options: UserDefaults, Keychain, files, Core Data, SwiftData
  • Pagination and offline support

App and platform

  • App lifecycle and scene lifecycle
  • Background tasks, push notifications
  • Security: Keychain, App Transport Security, certificate pinning
  • Accessibility: VoiceOver, Dynamic Type
  • Performance: launch time, scroll performance, main thread work

Testing and tooling

  • XCTest and Swift Testing (@Test, #expect)
  • Mocks and fakes with protocols
  • UI tests, snapshot tests
  • Swift Package Manager, build configurations, CI

Common follow-up questions to expect

Prepare for these. They test depth.

  • "Why is @MainActor needed if you already use DispatchQueue.main?"
  • "What happens if you capture self strongly in a Task?"
  • "How does SwiftUI know when to redraw a view?"
  • "What is the difference between some View and any View?"
  • "How would you store a user's auth token? Why not UserDefaults?"
  • "Why are cells reused, and what bug can that cause?"

Example short answer for the last one:

"Cells are reused to save memory and keep scrolling smooth. The common bug is old content showing in a reused cell, for example the wrong image after a slow download. I reset the cell in prepareForReuse() and check that the result still matches the cell's item before I set the image."

override func prepareForReuse() {
    super.prepareForReuse()
    imageTask?.cancel()
    imageView.image = nil
}

How to practise

  • Use the quiz questions on this site. Read the title, then answer out loud before you open the answer.
  • Time yourself: 1–2 minutes per answer.
  • After each answer, check: did I give a definition, an example and a trade-off?
  • Start with "high" importance questions. They come up most often.
  • Write every question you could not answer in your mistakes list. Return to it in a week.