【问题标题】:Creating example of Core Data entity创建核心数据实体的示例
【发布时间】:2026-02-02 08:40:01
【问题描述】:

为结构创建示例非常简单直接。例如,

import Foundation

struct User: Identifiable, Codable {
    let id: UUID
    let isActive: Bool
    let name: String
    let age: Int
    let company: String

    static let example = User(id: UUID(), isActive: true, name: "Rick Owens", age: 35, company: "Rick Owens Inc.")
}

现在,如果我将其作为核心数据中的实体,我该如何创建示例?我不能像对结构体所做的那样只输入let example = CachedUser(id: UUID(), ...)。我希望这个示例自动成为我的核心数据的一部分,而无需使用表单、按钮等手动创建它...提前致谢!

【问题讨论】:

  • 你不能在 CoreData 中做同样的事情。有几种方法可以克服这个问题,例如使用 preview 容器作为画布、默认值、使用预先传播示例对象的方法,但如果没有更多信息就很难提供帮助
  • this问题
  • @loremipsum 您还需要什么其他信息来提供帮助?我只是一般地问,如何才能做到这一点。我宁愿在整个项目中使用默认的 CachedUser。
  • 看附上的问题。最重要的是目的。如果您只需要它进行预览,那么只需使用 Apple 示例项目中提供的 preview 容器创建一个用户。如果您想一直使用样本数据预加载对象,请使用实体提供的awakeFromInsert 方法
  • 创建一个硬编码的 UUID 并将其定义为“示例 ID”(将其初始化为全零或其他)。然后当应用程序启动时检查数据库中是否存在此条目。如果存在,则返回它。否则创建一个新的,设置默认值和示例 ID,将其保存到数据库中并返回。

标签: ios swift swiftui


【解决方案1】:

您可以简单地检查您的默认用户是否存在于数据库中。如果没有,那么您需要创建一个并保存它。如果您有同步操作,则如下所示:

class CachedUser {
    
    static var example: CachedUser = {
        let exampleUUID = UUID(uuidString: "33041937-05b2-464a-98ad-3910cbe0d09e")!
        
        if let existingUser = Database.fetchUser(id: exampleUUID) {
            return existingUser
        } else {
            let newUser = CachedUser()
            // TODO: apply example values to user
            Database.saveUser(newUser)
            return newUser
        }
    }()
    
}

这将延迟返回现有用户或为您生成新用户。然后,此用户将永久保存在您的数据库中。

每个会话只执行一次代码,第一次调用CachedUser.example

如果你的数据库设置是异步的,那么使用闭包它应该看起来像这样:

class User {
    
    static private(set) var example: User!
    
    static func prepareExampleUser(_ completion: () -> Void) {
        let exampleUUID = UUID(uuidString: "33041937-05b2-464a-98ad-3910cbe0d09e")!
        
        Database.fetchUser(id: exampleUUID) { user in
            if let user = user {
                example = user
                completion()
            } else {
                let newUser = User()
                newUser.id = exampleUUID
                // TODO: apply example values to user
                Database.saveUser(newUser) {
                    example = newUser
                    completion()
                }
            }
        }
    }

但在这种情况下,在显示需要该用户在场的屏幕之前预热您的应用程序是有意义的。例如,您可以在您的应用首次启动时拥有一个加载屏幕,并在此方法完成后继续下一个屏幕...

// Loading screen enters
self.startLoading()
User.prepareExampleUser {
    self.navigateToNextScreen()
    self.stopLoading()
}

在这两种情况下,您现在都为示例条目保留了一个静态属性,例如 User.example,它可以在任何地方使用。

但是在这两种情况下,如果用户(如果能够)从数据库中删除此条目,您可能会遇到问题。你需要处理这种情况。要么阻止该操作,要么在旧的示例用户被删除后创建一个新的示例用户。

【讨论】:

    【解决方案2】:

    访问这个经理放

    let mgr = CachedUserPersistenceManager()
    

    在 ViewModel 或视图中

    /// Manager for the Item entity
    class CachedUserPersistenceManager: PersistenceManager<CachedUser>{
        let sampleUUID = UUID(uuidString: "00000000-0000-0000-0000-000000000000")!
    
        init(isTest: Bool = false) {
            super.init(entityType: CachedUser.self, isTest: isTest)
            //Preloads the user
            preloadSample()
        }
        ///Preloads a sample object to the context
        func preloadSample(){
            let list = retrieveObjects(sortDescriptors: nil, predicate: NSPredicate(format: "%K == %@", #keyPath(CachedUser.uuid), sampleUUID as CVarArg)
            )
            if list.isEmpty{
                let sampleItem = createObject()
                sampleItem.uuid = sampleUUID
                save()
            }
        }
        override func addSample() -> CachedUser {
            let new = super.addSample() as CachedUser
            //add any sample code
            return new
        }
        
        override func createObject() -> CachedUser {
            super.createObject()!
        }
        override func updateObject(object: CachedUser) -> Bool {
            //Replace the uuid if needed
            if object.uuid == sampleUUID{
                object.uuid = UUID()
            }
            return super.updateObject(object: object)
        }
    }
    

    作为此代码一部分的通用类如下所示。你不需要他们说它只是让一些代码可以通过应用程序重用。

    //Manager for any Entity
    class PersistenceManager<T : NSManagedObject>{
        let serviceSD: CoreDataPersistenceService<T>
        internal init(entityType: T.Type, isTest: Bool = false) {
            self.serviceSD = CoreDataPersistenceService(isTest: isTest, entityType: entityType)
        }
        
        //MARK: convenience
        func addSample() -> T {
            let newItem = createObject()
            return newItem!
        }
        
        //MARK: Persistence Service Methods
        func createObject() -> T? {
            let result = serviceSD.createObject()
            return result
        }
        
        func updateObject(object: T) -> Bool {
            return serviceSD.updateObject(object: object)
        }
        
        func deleteObject(object: T) -> Bool {
            return serviceSD.deleteObject(object: object)
        }
        func deleteAllObjects(entityName: String, isConfirmed: Bool) -> Bool {
            return serviceSD.deleteAllObjects(isConfirmed: isConfirmed)
        }
        func retrieveObjects(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?) -> [T]{
            return serviceSD.retrieveObjects(sortDescriptors: sortDescriptors, predicate: predicate)
        }
        func retrieveObject(id: String) -> T? {
            return serviceSD.retrieveObject(sortDescriptors: nil, id: id).first
        }
        func resetChanges() {
            serviceSD.resetChanges()
        }
        func save() {
            _ = serviceSD.save()
        }
    }
    //Service for Any Entity
    class CoreDataPersistenceService<T: NSManagedObject>: NSObject {
        var persistenceController: PersistenceController
        let entityType: T.Type
        
        required init(isTest: Bool = false, entityType: T.Type) {
            if isTest{
                self.persistenceController = PersistenceController.preview
            }else{
                self.persistenceController = PersistenceController.previewAware
            }
            self.entityType = entityType
            super.init()
        }
        //MARK: CRUD methods
        func createObject() -> T? {
            let result = entityType.init(context: persistenceController.container.viewContext)
            return result
        }
        
        func updateObject(object: T) -> Bool {
            var result = false
            result = save()
            return result
        }
        func deleteObject(object: T) -> Bool {
            var result = false
            persistenceController.container.viewContext.delete(object)
            result = save()
            return result
        }
        
        func deleteAllObjects(isConfirmed: Bool) -> Bool {
            var result = false
            //Locked in so only the Generic "Item" can be deleted like this
            if entityType == Item.self && isConfirmed == true{
                let deleteRequest = NSBatchDeleteRequest(fetchRequest: entityType.fetchRequest())
                do {
                    try persistenceController.container.persistentStoreCoordinator.execute(deleteRequest, with: persistenceController.container.viewContext)
                } catch {
                    print(error)
                    result = false
                }
            }
            return result
        }
        
        func resetChanges()  {
            persistenceController.container.viewContext.rollback()
            _ = save()
            
        }
        
        func save() -> Bool {
            var result = false
            do {
                if persistenceController.container.viewContext.hasChanges{
                    try persistenceController.container.viewContext.save()
                    result = true
                    
                }else{
                    result = false
                }
            } catch {
                print(error)
            }
            return result
        }
        func retrieveObject(sortDescriptors: [NSSortDescriptor]? = nil, id: String) -> [T]{
            return retrieveObjects(sortDescriptors: sortDescriptors, predicate: NSPredicate(format: "id == %@", id))
        }
        
        func retrieveObjects(sortDescriptors: [NSSortDescriptor]? = nil, predicate: NSPredicate? = nil) -> [T]
        {
            
            let request = entityType.fetchRequest()
            if let sortDescriptor = sortDescriptors
            {
                request.sortDescriptors = sortDescriptor
            }
            if let predicate = predicate
            {
                request.predicate = predicate
            }
            do
            {
                let results = try persistenceController.container.viewContext.fetch(request)
                return results as! [T]
            }
            catch
            {
                print(error)
                return []
            }
            
        }
        
    }
    

    提到的previewAware 变量与PersistenceController 中的Apple 标准代码一致

    它会自动为您提供preview 容器,因此您不必担心调整代码以适应 Canvas 中的示例。只需将以下代码添加到PersistenceController

    static var previewAware : PersistenceController{
        if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
            return PersistenceController.preview
        }else{
            return PersistenceController.shared
        }
    }
    

    【讨论】: