skip to Main Content

Creating an example for a struct is very easy and straightforward. For example,

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.")
}

Now, how can I create an example if I made this an entity in core data? I can’t just put let example = CachedUser(id: UUID(), ...) like I did with the struct. I want this example to be part of my core data automatically without having to manually create it by using forms, buttons, etc… Thanks in advance!

Core Data Entity

2

Answers


  1. You can simply check if your default user exists in database. If it does not then you need to create one and save it. Something like the following would work if you have synchronous operations:

    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
            }
        }()
        
    }
    

    This will lazily return existing or generate a new user for you. This user will then be persistent in your database.

    The code will only be executed once per session, first time you call CachedUser.example.

    If you have your database setup asynchronous then with closures it should look something like this:

    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()
                    }
                }
            }
        }
    

    But in this case it makes sense to warmup your application before you show screens that require this user to be present. You can for instance have a loading screen when your app first starts and continue to next screen once this method has finished…

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

    In both cases you now hold a static property to your example entry such as User.example which can be used anywhere.

    But in both cases you may stumble to issue if user (if able to) deletes this entry from database. You would need to handle that case. Either prevent that action or create a new example user once the old one is deleted.

    Login or Signup to reply.
  2. To access this manager put

    let mgr = CachedUserPersistenceManager()
    

    In a ViewModel or a View

    /// 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)
        }
    }
    

    The generic classes that are a part of this code are below. You don’t need them per say it just makes some of the code reusable through the app.

    //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 []
            }
            
        }
        
    }
    

    The previewAware variable that is mentioned goes with the Apple standard code in the PersistenceController

    It automatically give you the preview container so you don’t have to worry about adapting your code for samples in Canvas. Just add the below code to the PersistenceController

    static var previewAware : PersistenceController{
        if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
            return PersistenceController.preview
        }else{
            return PersistenceController.shared
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search