如何在SwiftUI预览中预览核心数据



下面是我的演示(有很多代码,但我希望有人能理解(
我在核心数据中有一个名为Activity的实体,其中有一个字符串字段。为此,我使用此扩展来显示预览中的数据:

extension Activity {
var _name: String {
name ?? ""
}

static var example: Activity {
let controller = DataController(inMemory: true)
let viewContext = controller.container.viewContext
let activity = Activity(context: viewContext)
activity.name = "running"

return activity
}
}

为了设置核心数据,我使用DataController对象:

class DataController: ObservableObject {
let container: NSPersistentCloudKitContainer

init(inMemory: Bool = false) {
container = NSPersistentCloudKitContainer(name: "Model")

if inMemory {
container.persistentStoreDescriptions.first?.url = URL(fileURLWithPath: "/dev/null")
}

container.loadPersistentStores { storeDescription, error in
if let _ = error {
fatalError("Fatal error loading store")
}
}
}

static var preview: DataController = {
let dataController = DataController(inMemory: true)
let viewContext = dataController.container.viewContext

do {
try dataController.createSampleData()
} catch {
fatalError("Fatal error creating preview")
}

return dataController
}()

func createSampleData() throws {
let viewContext = container.viewContext

for _ in 1...10 {
let activity = Activity(context: viewContext)
activity.name = "run"
}

try viewContext.save()
}
}

在应用程序文件中,我进行以下设置:

struct TestApp: App {
@StateObject var dataController: DataController
@Environment(.managedObjectContext) var managedObjectContext

init() {
let dataController = DataController()
_dataController = StateObject(wrappedValue: dataController)
}

var body: some Scene {
WindowGroup {
ContentView()
.environment(.managedObjectContext, dataController.container.viewContext)
}
}
}

在我的ContentView中,我显示了一个来自Core Data的字符串列表,它工作正常:

struct ContentView: View {
let activities: FetchRequest<Activity>

init() {
activities = FetchRequest<Activity>(entity: Activity.entity(), sortDescriptors: [NSSortDescriptor(keyPath: Activity.name, ascending: false)], predicate: nil)
}

var body: some View {
List {
ForEach(activities.wrappedValue) { activity in
ActivityView(activity: activity)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var dataController = DataController.preview

static var previews: some View {
ContentView()
.environment(.managedObjectContext, dataController.container.viewContext)
.environmentObject(dataController)
}
}

但在我的ActivityView中,我在一个简单的文本字段中显示字符串,预览是不起作用的。

struct ActivityView: View {
let activity: Activity

init(activity: Activity) {
self.activity = activity
}

var body: some View {
Text(activity._name)
}
}
struct ActivityView_Previews: PreviewProvider {
static var previews: some View {
ActivityView(activity: Activity.example)
}
}

我可以看到字符串";运行";在我的列表中,按设置方式显示了10次,但在ActivityView屏幕中,我看不到预览中显示的任何内容
不知道为什么,我希望有人知道。

编辑:

我在预览中也尝试过这个,但仍然不起作用。

struct ActivityView_Previews: PreviewProvider {
static var dataController = DataController.preview

static var previews: some View {
ActivityView(activity: Activity(context: dataController.container.viewContext))
.environment(.managedObjectContext, dataController.container.viewContext)
}
}

在SwiftUI中,我们使用View层次结构将富模型类型转换为简单类型。因此,解决这个问题的最佳方法是重新设计ActivityView,使其使用简单的类型,而不是模型类型,这样它就可以在不创建托管对象的情况下进行预览。我建议观看Structure your app for SwiftUI预览,其中涵盖了这项技术,并提供了一些其他技术,如协议和泛型。

顺便说一句,我也注意到了这个问题:

init() {
let dataController = DataController()
_dataController = StateObject(wrappedValue: dataController)
}

StateObject init使用@autoclosure,例如

@inlinable public init(wrappedValue thunk: @autoclosure @escaping () -> ObjectType)

这意味着对象init需要在括号内,例如

_dataController = StateObject(wrappedValue: DataController())

这就是防止每次SwiftUI重新计算View层次结构时对象被反复初始化的原因。

最新更新