BinBrain iOS: iPad Split-View and Teaching YOLO-World From the Review Screen

BinBrain iOS SwiftUI - iPad split-view with AI vision neural network illustration

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