For interviewers

You’re on a video call. Mark criteria in one tap.

Mark criteria while the candidate talks. Scores roll up by topic. Leave the call with a hire-ready debrief.

How it runs

From script to feedback in one place.

  1. 01

    Build a script with clear criteria

    Each question has a format, a prompt, and marking criteria. Topics like Concurrency and UIKit roll up for the debrief — you grade areas, not only answers.

  2. 02

    Mark during the call

    One tap per criterion: hit, partial, or missed. The score updates itself; notes stay one keystroke away — built for split attention on a video call.

  3. 03

    Walk out with a debrief

    Topic breakdown, an editable hire recommendation, and your notes per question — ready material for written feedback.

Question formats

Code review, mocks, predictions, live coding — same three taps every time. Only the prompt and criteria change.

  • Candidate reads the code

    Code review

    A flawed snippet is on screen. You mark what they spotted, what needed a hint, and what they missed — line references stay on the rubric.

    swift · review
    timer = Timer.scheduledTimer
    self.reloadProfile() // retain?
    DispatchQueue.global().async {
    SpottedHintedMissed
  • Designer mock on the table

    Screen mock

    Walk a layout: compositional vs SwiftUI, scrolling, image loading. Same three taps — the criteria are product decisions, not code lines.

    designer mock

    HitPartialMissed
  • What does this print?

    Output prediction

    GCD, ARC, layout math — they reason out loud before you run it. You mark each prediction as you hear it, including deadlocks they catch or miss.

    swift · prediction
    print("A")
    DispatchQueue.global().async {
        print("B")
        DispatchQueue.main.sync { /* … */ }
    }
    A then I before the async blockHit
    Deadlock on main.sync inside asyncHit

Mark in real time

Three taps per criterion, as you would on a video call. Open the full demo when you want the complete script and debrief.

Question 1 of 8 · Criteria · ~8 min · Memory & ARC

Strong vs weak vs unowned references

What is the difference between strong, weak and unowned references? Use the snippet: why is customer declared unowned and not weak?
swift
final class Customer {
    var card: CreditCard?
}

final class CreditCard {
    unowned let customer: Customer   // why unowned, not weak?
    init(customer: Customer) {
        self.customer = customer
    }
}

Criteria — tap what you hear

Tap a row to cycle: hit → partial → missed → clear

0/5 marked
Rating

After the call

Scores come from the marks you already tapped. Change the hire recommendation if your judgment disagrees — then copy your notes into the write-up.

Alexei M. — iOS Middle+

4 of 8 asked · 38:12 elapsed · 4 skipped

Recommendation

By topic

Weakest first

Swift Language

0%

Weak · 1 criterion

hit
0 hit
partial
0 partial
missed
1 missed

Error Handling

0%

Weak · 1 criterion

hit
0 hit
partial
0 partial
missed
1 missed

UIKit

50%

Soft · 1 criterion

hit
0 hit
partial
1 partial
missed
0 missed

Concurrency

75%

Solid · 6 criteria

hit
4 hit
partial
1 partial
missed
1 missed

Memory & ARC

92%

Strong · 6 criteria

hit
5 hit
partial
1 partial
missed
0 missed

Not covered

  • SwiftUI
  • Architecture
  • Ownership & Comms

Questions

4 asked · 4 skipped

01ExcellentCriteria · Memory & ARC

Strong vs weak vs unowned references

90%
  • ARC keeps three counts — strong, unowned, weak — not a single refcount
  • Strong means ownership: strong count → 0 runs deinit
  • Weak goes through a side table and reads nil after deinit
  • Unowned is a direct non-owning pointer — traps if used after deinit
  • Usage rule: weak when the target may die first, unowned only with a lifetime guarantee
NoteConfident. Drew the side table from memory, teardown order correct.
02GoodOutput prediction · Concurrency

What does this print — and where does it hang?

70%
  • Starts with A then I — sync path on the caller before the global block runs
  • B and D print on the background thread before waiting on main.sync
  • Explains C runs before E — C was already queued when sync blocked on main
  • H prints before F — F is only scheduled before H runs on the global queue
  • Spots the deadlock: main.sync { G } inside main.async { F }
NoteNailed A→I and the G deadlock. Soft on why C drains before E on the serial main queue.
03FairCode review

Code review: what problems do you see?

50%
  • Timer closure captures self strongly → retain cycle, VC never deinits · Memory & ARC
  • Timer is never invalidated (deinit / viewDidDisappear) · UIKit
  • UI (nameLabel) is updated from a background queue · Concurrency
  • try! crashes the app on any load failure · Error Handling
  • Implicitly unwrapped viewModel — fragile initialization contract · Swift Language
NoteCaught the retain cycle and background UI update. Hasn’t mentioned try! or the IUO viewModel yet.
08NotesDesign discussion · Ownership & Comms

Xcode debugging tools

NoteNamed LLDB, View Hierarchy, and Instruments — light on when to pick each.

Not asked

  1. 04 Build this screen — layout walkthrough
  2. 05 Live coding: implement a debouncer
  3. 06 Design an image caching layer for a feed
  4. 07 A production incident you owned

Run a practice interview before your next hire.

Sign in to create scripts — or open the demo to try marking now.