Home > Mobile >  Save Array of Object in Core Data
Save Array of Object in Core Data

Time:06-01

Fairly new to IOS development. Working on a Recipe keeper app. I have a struct for recipe that holds things like the name, ingredients, instructions. Then I have a class RecipeStore with a published array of Recipes. I am trying to save an instance of RecipeStore in core data so the recipes are saved even when the app is force quit. I have never used Core data and I have done a lot of research but I just don't quite understand it. I tried using userdefaults but it seems that only works with like strings, ints, bools, etc???? Also struggling with some of the core data tutorials I tried following because some of my attributes are arrays. Also if there is a way for me to use userdefaults, please let me know because core data scares me! Any help would be appreciated. Thank you


struct Recipe : Codable, Identifiable {
    var id: String
    var name: String
    var ingredients: [String]
    var instructions: [String]
    var category: String
    var imageName: String
}
class RecipeStore : ObservableObject {
    @Published var recipes: [Recipe]
    init (recipes: [Recipe] = []) {
        self.recipes = recipes

    }
}

CodePudding user response:

My suggestion (inspired by Joakim Danielson) is to save the string arrays as JSON in Core Data. The benefit is that the string can be filtered by predicates.

Declare ingredients and instructions as computed properties to convert the arrays to JSON and vice versa and declare the JSON properties as String

class CDRecipe : NSManagedObject {

    @NSManaged var id: String
    @NSManaged var name: String
    @NSManaged var category: String
    @NSManaged var imageName: String
    @NSManaged var ingredientJSON: String
    @NSManaged var instructionJSON: String
    
    var ingredients : [String] {
        get { decodeArray(from: \.ingredientJSON) }
        set { ingredientJSON = encodeArray(newValue) }
    }
    
    var instructions : [String] {
        get { decodeArray(from: \.instructionJSON) }
        set { instructionJSON = encodeArray(newValue) }
    }
    
    private func decodeArray(from keyPath: KeyPath<CDRecipe,String>) -> [String] {
        return (try? JSONDecoder().decode([String].self, from: Data(self[keyPath: keyPath].utf8))) ?? []
    }
    
    private func encodeArray(_ array: [String]) -> String {
        guard let data = try? JSONEncoder().encode(array) else { return "" }
        return String(data: data, encoding: .utf8)!
    }

}

CodePudding user response:

Friend please refer to this answer enter image description here

after select manual/none enter image description here

then create NSManagedCustomClass and add it bellow project bellow any class enter image description here in you extension add two arrays following:

import Foundation
import CoreData


extension CDRecipeStore {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<CDRecipeStore> {
        return NSFetchRequest<CDRecipeStore>(entityName: "CDRecipeStore")
    }

    @NSManaged public var id: String?
    @NSManaged public var name: String?
    @NSManaged public var category: String?
    @NSManaged public var imageName: String?

}

extension CDRecipeStore : Identifiable {

//    return saved Ingredient and instructions Array 1, you can save ingrediant with id at any place in your code

    var ingredients: [String] {
        guard let id = self.id else {return []}
        let savedIngredients = UserDefaults.standard.array(forKey: "\(id   "ing")") as? [String]
        return savedIngredients ?? []
    }
    
    var instructions: [String] {
        guard let id = self.id else {return []}
        let savedInstructions = UserDefaults.standard.array(forKey: "\(id   "inst")") as? [String]
        return savedInstructions ?? []
    }
}

After that when you fetch data from web or firebase you can save it core data by simple save and also save the ingredients and instructions with key id of that object like your fetching function you can call this function it will save ingredients and instruction to UserDefaults:

 func saveCoreData(reciepes:[Recipe]) {
    for reciepe in reciepes {
        let cdRecipe = CDRecipeStore(context: moc)
        cdRecipe.id = reciepe.id
        cdRecipe.category = reciepe.category
        cdRecipe.imageName = reciepe.imageName
        cdRecipe.name = reciepe.name
        UserDefaults.standard.set(reciepe.ingredients, forKey: "\(reciepe.id   "ing")")
        UserDefaults.standard.set(reciepe.instructions, forKey: "\(reciepe.id   "inst")")
        do {
            try? moc.save()
        }
    }
}

after this you can easily retrieve array from coredata custom class as you retrieve other string properties

CodePudding user response:

I see you shared some SwiftUI code and you are new to iOS. Core Data is an expert level framework and integrating it with SwiftUI is not fully understood yet. Instead, I would recommend going the document-based route which does have some native support inside SwiftUI. It would work well with your existing struct model. It's explained the video Build Document Based Apps in SwiftUI (WWDC 2021). And here is a sample project Building a Document-Based App with SwiftUI (Sample Code).

  • Related