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
There are two steps to storing an array of a custom struct or class in Core Data. The first step is to create a Core Data entity for your custom struct or class. The second step is to add a to-many relationship in the Core Data entity where you want to store the array.
Right-click on your project's folder in the project navigator and then New File… In the new window, type “data” in the top right corner, select Data Model, and press Next. Give it a name, and save it. Now, let's add all the necessary code to connect Core Data with our project.
The next time you need to store data, you should have a better idea of your options. Core Data is unnecessary for random pieces of unrelated data, but it's a perfect fit for a large, relational data set. The defaults system is ideal for small, random pieces of unrelated data, such as settings or the user's preferences.
Open Xcode and create a new iOS project based on the Single View App template. Name the app HitList and make sure Use Core Data is checked. Checking the Use Core Data box will cause Xcode to generate boilerplate code for what's known as an NSPersistentContainer in AppDelegate.
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
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.
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: ", ")
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")
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.
// 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)")
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With