使用 CoreDataViewModel 时如何在 Canvas 中预览核心数据对象
How to preview Core Data objects in Canvas when using a CoreDataViewModel
以下代码有效地使用了 @StateObject
和 @ObservedObject
包装器来捕获来自 ObservableObject
视图模型的更改。一切都按预期工作,除了我希望能够在 Canvas 中显示虚拟数据以用于设计目的。正如您从下面的代码中看到的,我能够创建一个 Car
对象 inMemory
并在 Preview
结构中有效地检索它,我的问题是在尝试显示多辆汽车时,如您所见,为了显示多辆汽车,我必须在 CarsView
的 Preview
结构中复制 List
的代码。在下面的代码中,这似乎没什么大不了的,因为我删除了很多构成 List
的代码,但在我的生产代码中,我有很多代码来自定义行。
这真的是显示通常由 ViewModel 管理的数据的最佳方式吗?
有没有一种无需重复大量 SwiftUI
代码即可显示数据的方法?
SwiftUI 视图
父视图/内容视图
struct ContentView: View {
@StateObject var coreDataViewModel = CoreDataViewModel()
var body: some View {
TabView(selection: 1){
CarsView(coreDataViewModel: coreDataViewModel)
.tabItem {
Text("Cars")
}.tag(1)
// other tabs...
}
}
}
第二视图/汽车视图
我的问题是,我在预览结构中复制主视图中的代码。
struct CarsView: View {
@ObservedObject var coreDataViewModel:CoreDataViewModel
var body: some View {
List {
ForEach(coreDataViewModel.cars) { car in
// custom row to display cars
}
}
}
}
struct CarsView_Previews: PreviewProvider {
@Environment(\.managedObjectContext) private var viewContext
static var previews: some View {
CarsView(coreDataViewModel: CoreDataViewModel())
let context = CoreDataManager.preview.container.viewContext
let requestCar: NSFetchRequest<Car> = Car.fetchRequest()
let fetchedCar = (try! context.fetch(requestCar).first)!
let cars = [fetchedCar]
// repeated code
List{
ForEach(cars) { car in
Text(car.make ?? "")
}
}
}
}
核心数据
实体和属性
汽车
- 制作:字符串
- 模型:字符串
核心数据管理器
class CoreDataManager{
static let instance = CoreDataManager()
static var preview: CoreDataManager = {
let result = CoreDataManager(inMemory: true)
let viewContext = result.container.viewContext
// new car
let car = Car(context: viewContext)
car.make = "Ford"
car.model = "Mustang"
do {
try viewContext.save()
} catch {
}
return result
}()
let container: NSPersistentContainer
let context: NSManagedObjectContext
init(inMemory: Bool = false){
container = NSPersistentContainer(name: "CoreDataContainer")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores { (description, error) in
if let error = error{
print("Error loading Core Data. \(error)")
}
}
context = container.viewContext
}
func save(){
do{
try context.save()
print("Saved successfully!")
}catch let error{
print("Error saving Core Data. \(error.localizedDescription)")
}
}
}
核心数据视图模型
class CoreDataViewModel: ObservableObject{
let manager = CoreDataManager.instance
@Published var cars: [Car] = []
init(){
getCars()
}
// addCar, deleteCar, updateCar etc. methods...
func getCars(){
let request = NSFetchRequest<Car>(entityName: "Car")
let sort = NSSortDescriptor(keyPath: \Car.model, ascending: true)
request.sortDescriptors = [sort]
do{
cars = try manager.context.fetch(request)
}catch let error{
print("Error fetching businesses. \(error.localizedDescription)")
}
}
func save(){
self.manager.save()
}
}
预览的目的是使用普通视图和一些给定的数据,而不是从实际视图中复制代码。除此之外,你似乎还有很多东西。
我会将核心数据管理器 class 注入视图模型以开始:
class CoreDataViewModel: ObservableObject{
let manager: CoreDataManager
@Published var cars: [Car] = []
init(coreDataManager: CoreDataManager = .instance){
self.manager = coreDataManager
getCars()
}
那么我会把预览简化为
struct CarsView_Previews: PreviewProvider {
static var previews: some View {
CarsView(coreDataViewModel: CoreDataViewModel(coreDataManager: .preview))
}
}
您已经在 preview
中创建了一个 Car 对象,所以为什么不在循环中执行此操作,这里我已将其移动到一个单独的函数中,可以从 preview
声明中调用或单独
#if DEBUG
extension CoreDataManager {
func createMockCars() {
for i in 1...5 {
let car = Car(context: self.context)
car.make = "Make \(i)"
car.model = "Model \(i)"
}
try! self.context.save()
}
}
#endif
如其中一条评论所述,您应该考虑将视图模型重命名为与 CarsViewModel 或 CarListViewModel 等汽车有关的名称。
以下代码有效地使用了 @StateObject
和 @ObservedObject
包装器来捕获来自 ObservableObject
视图模型的更改。一切都按预期工作,除了我希望能够在 Canvas 中显示虚拟数据以用于设计目的。正如您从下面的代码中看到的,我能够创建一个 Car
对象 inMemory
并在 Preview
结构中有效地检索它,我的问题是在尝试显示多辆汽车时,如您所见,为了显示多辆汽车,我必须在 CarsView
的 Preview
结构中复制 List
的代码。在下面的代码中,这似乎没什么大不了的,因为我删除了很多构成 List
的代码,但在我的生产代码中,我有很多代码来自定义行。
这真的是显示通常由 ViewModel 管理的数据的最佳方式吗?
有没有一种无需重复大量 SwiftUI
代码即可显示数据的方法?
SwiftUI 视图
父视图/内容视图
struct ContentView: View {
@StateObject var coreDataViewModel = CoreDataViewModel()
var body: some View {
TabView(selection: 1){
CarsView(coreDataViewModel: coreDataViewModel)
.tabItem {
Text("Cars")
}.tag(1)
// other tabs...
}
}
}
第二视图/汽车视图
我的问题是,我在预览结构中复制主视图中的代码。
struct CarsView: View {
@ObservedObject var coreDataViewModel:CoreDataViewModel
var body: some View {
List {
ForEach(coreDataViewModel.cars) { car in
// custom row to display cars
}
}
}
}
struct CarsView_Previews: PreviewProvider {
@Environment(\.managedObjectContext) private var viewContext
static var previews: some View {
CarsView(coreDataViewModel: CoreDataViewModel())
let context = CoreDataManager.preview.container.viewContext
let requestCar: NSFetchRequest<Car> = Car.fetchRequest()
let fetchedCar = (try! context.fetch(requestCar).first)!
let cars = [fetchedCar]
// repeated code
List{
ForEach(cars) { car in
Text(car.make ?? "")
}
}
}
}
核心数据
实体和属性
汽车
- 制作:字符串
- 模型:字符串
核心数据管理器
class CoreDataManager{
static let instance = CoreDataManager()
static var preview: CoreDataManager = {
let result = CoreDataManager(inMemory: true)
let viewContext = result.container.viewContext
// new car
let car = Car(context: viewContext)
car.make = "Ford"
car.model = "Mustang"
do {
try viewContext.save()
} catch {
}
return result
}()
let container: NSPersistentContainer
let context: NSManagedObjectContext
init(inMemory: Bool = false){
container = NSPersistentContainer(name: "CoreDataContainer")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores { (description, error) in
if let error = error{
print("Error loading Core Data. \(error)")
}
}
context = container.viewContext
}
func save(){
do{
try context.save()
print("Saved successfully!")
}catch let error{
print("Error saving Core Data. \(error.localizedDescription)")
}
}
}
核心数据视图模型
class CoreDataViewModel: ObservableObject{
let manager = CoreDataManager.instance
@Published var cars: [Car] = []
init(){
getCars()
}
// addCar, deleteCar, updateCar etc. methods...
func getCars(){
let request = NSFetchRequest<Car>(entityName: "Car")
let sort = NSSortDescriptor(keyPath: \Car.model, ascending: true)
request.sortDescriptors = [sort]
do{
cars = try manager.context.fetch(request)
}catch let error{
print("Error fetching businesses. \(error.localizedDescription)")
}
}
func save(){
self.manager.save()
}
}
预览的目的是使用普通视图和一些给定的数据,而不是从实际视图中复制代码。除此之外,你似乎还有很多东西。
我会将核心数据管理器 class 注入视图模型以开始:
class CoreDataViewModel: ObservableObject{
let manager: CoreDataManager
@Published var cars: [Car] = []
init(coreDataManager: CoreDataManager = .instance){
self.manager = coreDataManager
getCars()
}
那么我会把预览简化为
struct CarsView_Previews: PreviewProvider {
static var previews: some View {
CarsView(coreDataViewModel: CoreDataViewModel(coreDataManager: .preview))
}
}
您已经在 preview
中创建了一个 Car 对象,所以为什么不在循环中执行此操作,这里我已将其移动到一个单独的函数中,可以从 preview
声明中调用或单独
#if DEBUG
extension CoreDataManager {
func createMockCars() {
for i in 1...5 {
let car = Car(context: self.context)
car.make = "Make \(i)"
car.model = "Model \(i)"
}
try! self.context.save()
}
}
#endif
如其中一条评论所述,您应该考虑将视图模型重命名为与 CarsViewModel 或 CarListViewModel 等汽车有关的名称。