如何将 .environment 全局绑定到所有 SwiftUI 预览?

How to Bind .environment Globally to All SwiftUI Previews?

提问人:Marius Bonifer 提问时间:10/26/2023 最后编辑:Marius Bonifer 更新时间:10/26/2023 访问量:91

问:

我有一个全局 DataManager,它保存了一个 NSManagedObjectContext:

class DataManager {
    static let shared = DataManager()
    var viewContext: NSManagedObjectContext
    // ... Other code
}

应用程序本身一切正常。但是,我在使用 SwiftUI 预览时遇到了问题。目前,我正在为每个预览设置.environment,如下所示:

#Preview {
    let exampleModel = ExampleModel.create(context: DataManager.shared.viewContext)
    
    return NavigationStack {
        ExampleView(model: exampleModel)
    }
    .environment(\.managedObjectContext, DataManager.shared.viewContext)
}

在处理多个视图时,这种方法变得乏味。我正在寻找一种方法来为所有 SwiftUI 预览全局设置 .environment(\.managedObjectContext, DataManager.shared.viewContext)。

有没有更有效的方法来实现这一目标?

我做了一些研究,为预览创建一个包装器似乎是可行的。但是,我还没有找到允许我继续使用 #Preview 宏的解决方案。

iOS Swift SwiftUI 核心数据

评论

0赞 malhal 10/27/2023
最好为每个仅包含所需示例数据的预览上下文创建不同的预览上下文。View

答:

0赞 lorem ipsum 10/26/2023 #1

您可以使用所有注入和示例代码创建一个。ViewModifier

extension View {
    func globalInjection() -> some View {
        modifier(GlobalInjectionVM())
    }
}
struct GlobalInjectionVM: ViewModifier {
    let context: NSManagedObjectContext = DataManager.shared.viewContext
    func body(content: Content) -> some View {
        content
            .environment(\.managedObjectContext, context)
    }
}

然后,您需要做的就是在每次预览时调用。func

#Preview {
    SampleView()
        .globalInjection()
}

如果你想加强这一点,你可以用previewprotocol

protocol SampleProviderProtocol {
    static func preview() -> Self
}

extension Item: SampleProviderProtocol {
    static func preview() -> Self {
        let new = Self(context: DataManager. shared.viewContext)
        new.timestamp = Calendar.current.date(byAdding: .day, value: (-10...10).randomElement()!, to: Date())
        return new
    }
}

然后,您可以在ViewModifier

struct GlobalInjectionVM: ViewModifier {
    let context: NSManagedObjectContext = DataManager.shared.viewContext
    //Declare the types that you want to create samples 
    let types: [SampleProviderProtocol.Type] = [Item.self]
    func body(content: Content) -> some View {
        content
            .environment(\.managedObjectContext, context)
            .task {
                //Iterate over the types
                for type in types {
                    //Create 4 preview objects
                    for _ in 0...3 {
                        _ = type.preview()
                    }
                }
            }
    }
}

确保在使用 Canvas 时使用“预览”上下文,您可以通过在加载商店之前将 url 设置为 null 来使用它。

container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")

评论

0赞 Marius Bonifer 10/26/2023
非常感谢:)这正是我一直在寻找的^^