9

I am trying to save an array of 6 strings to CoreData, like this:

     let imageUrls = ["image-url....", "image-url....", ..., "image-url"]

I have tried all methods stated in How to save Array to CoreData? and in Best practice? - Array/Dictionary as a Core Data Entity Attribute without success.

This is the method I am trying to do.

Inside my .xcdatamodeld, I have declared: imageUrl as a Transformable, see picture in link (couldn't attach it to the post, I'm sorry for that!): https://drive.google.com/file/d/1VJey55oD9KhOy1KDy59h8PweMMQnaK2-/view?usp=sharing

Inside my NSManagedObject class I have

    @objc(Entity)
    public class Entity: NSManagedObject {
    @NSManaged public var imageUrls: [String]?
    ....

This is how I create the entity before saving it to CoreData:

    entity.imageUrls = ["test", "test", "test"]

When I try to fetch the imageUrls, the fetched entity.imageUrls is equal to nil. I don't understand this. The expected results is the same array I saved to CoreData.

ANSWERED by Joakim Danielson

Casper Lindberg
  • 600
  • 1
  • 6
  • 21

7 Answers7

12

My suggestion is to encode/decode the array to JSON and use a computed property. From the Swift perspective this is probably more efficient than a transformable attribute which bridges the array to the Objective-C runtime and uses heavier NSCoding.

@NSManaged public var imageUrls: String

var urls : [String] {
    get {
        let data = Data(imageUrls.utf8)
        return (try? JSONDecoder().decode([String].self, from: data)) ?? []
    }
    set {
        guard let data = try? JSONEncoder().encode(newValue),
            let string = String(data: data, encoding: .utf8) else { imageUrls = "" }
        imageUrls = string
    }
}

Another benefit is that a JSON string is searchable

vadian
  • 274,689
  • 30
  • 353
  • 361
5

check the attachement below for declration in ".xcdatamodelId" file. After that declare below line in CoreDataProperties.swift file

@NSManaged public var thumbnail: [String]?

Now, we can initialised string array into thumnail variable.Declaration variable in xcdatamodelID file

Pawan Kumar
  • 508
  • 3
  • 19
1

You will have to save it to the core data as String and split the array with a seperator. The thing is that you have to be sure that the string will no include that seperator.

For example if you used the seperator ", " on saving your array to the core data you will have to save it like

coreData.arrayToStringValue = array.joined(separator: ", ")

and when you read your data you will have to read it like:

let arrayFromStringValue:[String] = coreDataValue.split(separator: ", ")
Vasilis D.
  • 1,416
  • 13
  • 21
  • 2
    Hi, thanks for your answer! That's a good idea, but it's a workaround. I might go for this solution, but I'm interested why mine doesn't work too... – Casper Lindberg Aug 29 '19 at 06:18
1

Change type of imageUrl to BinaryData, then convert your list to data and save.

let urls = NSKeyedArchiver.archivedData(withRootObject: imageUrls)
manageObject.setValue(urls, forKey: "imageUrls")
Ruben Nahatakyan
  • 372
  • 3
  • 13
1

Swift 5

I found vadian's answer very helpful. I Used the same idea, But in a different way. Giving my code hoping that it may help.

  • Select the Type of the attribute that you want to store the array as String

enter image description here

// save to core data

var quizManagedObject : [NSManagedObject] = []

func save(question: String, answers: [String]) {

    guard let data = try? JSONEncoder().encode(answers),
    let answersEncodedString = String(data: data, encoding: .utf8) else { return }

    guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
    let managedContext = appDelegate.persistentContainer.viewContext

    let entity = NSEntityDescription.entity(forEntityName: "Quiz", in: managedContext)!
    let quiz = NSManagedObject(entity: entity, insertInto: managedContext)

    quiz.setValue(question, forKeyPath: "question")
    quiz.setValue(answersEncodedString, forKeyPath: "answers")

    do {
        quizManagedObject.append(quiz)
        try managedContext.save()
    } catch let error as NSError {
        print("Could not save. \(error), \(error.userInfo)")
    }
}

// fetch from core data

func fetchCoreData() {

    guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
    let managedContext = appDelegate.persistentContainer.viewContext
    let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Quiz")

    do {
        quizManagedObject = try managedContext.fetch(fetchRequest)

        for i in quizManagedObject {
            if let decodedAnswerString = i.value(forKey: "answers") as? String {
                let data = Data(decodedAnswerString.utf8)
                let answerArray = try? JSONDecoder().decode([String].self, from: data)
                let question = i.value(forKey: "question") as! String

                print("Q : ", question)
                print("A : ", answerArray ?? [""])
            }
        }
    } catch let error as NSError {
        print("Could not fetch. \(error), \(error.userInfo)")
    }
}
Naval Hasan
  • 1,226
  • 1
  • 13
  • 18
0

If your object is transformable without any custom class you might either want to cast it to [NSObject] and save it like that as it's the Transformable base-type. But you probably should make it [String] as the base class.

There are also no indication of you ever saving the data to core data? What you have displayed is just assigning a variable in a model. Which you really never should do as they should be immutable and not mutable.

Vollan
  • 1,887
  • 11
  • 26
-6

The following are the types of data, that we can store.

enter image description here

You cannot store array data. You need to convert into comma-separated strings. Then try to save it into coredata. And while you are fetching, you can convert it into array.

Vinu Jacob
  • 393
  • 2
  • 15