11

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

5
  • 1
    Did you set the custom class to [String] in your model for the attribute? Commented Aug 29, 2019 at 6:14
  • Yes I did, forgot to mention that! Commented Aug 29, 2019 at 6:17
  • Maybe you need to clean and rebuild your project. I just created a simple test project and it worked fine using Transformable and setting a custom class Commented Aug 29, 2019 at 6:50
  • I forgot that I had to clean CoreData of all the nil objects.... It worked. Thanks a lot! Commented Aug 29, 2019 at 9:47
  • Hey, @CoderOgden I'm having this exact problem. I tried cleaning the build folder and rebuilding but still the same result. Could you explain it a bit further how you fixed this? Commented May 20, 2021 at 11:50

7 Answers 7

13

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

Sign up to request clarification or add additional context in comments.

2 Comments

The searchability is a good point. Thanks for the perspective.
The Transformable is also searchable. So, this argument is invalid.
9

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

4 Comments

Why Array<Any> and not Array<String>?
we can use Array<String> .It will also work.I just take Array<Any> so I don't need to update model everytime.
For my case, I have to use non-optional type
Also, set NSSecureUnarchiveFromData to Value Transformer (it was renamed to just "Transformer"). Otherwise, you may get this runtime warning: 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
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: ", ")

1 Comment

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

Comments

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

Comments

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.

Comments

-7

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.