AlexK
AlexK

Reputation: 396

How to save existing objects to Core Data

I have been working on a project and have an existing object I am using. I am trying to find a simply way to save, retrieve and delete a list of these objects in Core Data.

Here is my object

import Foundation

class Book : Codable {

    var coverIndex:Int?
    var authorName:[String]?
    var title:String?
    var editionCount:Int?
    var firstPublishYear:Int?
    var key: String?
    var publishPlace:[String]?
    var publisher:[String]?

    public enum BookResponseCodingKeys: String, CodingKey {
        case coverIndex = "cover_i"
        case authorName = "author_name"
        case editionCount = "edition_count"
        case firstPublishYear = "first_publish_year"
        case key = "key"
        case title = "title"
        case publishPlace = "publish_place"
        case publisher = "publisher"
    }

    public required init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: BookResponseCodingKeys.self)

        self.coverIndex = try container.decodeIfPresent(Int.self, forKey: .coverIndex)
        self.authorName = try container.decodeIfPresent([String].self, forKey: .authorName)
        self.editionCount = try container.decodeIfPresent(Int.self, forKey: .editionCount)
        self.firstPublishYear = try container.decodeIfPresent(Int.self, forKey: .firstPublishYear)
        self.key = try container.decodeIfPresent(String.self, forKey: .key)
        self.title = try container.decodeIfPresent(String.self, forKey: .title)
        self.publishPlace = try container.decodeIfPresent([String].self, forKey: .publishPlace)
        self.publisher = try container.decodeIfPresent([String].self, forKey: .publisher)
    }
}

What is the most straightforward to save this in Core Data (or to map it to a Core Data model.)

Upvotes: 5

Views: 12137

Answers (2)

jake
jake

Reputation: 1264

In your xcdatamodeld define an entity, such as User:

enter image description here

Add an attribute with a Transformable type. Name it 'books'.

enter image description here

Next, set the Transformable attribute's class to an array of Book. In the Custom Class field below.

enter image description here

Use the following code to get the current array from your current context. These methods should go in some sort of DataManager class (which should be a singleton):

import CoreData

open class DataManager: NSObject {
    
    public static let sharedInstance = DataManager()
    
    private override init() {}

    // Helper func for getting the current context.
    private func getContext() -> NSManagedObjectContext? {
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return nil }
        return appDelegate.persistentContainer.viewContext
    }

    func retrieveUser() -> NSManagedObject? {
        guard let managedContext = getContext() else { return nil }
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "User")
        
        do {
            let result = try managedContext.fetch(fetchRequest) as! [NSManagedObject]
            if result.count > 0 {
                // Assuming there will only ever be one User in the app.
                return result[0]
            } else {
                return nil
            }
        } catch let error as NSError {
            print("Retrieving user failed. \(error): \(error.userInfo)")
           return nil
        }
    }

    func saveBook(_ book: Book) {
        print(NSStringFromClass(type(of: book)))
        guard let managedContext = getContext() else { return }
        guard let user = retrieveUser() else { return }
        
        var books: [Book] = []
        if let pastBooks = user.value(forKey: "books") as? [Book] {
            books += pastBooks
        }
        books.append(book)
        user.setValue(books, forKey: "books")
        
        do {
            print("Saving session...")
            try managedContext.save()
        } catch let error as NSError {
            print("Failed to save session data! \(error): \(error.userInfo)")
        }
    }

}

You'll also need a method to create a user (and likely delete, assuming we want to follow CRUD). First, you'll need to grab a reference to the User Entity to create one. This definition should be at the top of your DataManager class.

extension DataManager {
    private lazy var userEntity: NSEntityDescription = {
        let managedContext = getContext()
        return NSEntityDescription.entity(forEntityName: "User", in: managedContext!)!
    }()
}

And then implement this function to create one.

extension DataManager {
    /// Creates a new user with fresh starting data.
    func createUser() {
        guard let managedContext = getContext() else { return }
        let user = NSManagedObject(entity: userEntity, insertInto: managedContext)
        
        do {
            try managedContext.save()
        } catch let error as NSError {
            print("Failed to save new user! \(error): \(error.userInfo)")
        }
    }
}

Now just call:

DataManager.sharedInstance.createUser()

to create a new user. Then, to append books to the user's storage:

DataManager.sharedInstance.saveBook(book)

Upvotes: 11

Serj Kultenko
Serj Kultenko

Reputation: 169

It depends on how you want to work with your object. You could inherit your object from NSManagedObject or establish mapping from NSManagedObject consisting your object data to your class.

If you won't inherit your object from NSManagedObject, you could use code like this:

func save(bookObject: Book, in container: NSPersistentContainer) -> NSManagedObject {
        container?.performBackgroundTask { context in
            do {
                // Create NSManagedObject backed object
                let bookEntity = BookEntity(context: context)
                bookEntity.coverIndex = bookObject.coverIndex
                // And all the rest properties

                try context.save()
            } catch (let error) {
                print(error)
                return
            }

        }
    }

Upvotes: 0

Related Questions