The BinBrain iOS SwiftUI client has been evolving quickly over the past two weeks. Two recent additions — an iPad split-view layout and a per-item “teach” toggle that feeds confirmed class names back to YOLO-World — brought interesting design decisions worth documenting.
iPad Split-View with NavigationSplitView
Before this work, BinBrain had zero iPad-specific code. The entire app ran as an iPhone layout stretched to fill the screen. The fix was straightforward: check horizontalSizeClass at the root and swap between NavigationSplitView (iPad) and TabView (iPhone).
@Environment(\.horizontalSizeClass) private var sizeClass
var body: some View {
if sizeClass == .regular {
splitView
} else {
tabView
}
}
The sidebar uses a SidebarSection enum conforming to CaseIterable, which keeps the sidebar list declarative and easy to extend:
enum SidebarSection: String, CaseIterable, Identifiable {
case bins, search, settings
var title: String { /* ... */ }
var systemImage: String { /* ... */ }
}
NavigationStack Inside NavigationSplitView
One concern was whether each tab’s existing NavigationStack would conflict with the NavigationSplitView detail pane. It doesn’t. Apple’s recommended pattern is exactly this: each detail view keeps its own NavigationStack. No need to extract content-only subviews or restructure the navigation hierarchy.
Teaching YOLO-World From the Review Screen
BinBrain’s cataloging flow works like this: snap a photo → AI vision suggests items → user reviews and confirms. The new “teach” feature adds a step: after the user confirms an item, the app sends its name to POST /classes/confirm so the server can add it to YOLO-World’s active class list for faster future detection.
The Toggle
Each suggestion row now includes a “Teach for future detection” toggle, defaulting to true. The rationale: most users want to build their class library over time; power users who don’t can toggle it off per item.
Toggle(isOn: $viewModel.editableSuggestions[idx].teach) {
Label("Teach for future detection", systemImage: "brain")
.font(.caption)
.foregroundStyle(.secondary)
}
.toggleStyle(.switch)
.tint(.green)
Fire-and-Forget Design
The critical design decision: confirmClass failures are silently swallowed with try?. The teach feature is an enhancement — it should never block the primary upsert flow. If the server is down or the endpoint isn’t deployed yet, the user’s items still get saved.
// Confirm taught items as YOLO-World classes (fire-and-forget).
for idx in includedIndices where editableSuggestions[idx].teach {
let s = editableSuggestions[idx]
let category = s.editedCategory.isEmpty ? nil : s.editedCategory
try? await apiClient.confirmClass(
className: s.editedName, category: category
)
}
The Server Contract
The POST /classes/confirm endpoint accepts a class name and optional category, and returns whether the class was newly added or already existed (idempotent). The server normalizes names to lowercase and triggers a YOLO-World class list reload when a new class is added.
struct ConfirmClassResponse: Decodable {
let version: String
let className: String
let added: Bool
let activeClassCount: Int
let reloadTriggered: Bool
}
Also: Server-Side Score
A smaller change worth noting: search results previously carried a raw pgvector cosine distance (range 0–2) and the iOS client computed a 0–1 similarity score via 1.0 - (distance / 2.0). The server now provides score directly, so the client-side computation was removed. One less place for the formula to drift out of sync.
Testing Strategy
The teach toggle required path-aware mock handlers in tests — the same MockURLProtocol now inspects the request path to distinguish /items upsert calls from /classes/confirm calls, returning different response payloads and tracking call counts independently. This pattern made it straightforward to verify that teach-disabled items skip the confirm call, and that confirm failures don’t propagate to the upsert result.
What’s Next
The iPad split-view needs testing on actual iPad hardware to refine sidebar proportions. After that: VoiceOver and Dynamic Type accessibility audit, automated UI tests, and App Store submission prep. The BinBrain backend continues to evolve in parallel.
This post was generated by Claude, an AI assistant by Anthropic, as an exercise in learning extraction and technical documentation. The content reflects real work performed during a development session, with AI assistance in both the implementation and the writing.

Leave a Reply
You must be logged in to post a comment.