Pingline — rubric and defend your codeswift-6.4/ios-26
Lesson 6 / 6
Unit 26 · Pingline

Pingline — rubric and defend your code

Note

Unit 26 · Pingline rubric. Score yourself honestly, then answer the defend-your-code prompts out loud. If you cannot defend a choice, that is the part to revisit before moving to Staybnb.

A messaging app is judged on how it behaves when the network misbehaves. Anyone can render a list of rows from a happy socket; the bar here is graceful degradation and correctness under concurrency. Grade the behavior, not the code you wish you had written.

Rubric

Area Meets the bar Falls short
Live receive A message from another client appears in the open thread with no refresh Requires a pull-to-refresh or reopening the thread
Optimistic send Own message shows instantly, then confirms or shows failed Waits for the server before showing; no failure state
Echo dedupe Own message appears exactly once after the server echo Message shows twice, or the optimistic row is orphaned
Connection actor Status and outbox live in an actor; no data-race warnings Mutable connection state in a class touched off-main
Reentrancy Check-then-act sections are synchronous; no double-send under burst guard/read, then await, then mutate on stale state
Offline buffer Sends queue while down and flush once on reconnect Sends silently lost while offline, or flushed twice
Ordering Transcript sorts by createdAt, stable across reconnect Renders in arrival order; reorders on a replay
Groups Group threads attribute each message to its sender (avatar + name) One thread, no idea who said what
Reactions & attachments Long-press tapbacks render as badges; image messages render an attachment bubble No reactions; text-only messages
Read receipts Delivered → Read shown under the last sent message only No receipt, or a receipt under every message
Lifecycle Stream unsubscribes when the thread closes Leaks a live subscription per thread visit
Craft Light/dark correct, VoiceOver labels, scroll-to-bottom Dark mode broken, unlabeled controls

Give yourself "meets the bar" only if you have seen the behavior with two clients and a toggled network, not merely written the code for it.

Defend your code

Answer these as if in review. The strong answers reference the exact mechanisms you built.

Why is connection state an actor? Because its mutable state — connection status and the outbox — is written from several tasks concurrently: the send path, the receive/reconnect path, and any status observers. An actor serializes that access so no two tasks corrupt the buffer, without a hand-written lock. It is not @Observable because it is infrastructure hit off the main actor, not view state.

What happens to optimistic messages if the send fails? The optimistic row was inserted as sending and located by its client-generated id. On the thrown error the send task flips that row to failed (found by id, not index), so the UI shows a clearly failed message with a retry affordance. Nothing is silently dropped, and the row is never mistaken for a delivered message.

Why does the server echo of your own message not create a duplicate? Because the optimistic message and the echo share the same client-generated UUID. The insert path dedupes by id: when the echo arrives, the row is recognized as already present and dropped, leaving the single row you already showed.

Where is the reentrancy hazard in your outbox flush, and how did you close it? The hazard is a guard/read on pending and the sequence counter, then an await on the network send, then a mutation assuming the state is unchanged. I closed it by claiming the work synchronously — removeFirst() and advancing the counter before the await — so no other concurrent flush sees the same message or number.

What guarantees message order if Realtime delivers inserts out of order? Every message carries a createdAt, and the view model sorts by it after each insert. Arrival order and reconnect replays do not affect the rendered order because the timestamp is the sort key, not insertion sequence.

Knowledge check

Q: An interviewer kills your network mid-conversation. What should they observe? Sends queue in the outbox and the UI reflects that (a pending/failed state or a banner); when the socket returns, the buffer flushes exactly once and the transcript reconciles. No lost sends, no duplicates.

Q: What is the single most important test to have for this project? A concurrency test that fires many sends/flushes at once and asserts no message is duplicated or dropped — the outbox-under-burst test that mirrors the reentrancy checkpoint. It is the one that catches the bug that never shows up single-threaded.