Josch Hazard
Josch Hazard

Reputation: 343

Array of structs: How to save in coredata?

I´m trying to save an array of structs into coredata. I did a lot of research, but i cannot find the solution. Here´s what i´ve got:

import Cocoa
import CoreData

class ViewController: NSViewController {

    struct StudentsStruct {
        let firstName: String
        let lastName: String
        let age: Int
    }


    let Studentsdata: [StudentsStruct] = [StudentsStruct(firstName: "Albert", lastName: "Miller", age: 24), StudentsStruct(firstName: "Susan", lastName: "Gordon", age: 24), StudentsStruct(firstName: "Henry", lastName: "Colbert", age: 24)]


    override func viewDidLoad() {
        super.viewDidLoad()

        let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student


        for items in Studentsdata {
            student.firstName = StudentsStruct.firstName
            student.lastName = StudentsStruct.lastName
            student.age = StudentsStruct.age
        }

        DatabaseController.saveContext()
        let fetchRequest: NSFetchRequest<Student> = Student.fetchRequest()

        print (student)
    }
}

The DatabaseController is solution i´ve got from this tutorial: https://www.youtube.com/watch?v=da6W7wDh0Dw It´s not so important, it´s just making the "getContext" function. Whats important, in teh commandline "student.firstName = StudentsStruct.firstName" i´m getting the error "instance member "firstName" cannot be used on type ViewController.StudentStruct. After trying and trying, i´m running out of ideas how to get the array of structs into coredata.

This is the DatabaseController file:

import Foundation
import  CoreData


class DatabaseController  {

    private init() {
    }
    class func getContext() -> NSManagedObjectContext {
        return DatabaseController.persistentContainer.viewContext
    }

    // MARK: - Core Data stack

    static var persistentContainer: NSPersistentContainer = {
                let container = NSPersistentContainer(name: "StudentCoreFile")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error {
                               fatalError("Unresolved error \(error)")
            }
        })
        return container
    }()

    class func saveContext () {
        let context = DatabaseController.persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.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)")
            }
        }
    }

}

for any help thanks in advance!

Ok, you are right, i forgot to execute the fetchrequest. Here´s my current code:

import Cocoa
import CoreData

class ViewController: NSViewController {

    struct StudentsStruct {
        let firstName: String
        let lastName: String
        let age: Int
    }


    let Studentsdata: [StudentsStruct] = [StudentsStruct(firstName: "Albert", lastName: "Miller", age: 24), StudentsStruct(firstName: "Susan", lastName: "Gordon", age: 24), StudentsStruct(firstName: "Henry", lastName: "Colbert", age: 24)]


    override func viewDidLoad() {
        super.viewDidLoad()

        let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student


        for item in Studentsdata {
            let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
            student.firstName = item.firstName
            student.lastName = item.lastName
            student.age = Int16(item.age)
        }
        DatabaseController.saveContext()
        let fetchRequest: NSFetchRequest<Student> = Student.fetchRequest()

        do {
            let searchResults = try DatabaseController.getContext().fetch(fetchRequest)
            print("number of results: \(searchResults.count)")
            for result in searchResults as [Student] {
                print(student)
            }

        } catch {

            print ("error")

        }

    }
}

It´s running without errors. Now i´m getting 32 search results. Every entry is: age = 0; firstName = nil; lastName = nil;

For comparison, this code, without the loop is working:

import Cocoa
import CoreData

class ViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student

        student.firstName = "henry"
        student.lastName = "miller"
        student.age = 22

        DatabaseController.saveContext()
        let fetchRequest: NSFetchRequest<Student> = Student.fetchRequest()

        do {
            let searchResults = try DatabaseController.getContext().fetch(fetchRequest)
            print("number of results: \(searchResults.count)")
            for result in searchResults as [Student] {
                print(student)
            }
        } catch {

            print ("error")
        }

    }

}

Upvotes: 2

Views: 4204

Answers (3)

Michael Wells
Michael Wells

Reputation: 596

Perhaps this is lazy. You could also just encode your array as a json object and then create a field on your NSManagedObject for it as a transformable. When you want to retrieve you'd just decode and downcast to the proper type. That's what I did in one of my projects; worked fine.

Upvotes: 0

Josch Hazard
Josch Hazard

Reputation: 343

In case someone is interested, I found the solution: You first have to set up the struct in the CoredataEntity Class like that:

import Foundation
import CoreData

struct StudentsStruct {
    let firstName: String
    let lastName: String
    let age: Int
}

@objc(Student)
public class Student: NSManagedObject {

    @NSManaged public var firstName: String?
    @NSManaged public var lastName: String?
    @NSManaged public var age: Int16


    var allAtributes : StudentsStruct {
        get {
            return StudentsStruct(firstName: self.firstName!, lastName: self.lastName!, age: Int(self.age))
        }
        set {
            self.firstName = newValue.firstName
            self.lastName = newValue.lastName
            self.age = Int16(newValue.age)
        }
    }

}

Then use the same struct to paste the data:

import Cocoa
import CoreData

class ViewController: NSViewController {

    let studentsdata: [StudentsStruct] = [StudentsStruct(firstName: "Albert", lastName: "Miller", age: 24), StudentsStruct(firstName: "Susan", lastName: "Gordon", age: 24), StudentsStruct(firstName: "Henry", lastName: "Colbert", age: 24)]

    override func viewDidLoad() {
        super.viewDidLoad()

        for items in studentsdata {
            let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student

                       student.allAtributes = items
        }

        DatabaseController.saveContext()

        let fetchRequest: NSFetchRequest<Student> = Student.fetchRequest()

        do {
            let searchResults = try DatabaseController.getContext().fetch(fetchRequest)
            print("number of results: \(searchResults.count)")
            for result in searchResults as [Student] {
                print("student: \(firstName), \(lastName), \(age)" )
            }

        } catch {

            print ("error: \(error)")

        }

    }

}

Thats it.

Upvotes: 3

Nirav D
Nirav D

Reputation: 72460

You need to access the item in your for loop also you are currently accessing the same object Student object in for loop instead of that you need to create a new Student in every iteration of for loop.

for item in Studentsdata {
    //Create new student in for loop
    let student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
    //To get firstName, lastName and age access the item
    student.firstName = item.firstName
    student.lastName = item.lastName
    student.age = item.age
}
//Save context now
DatabaseController.saveContext()

Upvotes: 3

Related Questions