From 24639081c09a46258a33c0a1a4f50e1afd880e28 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 10 Feb 2026 01:32:31 +0100 Subject: [PATCH] Implemented the EmptyContainerModifier preview modifier in the app target. --- .../Modifiers/EmptyContainerModifier.swift | 79 +++++++++++++++++++ App/Sources/Views/NewTaskView.swift | 6 +- Todos.xcodeproj/project.pbxproj | 4 +- 3 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 App/Previews/Modifiers/EmptyContainerModifier.swift diff --git a/App/Previews/Modifiers/EmptyContainerModifier.swift b/App/Previews/Modifiers/EmptyContainerModifier.swift new file mode 100644 index 0000000..9f9cd28 --- /dev/null +++ b/App/Previews/Modifiers/EmptyContainerModifier.swift @@ -0,0 +1,79 @@ +import SwiftData +import SwiftUI + +/// A preview modifier that provides an empty SwiftData model container for SwiftUI previews. +/// +/// This modifier creates an in-memory SwiftData model container with no data, useful for testing and previewing views in their empty or initial state. +/// It's particularly helpful for testing empty state UI, loading states, or views that should gracefully handle no data. +/// +/// ## Usage +/// Use the `.emptyContainer` trait on your preview: +/// +/// ```swift +/// #Preview(traits: .emptyContainer) { +/// TaskGroupListView() +/// } +/// ``` +/// +/// This automatically provides an empty model container to the preview, allowing SwiftData queries to return empty results. +struct EmptyContainerModifier: PreviewModifier { + + // MARK: Functions + + /// Creates and configures a shared in-memory model container with no data. + /// + /// This method is called once when the preview is loaded and creates an in-memory + /// SwiftData container with no persisted entities. The container is configured + /// to store data only in memory, ensuring previews don't affect any persistent storage. + /// + /// - Returns: An empty `ModelContainer` configured for in-memory storage. + /// - Throws: An error if the model container cannot be created. + static func makeSharedContext() async throws -> ModelContainer { + try ModelContainer( + for: Todo.self, + configurations: .init(isStoredInMemoryOnly: true) + ) + } + + /// Applies the empty model container to the preview content. + /// + /// This method is called by SwiftUI to apply the modifier to the preview. + /// It injects the empty model container into the SwiftUI environment, making it + /// available to any views that use `@Query` or `@Environment(\.modelContext)`. + /// Queries will return empty results, allowing you to test empty state behavior. + /// + /// - Parameters: + /// - content: The preview content to modify. + /// - context: The empty model container. + /// - Returns: The content with the empty model container applied. + func body( + content: Content, + context: ModelContainer + ) -> some View { + content.modelContainer(context) + } + +} + +// MARK: - PreviewTrait + +extension PreviewTrait where T == Preview.ViewTraits { + + /// A preview trait that provides an empty model container. + /// + /// Use this trait to test views in their empty state without any data. + /// This is particularly useful for testing empty state UI, placeholder content, + /// and ensuring views handle the absence of data gracefully. + /// + /// ## Example + /// ```swift + /// #Preview("Empty State", traits: .emptyContainer) { + /// TaskGroupListView() + /// } + /// ``` + /// + /// The view will receive an empty SwiftData container, causing any `@Query` + /// properties to return empty collections. + static var emptyContainer: Self = .modifier(EmptyContainerModifier()) + +} diff --git a/App/Sources/Views/NewTaskView.swift b/App/Sources/Views/NewTaskView.swift index c25b455..7b95d0b 100644 --- a/App/Sources/Views/NewTaskView.swift +++ b/App/Sources/Views/NewTaskView.swift @@ -99,7 +99,9 @@ struct NewTaskView: View { // MARK: - Previews -#Preview("New todo task") { +#Preview( + "New todo task", + traits: .emptyContainer +) { NewTaskView() - .modelContainer(for: Todo.self, inMemory: true) } diff --git a/Todos.xcodeproj/project.pbxproj b/Todos.xcodeproj/project.pbxproj index 3688d17..7747608 100644 --- a/Todos.xcodeproj/project.pbxproj +++ b/Todos.xcodeproj/project.pbxproj @@ -378,7 +378,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = App/Previews/Modifiers/SampleTaskDataModifier.swift; + DEVELOPMENT_ASSET_PATHS = "App/Previews/Modifiers/SampleTaskDataModifier.swift App/Previews/Modifiers/EmptyContainerModifier.swift"; DEVELOPMENT_TEAM = 7FMNM89WKG; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -424,7 +424,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = App/Previews/Modifiers/SampleTaskDataModifier.swift; + DEVELOPMENT_ASSET_PATHS = "App/Previews/Modifiers/SampleTaskDataModifier.swift App/Previews/Modifiers/EmptyContainerModifier.swift"; DEVELOPMENT_TEAM = 7FMNM89WKG; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES;