% live-coding-strategy.tex — live-coding round + take-home game plan, one page.
% Sources: docs/memos/interview-ios-coding-strategy.md,
%          docs/school/mock-interviews/mock-algorithms-coding.md (lessons),
%          docs/school/mock-interviews/mock-system-design-feed.md (pagination, image pipeline)
% Build: tools/print/print-sheet.py docs/school/sheets/interview/live-coding-strategy.tex --dry-run
% @source: hiot monorepo, docs/school/sheets/interview/live-coding-strategy.tex — the SOURCE OF TRUTH; a copy anywhere else (e.g. artur.gurgul.pro) is regenerated from it, never edited
% @labels: area=interview kind=interview level=core platform=ios new=no round=round3-2026-09-24 topic=career,testing
% @tags: live-coding, take-home, think-aloud, brute-force, edge-cases, time-space-complexity, weak-self, force-unwrap, codable-edge-cases, debounced-search, pagination, image-loading
\documentclass[8pt]{extarticle}
\usepackage{printup-sheet}
\usepackage{array}

\newcommand\pat[3][sheetBlue]{\par\noindent\fcolorbox{#1}{#1!5}{\parbox{\dimexpr\linewidth-2\fboxsep-2\fboxrule\relax}{\raggedright{\bfseries\color{#1}#2}\enspace #3}}\par\vspace{2pt}}
\lstdefinelanguage{Swift}{
  morekeywords={class,struct,protocol,extension,func,let,var,static,private,
    init,return,self,Self,final,guard,else,in,weak,mutating,async,await,try,
    throws,catch,do,is,enum,case,actor,some,if,nil},
  sensitive=true, morecomment=[l]{//}, morestring=[b]"}
\newcommand\say[1]{\textcolor{sheetGreen!60!black}{\itshape ``#1''}}

\begin{document}

\sheettitle{Live coding \& take-home — the game plan}{interview · memo}

\oneliner{They grade \textbf{how you think, out loud}, not only the final code:
\textbf{clarify → examples → brute force (say it) → optimise → code → test with the examples → complexity}.
A mostly-working solution with clear narration beats a silent perfect one. In iOS exercises the
extra marks are for the \textbf{platform hygiene}: \texttt{[weak self]}, main-thread UI, no \texttt{!}, cancellation, loading/error states.}

\vspace{3pt}
\noindent\begin{tikzpicture}[sheet,
    st/.style={box, minimum height=9mm, text width=19mm, font=\scriptsize},
    sy/.style={font=\scriptsize\itshape, text=sheetGreen!50!black, text width=21mm, align=center, inner sep=1pt}]
  \foreach \i/\n/\t/\s in {
      0/{\textbf{1 Clarify}\\inputs · limits · edges}/5/{``Can it be empty? Sorted? Duplicates?''},
      1/{\textbf{2 Examples}\\normal + edge, by hand}/2/{``[2,7,11], target 9 → I expect (0,1).''},
      2/{\textbf{3 Brute force}\\say it, cost it}/3/{``Nested loops, O(n\textsuperscript{2}) — works, but…''},
      3/{\textbf{4 Optimise}\\name the repeated work}/5/{``I re-scan; a hash map makes lookup O(1).''},
      4/{\textbf{5 Code}\\clean names, small fns}/20/{``Guarding the empty case first.''},
      5/{\textbf{6 Test}\\trace your examples}/7/{``Walking [2,7,11] line by line…''},
      6/{\textbf{7 Complexity}\\time AND space}/3/{``O(n) time, O(n) extra for the map.''}}
  { \node[st, fill=sheetBlue!\the\numexpr 5+2*\i\relax] (s\i) at (2.4*\i,0) {\n\\[-1pt]\textcolor{sheetOrange}{\textbf{\t\ min}}};
    \node[sy, anchor=north] (q\i) at (2.4*\i,-0.6) {\s}; }
  \foreach \i [evaluate=\i as \j using int(\i+1)] in {0,...,5} \draw[flow] (s\i) -- (s\j);
  \draw[hot, rounded corners=3pt] (s5.north) -- ++(0,0.3) -| node[pos=0.25, above, font=\scriptsize, text=sheetOrange]{bug found → fix, re-trace: \emph{you} find it, not them} (s4.north);
  \draw[hot, rounded corners=3pt, dashed] (q1.south) -- ++(0,-0.15) -| node[pos=0.25, below, font=\scriptsize, text=sheetOrange]{the same examples become your tests} (q5.south);
  \node[font=\scriptsize, text=sheetGrey, anchor=north west] at (-1.2,-1.75) {45-min round $\approx$ 10 plan · 20 code · 10 test/talk · 5 buffer — \textbf{check the clock}. Stuck $>$2 min → smaller example, or fall back to brute force, \emph{aloud}.};
\end{tikzpicture}

\begin{multicols}{2}
\small\setstretch{1.0}

\section{Communication rules}
\begin{itemize}
  \item \textbf{Narrate continuously} — silence is the biggest tell; they cannot help what they cannot hear.
  \item \textbf{State the approach, get a nod}, \emph{then} type: a wrong turn costs 30\,s, not 15 min.
  \item \textbf{Take hints fast}: acknowledge, use it, say why it helps — a hint means they want you to pass.
  \item \textbf{``I don't know''} → best hypothesis + how you'd verify it. Never bluff.
  \item \textbf{Finished early?} Offer extensions: edge cases, concurrency, tests, an alternative + trade-off.
  \item Readability is graded: \texttt{remaining}, not \texttt{r}; a helper, not copy-paste.
  \item \textbf{Edge cases to offer unprompted}: empty · one element · duplicates · negatives / zero ·
        huge \emph{n} (overflow, recursion depth) · already sorted / reversed · emoji in a \texttt{String} ·
        network: slow, offline, 401, malformed JSON, empty page.
\end{itemize}

\section{iOS pitfalls they watch for}
\pat[sheetRed]{Retain cycles}{closure stored by something \texttt{self} owns (\texttt{sink}, timer, stored completion) → \texttt{[weak self]} + \texttt{guard let self}; \texttt{weak var delegate} (protocol \texttt{: AnyObject}). Say it aloud.}
\pat[sheetRed]{Main-thread UI}{\texttt{dataTask} completion runs on a \emph{background} queue → hop to main. Better: \texttt{@MainActor} ViewModel that \texttt{await}s off-main work.}
\pat[sheetRed]{Force unwraps}{\texttt{URL(string:)!}, \texttt{as!}, \texttt{first!} → \texttt{guard let … else \{ throw \}}. A crash in the demo is fatal.}
\pat[sheetRed]{Codable edge cases}{\texttt{snake\_case} (\texttt{.convertFromSnakeCase} / \texttt{CodingKeys}) · missing key → \texttt{Optional} · \textbf{unknown enum case throws} → custom \texttt{init(from:)} with a fallback · number sent as string · \texttt{.iso8601} rejects fractional seconds.}
\pat[sheetRed]{Cancellation}{cancel the old search per keystroke and image loads on cell reuse. A cancelled \texttt{URLSession} call throws \texttt{URLError(.cancelled)}, \emph{not} \texttt{CancellationError} — swallow both, never show them.}

\section{Take-home — what gets graded}
\begin{itemize}
  \item \textbf{Builds and runs first time}; no missing keys, few or no deps.
  \item \textbf{README}: how to run · architecture + \emph{why} · trade-offs · ``with more time'' · time spent.
  \item \textbf{Structure}: View / ViewModel / Service behind protocols, injected — not a 600-line VC.
  \item \textbf{Tests}: VM with a fake service (loading→loaded, error, empty); decoding from a JSON fixture.
  \item \textbf{States}: loading, empty, error + retry, refresh; Dynamic Type / VoiceOver = bonus.
  \item Small readable commits; no over-engineering (no DI framework for 3 screens).
\end{itemize}

\columnbreak

\section{Typical iOS exercises → reach for}
\noindent\begin{tabular}{@{}>{\raggedright}p{0.2\linewidth}@{\hspace{1.5mm}}>{\raggedright\arraybackslash}p{0.76\linewidth}@{}}
\toprule
\textbf{Exercise} & \textbf{Reach for}\\\midrule
Fetch + list & \texttt{async} service → \texttt{@MainActor} VM with a \texttt{State} enum → \texttt{List} / diffable source\\
Image loading & \texttt{NSCache} → dedupe in-flight by URL → cancel on reuse → check the cell still wants that URL\\
Debounced search & cancel the previous \texttt{Task}, sleep 300\,ms, check \texttt{isCancelled} (or Combine \texttt{.debounce} + \texttt{removeDuplicates})\\
Pagination & fetch when a row \emph{N from the end} appears; \texttt{guard !isLoading, hasMore}; cursor; dedupe by \texttt{id}\\
Tiny cache & \texttt{actor} + dictionary + LRU order, or \texttt{NSCache} (thread-safe, object keys, evicts under memory pressure)\\
\bottomrule
\end{tabular}

\section{Skeleton to reach for}
\begin{lstlisting}[language=Swift, basicstyle=\ttfamily\scriptsize]
protocol ItemService { func search(_ q: String) async throws -> [Item] }

@MainActor final class SearchVM: ObservableObject {
  enum State { case idle, loading, loaded([Item]), failed(String) }
  @Published private(set) var state: State = .idle
  private let service: ItemService   // injected: fake in tests
  private var task: Task<Void, Never>?
  init(service: ItemService) { self.service = service }

  func queryChanged(_ q: String) {
    task?.cancel()                   // debounce = cancel + wait
    task = Task { [weak self] in
      try? await Task.sleep(for: .milliseconds(300))
      guard let self, !Task.isCancelled else { return }
      state = .loading
      do { state = .loaded(try await service.search(q)) }
      catch is CancellationError {}  // superseded: stay quiet
      catch let e as URLError where e.code == .cancelled {}
      catch { state = .failed(error.localizedDescription) }
    }
  }
}
// paging VM (items, isLoading, hasMore): call from each row's onAppear
func rowAppeared(_ item: Item) {
  guard let i = items.firstIndex(where: { $0.id == item.id }),
        i >= items.count - 5, !isLoading, hasMore else { return }
  Task { await loadNextPage() }      // cursor = last page's nextCursor
}
// cell.prepareForReuse(): loadTask?.cancel(); imageView.image = nil
\end{lstlisting}

\section{Interview traps}
\begin{itemize}
  \trap{Coding in the first 10\,s — you solve the wrong problem.}
  \trap{``Done'' without tracing; empty input / off-by-one found by \emph{them}.}
  \trap{``O(n)'' recited, ignoring the \textbf{space} of the map or recursion stack.}
  \trap{Image lands in a \emph{reused} cell → wrong picture: cancel \emph{and} compare the URL.}
\end{itemize}

\pat[sheetOrange]{Remember}{\textbf{Clarify, brute force aloud, optimise the repeated work, code, trace your own examples, state time + space.}}

\section{Likely prompts}
\begin{enumerate}
  \item Build a searchable list from this endpoint. — service + VM + debounce + states.
  \item Why \texttt{[weak self]} in that \texttt{Task}? — it would keep the VM alive until the sleep + request finish.
  \item How do you test the VM? — inject a fake service, assert the state sequence.
  \item More time? — caching, pagination, retry, accessibility.
\end{enumerate}

\end{multicols}

\noindent{\footnotesize\color{sheetGrey}\textit{Related:} algorithm-patterns · behavioral-star · ios-system-design-deep-dives · concurrency · arc-ownership · urlsession-networking · testing-viewmodels-ui-snapshot}

\end{document}
