PlanBoard — Interview Talking Points
The 30-second pitch
“PlanBoard is a Kanban app I shipped as a true universal binary — iPhone, iPad, and Mac, one codebase, native everywhere. About 80% of the SwiftUI views are shared across all three. The remaining 20% splits into two layers: small platform-conditional modifiers, and structural platform-specific shells where the Mac gets its own sidebar+toolbar+CommandMenu+MenuBarExtra and the iPhone gets a stack. CloudKit syncs through SwiftData. WidgetKit on iOS, MenuBarExtra on Mac (same view, different host). AppIntents work in Shortcuts on every platform. The artifact I’m proudest of is the platform decision record — every
#if os(...)in the codebase has a dated entry explaining why.”
The 3-minute deep dive (cross-platform strategy)
“The hardest thing about a universal SwiftUI app isn’t the code — SwiftUI compiles across platforms — it’s the decisions. Should the sidebar look the same on Mac and iPad? Should the Mac use a popover or a window for card detail? Should I write a CommandMenu or rely on the global File menu? Each one is small in isolation; together they decide whether the app feels native everywhere or iOS-on-Mac.
I settled on three layers. Layer one is shared views — a
CardViewrenders identically on every platform because the underlying SwiftUI primitives translate well. About 80% of the UI fits here. Layer two is conditional modifiers — same view, but with#if os(macOS)for things like hover effects or sidebar style. About 15%. Layer three is platform shells — completely separateiPhoneRootView,iPadRootView,MacRootView. About 5%, but it’s where the most user-perceptible decisions live.The Mac shell is the most work. SwiftUI gives you
NavigationSplitView,Toolbar,CommandGroup,MenuBarExtra, multi-WindowGroup— but each one has Mac-specific configuration that doesn’t apply on iOS. I had to learn the right balance: enough native Mac chrome to feel right, not so much that the codebase becomes unmaintainable.The artifact that captures this is the
platform-decision-record.md. Every#if os(...)block in the code has a dated entry. ‘Why does the sidebar collapse differently?’ has an answer. ‘Why no Toolbar on iPhone?’ has an answer. The cost of maintaining it is low — write the entry when you write the conditional — and the payoff is huge in code review and onboarding. Companies that ship cross-Apple-platform almost never have this document; I think it should be table stakes for senior work.“
12 interview questions
1. “Why universal SwiftUI and not Catalyst?”
Catalyst would compile faster — it’s literally taking iOS UIKit and giving it a Mac windowed surface. But the result feels iOS-derivative. Buttons are tinted with iOS blue. Toolbars are sized for touch. CommandMenu doesn’t quite work. For a portfolio piece demonstrating “I can ship a Mac app,” Catalyst defeats the demonstration. Native SwiftUI on Mac is more work but produces an app a Mac user can’t tell is cross-platform.
2. “What about Swift Package Manager vs Xcode targets?”
I use both. SPM packages for code (PlanBoardCore, PlanBoardShared, etc.) — they’re easy to share between targets, build independently, and test in isolation. Xcode targets for the platform shells, app extensions, and widgets — those need Info.plist, entitlements, capabilities. The boundary is: pure Swift code lives in packages, app metadata lives in targets.
3. “Walk me through the layer strategy.”
Three layers. Layer 1: shared views. A CardView works unchanged on iOS, iPadOS, macOS. About 80% of the UI. Layer 2: platform-conditional modifiers, applied via extensions on View. Same view, but with #if os(macOS) for things like hover or sidebar style. About 15%. Layer 3: platform-specific shells — separate iPhoneRootView, iPadRootView, MacRootView files. About 5% but where the most user-perceptible decisions live. The discipline: default to layer 1, escalate to 2 only if the conditional is small, escalate to 3 if it’s structural.
4. “Why a platform-decision-record.md?”
Every cross-platform app accumulates #if os(...) blocks. A year later, nobody remembers why. The PDR (platform decision record) is a single file with a dated entry per conditional explaining the why. It’s reviewable in PRs (‘this change adds an #if; update the PDR’). It’s onboarding material. It’s an interview artifact. The cost is one paragraph per conditional; the payoff is that the codebase’s cross-platform decisions are explicit rather than implicit folklore.
5. “How does MenuBarExtra work?”
It’s a SwiftUI Scene type added in macOS 13. You declare it alongside your WindowGroup. It creates a menu bar item; its body is a SwiftUI view shown in a popover (.menuBarExtraStyle(.window)) or a menu (.menu). Same process as the main app — shared SwiftData container, shared state. Much simpler than implementing a separate menu bar app talking to the main via IPC.
6. “Multi-window?”
WindowGroup(id: "board", for: UUID.self). Each window has its own state. Open via @Environment(\.openWindow). Cross-window drag-and-drop works because SwiftUI’s Transferable protocol works across windows. Cards have a Transferable conformance carrying their UUID; the receiving window resolves it via SwiftData.
7. “How does CloudKit handle sync across platforms?”
Same iCloud.com.yourorg.planboard container is used on iOS, iPadOS, macOS. SwiftData’s CloudKit integration handles upload/download. Conflict resolution is last-writer-wins (fine for this app, would not be for a multi-user app). Latency is 1-5 s typical. Devices auto-sync via silent push when one is awake; if both are asleep, sync happens on next foreground.
8. “Why no conflict resolution like in NoteSync?”
Different problem. PlanBoard is single-user. The only conflict is the user edits on iPhone and iPad simultaneously — rare, and last-writer-wins is good enough. NoteSync is multi-user, where conflicts are common and lossy resolution would be unacceptable. Match the conflict-resolution complexity to the actual conflict rate.
9. “What’s the WidgetKit story across iOS and macOS?”
iOS: widget extension shows on home screen. macOS: MenuBarExtra plays the same role (Apple doesn’t have widgets in the macOS sense; the menu bar item IS the widget). I built one shared TopCardsView and instantiated it in both hosts. About 20 lines of platform-specific glue for each host; the view itself is identical.
10. “AppIntents — how do they work cross-platform?”
Same intents package built into both targets. Shortcuts on each platform discovers them. Same parameter UI. Siri works the same on every platform that has Siri (iOS, iPadOS, watchOS — macOS Siri is more limited). I tested on each platform and they all worked without modification.
11. “Tell me about a cross-platform bug.”
Drag-and-drop on Mac wouldn’t work between columns. Worked on iPad. Took half a day. Turned out my Transferable conformance used .suggestedFileName which iOS ignored but macOS required to be non-nil. Fix: provide a default. Lesson: SwiftUI’s cross-platform APIs sometimes have platform-specific behavioral requirements even when the types are identical. Test on every target.
12. “How would you add Apple Watch?”
watchOS is a fourth target. Same PlanBoardCore + PlanBoardShared SwiftPM packages reused. Watch shell is dedicated — WKApplicationDelegate lifecycle, smaller UI, complications for the watch face. CloudKit sync is the same private DB. About a week’s work after the iOS/iPad/Mac story is solid. The reason I didn’t include it in v1: scope. Each platform is a non-trivial polish task, and stopping at 3 lets me get to v1 in 3 weeks instead of 4.
Red-flag answers
If asked “could you have done this with Catalyst,” don’t be defensive. Say: “Yes, in less time. But the resulting Mac app would have felt iOS-derivative. For PlanBoard’s portfolio purpose — demonstrating native Mac development — native SwiftUI is the right tradeoff. For a different app where Mac is secondary to iOS, I’d happily use Catalyst.”
If asked “why no Apple Watch app,” don’t say “I didn’t get to it.” Say: “Scope. Watch is a fourth platform with its own UX patterns, complications, and shell. Adding it well is a week of work after the iOS/iPad/Mac story is stable. I’d add it in v2 once those three are battle-tested in production.”
Next: Platform decision record, then on to the Appendix.