Two SwiftData @Query Pitfalls — enum #Predicate and modelContext on Empty Lists

The Setup I was building Inbox/Archive list views for a document scanning app. Simple enough: filter documents by status using SwiftData’s @Query. Two compile-time surprises were waiting. Pitfall 1: #Predicate Can’t Compare Enums What I Wrote The natural approach: @Model class Document { var status: DocumentStatus // enum stored property // ... } enum DocumentStatus: String, Codable { case inbox, archived } Then in the view: @Query( filter: #Predicate<Document> { $0.status == .inbox }, sort: \Document.createdAt, order: .reverse ) private var documents: [Document] Looks clean. Doesn’t compile. ...

April 26, 2026 · 3 min · Young

Why Naming Your SwiftData Model 'Tag' Breaks Your Build (+ Xcode 16 .gitkeep Trap)

What I Was Trying to Do I started a new document scanning app and was setting up SwiftData models using TDD. Simple Document and Tag models: @Model class Tag { var name: String var color: String @Relationship(inverse: \Document.tags) var documents: [Document] } Built fine. No issues so far. Trap #1: That’s Not Your Tag The moment I used FetchDescriptor in a test file, it blew up: let fetched = try context.fetch(FetchDescriptor<Tag>()) error: 'Tag' is ambiguous for type lookup in this context Turns out SwiftUI already has a Tag type — the one used for identifying selection items in TabView and Picker. When your test file imports both @testable import scansort and SwiftUI (via SwiftData), the compiler can’t decide which Tag you mean. ...

April 26, 2026 · 3 min · Young

Why SwiftUI Image Zoom Feels Wrong — and When UIScrollView Is the Answer

The Problem I needed a full-screen image viewer for a document scanning app. Pinch to zoom in, drag to pan around while zoomed. Standard stuff. Surely SwiftUI can handle this natively, right? The Journey Attempt 1: MagnifyGesture Only The straightforward approach: Image(uiImage: uiImage) .scaleEffect(scale) .gesture( MagnifyGesture() .onChanged { value in scale = max(1.0, value.magnification) } .onEnded { _ in withAnimation { scale = 1.0 } } ) Releasing fingers snaps back to 1x. Adding lastScale to persist the zoom level works, but now there’s no way to pan the zoomed image. ...

April 26, 2026 · 3 min · Young

Debugging SwiftUI Image Flickering: Three Bugs Stacked on Top of Each Other

The Problem I was building a music streaming app, and the album artwork kept flickering. Not just during track changes — even while playing the same song, the artwork would briefly disappear and reappear. The blurred background on the full player was especially bad. I figured it was slow image loading, so I added caching. Still flickered. Dug deeper and found three bugs stacked on top of each other. ...

April 21, 2026 · 3 min · Young

Why Storing Closures in SwiftUI @Observable Causes EXC_BAD_ACCESS

What I Was Trying to Do I was building an iOS music streaming app with detail views behind NavigationDestination. Each detail view had its own ViewModel, with API calls injected as closures. Something like this: @Observable final class PlaylistDetailVM { var songs: [Song] = [] var isLoading = false // API methods injected as closures private let fetchSongs: (String) async throws -> [Song] private let addToPlaylist: (String, [String]) async throws -> Void init( fetchSongs: @escaping (String) async throws -> [Song], addToPlaylist: @escaping (String, [String]) async throws -> Void ) { self.fetchSongs = fetchSongs self.addToPlaylist = addToPlaylist } } Created as @State inside NavigationDestination: ...

April 21, 2026 · 3 min · Young