SwiftUI 2.0 / CoreData 2021:没有错误但实体未保存

Posted

技术标签:

【中文标题】SwiftUI 2.0 / CoreData 2021:没有错误但实体未保存【英文标题】:SwiftUI 2.0 / CoreData 2021: No error but entity not saved 【发布时间】:2021-02-24 09:37:02 【问题描述】:

我有一个 SwiftUI 2.0 应用程序,它使用 CoreData 在本地存储数据,虽然当我尝试在版本视图中将数据保存到 CoreData 时,preview 模式可以将数据写入内存或不写入内存(我测试了两者),但我没有收到错误消息,但数据没有写入模拟器的 sqlite 数据文件......我做错了什么?

我有 2 个实体处于一对多关系中:

发帖

日期:日期 id:UUID 文本:字符串 时间戳:日期 图片:toMany 关系 / 目的地 PostPic / 反向帖子

PostPic

数据:BinaryData / 允许外部存储 post:toOne 关系 / 目的地 Post / 反向图片

我已提取以下主要文件:

MyApp.swift

import SwiftUI

@main
struct MyApp: App 
    let persistenceController = PersistenceController.shared

    var body: some Scene 
        WindowGroup 
            MyNavigationView().environment(\.managedObjectContext, persistenceController.container.viewContext)
        
    

Persistence.swift

import Foundation
import SwiftUI
import CoreData
import CloudKit

struct PersistenceController 
    static let shared = PersistenceController()

    static var preview: PersistenceController = 
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext
        for day in 1...10 
            let newPost = Post(context: viewContext)
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "yyyy/MM/dd"
            newPost.date = dateFormatter.date(from: "2021/02/\(String(format: "%02d", day))")!
            newPost.id = UUID()
            newPost.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
            newpost.timestamp = Date()
            for index in 1...7 
                let newPostPic = PostPic(context: viewContext)
                if let image = UIImage(named:newPostPic.filename!) 
                    newPostPic.data = image.pngData()
                
                newPost.addToFiles(newPostPic)
            
        
        do 
            try viewContext.save()
         catch 
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        
        return result
    ()

    let container: NSPersistentCloudKitContainer
    private static var _model: NSManagedObjectModel?
    private static func model(name: String) throws -> NSManagedObjectModel 
        if _model == nil 
            _model = try loadModel(name: name, bundle: Bundle.main)
        
        return _model!
    
    private static func loadModel(name: String, bundle: Bundle) throws -> NSManagedObjectModel 
        guard let modelURL = bundle.url(forResource: name, withExtension: "momd") else 
            throw CoreDataError.modelURLNotFound(forResourceName: name)
        

        guard let model = NSManagedObjectModel(contentsOf: modelURL) else 
            throw CoreDataError.modelLoadingFailed(forURL: modelURL)
       
        return model
    

    enum CoreDataError: Error 
        case modelURLNotFound(forResourceName: String)
        case modelLoadingFailed(forURL: URL)
    

    init(inMemory: Bool = false) 
     
        container = NSPersistentCloudKitContainer(name: "curum")
        if inMemory 
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        
        container.loadPersistentStores(completionHandler:  (storeDescription, error) in
            if let error = error as NSError? 
                fatalError("Unresolved error \(error), \(error.userInfo)")
            
        )
    

MyNavigationView.swift

struct MyNavigationView: View 
    @Environment(\.managedObjectContext) private var viewContext
    var body: some View 
        NavigationView 
            TabView  // tab bar at the bottom of the screen
                HomeView()
                    .tabItem
                        Image(systemName: "house.fill")
                    .tag(0)
            
            .navigationBarTitleDisplayMode(.inline)
            .navigationTitle("MyApp")
        
    


struct MyNavigationView_Previews: PreviewProvider 
    static var previews: some View 
        MyNavigationView()
    


HomeView.swift

//
//  HomeView.swift
//  curum
//
//  Created by loic on 2021/02/19.
//

import SwiftUI

struct HomeView: View 
    var body: some View 
        ZStack 
            // ...
            VStack  // the plus button
                Spacer()
                HStack 
                    Spacer()
                    NavigationLink(destination: EditionView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)) 
                        ZStack 
                            Circle()
                                .frame(width: 80, height: 80)
                            Image(systemName: "plus")
                                .renderingMode(.template)
                                .font(.system(size: 60, weight: .light))
                        
                    
                    .padding(.trailing, 16)
                    .padding(.bottom, 16)
                
            
        

    


struct HomeView_Previews: PreviewProvider 
    static var previews: some View 
        HomeView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    

EditionView.swift

import Foundation
import SwiftUI
import CoreData

struct EditionView: View 
    @Environment(\.managedObjectContext) private var viewContext
    @Environment(\.presentationMode) var mode: Binding<PresentationMode>
    @State private var postText: String = ""
    @State private var postImage: Image = Image("image1")
    @State private var showingImagePicker = false
    @State private var inputImage: UIImage?
    
    var body: some View 
        VStack 
                TextEditor(text: $postText)
                    .lineSpacing(10)
                    .border(Color.gray)
                    .padding(.all)
            HStack 
                Button(action: 
                    self.showingImagePicker = true
                ) 
                    
                    if (inputImage == nil) 
                        Text("Pick Image")
                     else 
                        postImage
                            .resizable()
                            .renderingMode(.original)
                            .frame(width: 100, height: 100)
                    
                .sheet(isPresented: $showingImagePicker, onDismiss: loadImage) 
                    ImagePicker(image: self.$inputImage)
                
            
        
        .navigationBarBackButtonHidden(true)
        .navigationBarItems(leading: Button(action : 
            self.mode.wrappedValue.dismiss()
        )
            Image(systemName: "xmark")
        ,
        trailing: Button 
                let newPost = Post(use: viewContext)
                newPost.date = Date()
                newPost.id = UUID()
                newPost.text = entryText
                newPost.timestamp = Date()
                let newPostPic = PostPic(use: viewContext)
                newPostPic.data = inputImage!.pngData()
                newPost.addToFiles(newPostPic)
                do 
                    try viewContext.save()
                    mode.wrappedValue.dismiss()
                 catch 
                    let nsError = error as NSError
                    fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
                
             label: 
                Image(systemName: "checkmark.circle.fill")
            )
    
    func loadImage() 
        guard let inputImage = inputImage else  return 
        postImage = Image(uiImage: inputImage)
    


struct EditionView_Previews: PreviewProvider 
    static var previews: some View 
        EditionView()
    


struct ImagePicker: UIViewControllerRepresentable 
    @Environment(\.presentationMode) var presentationMode
    @Binding var image: UIImage?

    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate 
        let parent: ImagePicker

        init(_ parent: ImagePicker) 
            self.parent = parent
        

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) 
            if let uiImage = info[.originalImage] as? UIImage 
                parent.image = uiImage
            

            parent.presentationMode.wrappedValue.dismiss()
        
    

    func makeCoordinator() -> Coordinator 
        Coordinator(self)
    

    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController 
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        return picker
    

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) 

    

** 编辑以回应@Tushar Sharma** 我忘了补充一下,我在NSManagedObject 中添加了以下扩展名,以消除Multiple NSEntityDescriptions Claim NSManagedObject Subclass 中的错误:

import Foundation
import CoreData

public extension NSManagedObject 
    convenience init(use context: NSManagedObjectContext) 
        let name = String(describing: type(of: self))
        let entity = NSEntityDescription.entity(forEntityName: name, in: context)!
        self.init(entity: entity, insertInto: context)
    

【问题讨论】:

不确定实体的初始化器是否应该看起来像“let newPost = Post(context: viewContext)?” 我遇到了一个错误,我通过在***.com/questions/51851485/… 中向 NSManagedObject 添加扩展名解决了这个错误,我在问题的末尾添加了信息 【参考方案1】:

抱歉,这是 HomeView.swift 文件中的简单复制粘贴问题:

在下一行,我使用了 preview 持久化上下文 ...

NavigationLink(destination: EditionView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)) 

...而不是使用共享

NavigationLink(destination: EditionView().environment(\.managedObjectContext, PersistenceController.shared.container.viewContext)) 

这解决了我的问题!

【讨论】:

以上是关于SwiftUI 2.0 / CoreData 2021:没有错误但实体未保存的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 2.0列表项层级缩进显示CoreData托管数据的3种实现

Xcode 12.2+生成SwiftUI 2.0 CoreData模板预览时崩溃问题的解决

Xcode 12.2+生成SwiftUI 2.0 CoreData模板预览时崩溃问题的解决

遍历 NSSet - SwiftUI 2.0

使用 SwiftUI 2.0 和 Core Data 预填充数据

SwiftUI CoreData - 如何更新获取请求和列表