How to save Array to CoreData?
Asked Answered
I

6

140

I need to save my array to Core Data.

let array = [8, 17.7, 18, 21, 0, 0, 34]

The values inside that array, and the number of values are variable.

1. What do I declare inside my NSManagedObject class?

class PBOStatistics: NSManagedObject, Equatable {
    @NSManaged var date: NSDate
    @NSManaged var average: NSNumber
    @NSManaged var historicAverage: NSNumber
    @NSManaged var total: NSNumber
    @NSManaged var historicTotal: NSNumber
    @NSManaged var ordersCount: NSNumber
    @NSManaged var historicOrdersCount: NSNumber
    @NSManaged var values: [Double]  //is it ok?

    @NSManaged var location: PBOLocation

}

2. What do I declare inside my .xcdatamodel?

enter image description here

3. How do I save this in my Entity? (I use MagicalRecord)

let statistics = (PBOStatistics.MR_createInContext(context) as! PBOStatistics)
statistics.values = [8, 17.7, 18, 21, 0, 0, 34] //is it enough?
Idea answered 23/4, 2015 at 13:53 Comment(4)
There's no "should", the DB design is up to you, e.g. as far as I'm concerned you might as well use dates or text format if this is proves to be the most efficient way to store this data at your application.Reilly
So inside my NSManagedObject: @NSManaged var values: [Double] is it good? Can you tell me what type should I use in .xcdatamodel to save this?Baalbeer
I might not've made it clear enough, the way you ask it is about personal preference, there's no real problem to solve. If you are looking for any way to use one-to-many relations, add the information of what you've tried and where you've had an issue. If you understand each of the solutions mentioned by you and are looking for the most efficient one - list your criteria of efficiency and describe the use cases. If for some reason you are having troubles understanding different kinds of relations or don't want to use relations at all - say it directly.Reilly
Now that is a decent question, I've added a MagicalRecord tag for you, unfortunately I'm not experienced at this area and hopefully someone who is will be able to help you better from this point.Reilly
I
223

Ok, I made some research and testing. Using Transformable type, solution is simple:

1. What do I declare inside my NSManagedObject class?

@NSManaged var values: [NSNumber]  //[Double] also works

2. What do I declare inside my .xcdatamodel?

Transformable data type.

3. How do I save this in my Entity?

statistics!.values = [23, 45, 567.8, 123, 0, 0] //just this

“You can store an NSArray or an NSDictionary as a transformable attribute. This will use the NSCoding to serialize the array or dictionary to an NSData attribute (and appropriately deserialize it upon access)” - Source

Or If you want to declare it as Binary Data then read this simple article:

Idea answered 23/4, 2015 at 15:10 Comment(6)
Also works with [NSString] for an array of stringsGannet
Is it possible to add predicate while fetching from core data?Saied
In my case I'm trying to use an array of Strings and it doesn't workRyannryazan
how would you do this in swift 3 way?Trout
Looks like this won't work for future releases of Swift: #62590485Mithgarthr
for array of Strings, set data type to Transformable and also set it's Transformer type to 'NSSecureUnarchiveFromDataTransformer' in the Attribute panel of that specific attribute.Eastward
M
148

Swift 3 As we don't have the implementation files anymore as of Swift 3, what we have to do is going to the xcdatamodeld file, select the entity and the desired attribute (in this example it is called values). Set it as transformable and its custom class to [Double]. Now use it as a normal array.

Setting custom class to array of Double

Maladjustment answered 1/11, 2017 at 17:4 Comment(6)
Is it possible to define custom class as [[String: Any]] ? An array of dictionariesUrbannal
Theoretically it should be acceptable, however I've never tried.Maladjustment
You can always set codegen to Manual/None and write your own Core Data model class.Impulsion
nice approach, TILPalaeogene
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 releaseTopee
how would this work in regards to Int array? attribute types has Int16, Int32, Int64Casandra
V
16

Convert Array to NSData

let appDelegate =
    UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entity =  NSEntityDescription.entityForName("Device",
                                                inManagedObjectContext:managedContext)
let device = NSManagedObject(entity: entity!,
                             insertIntoManagedObjectContext: managedContext)
let data = NSKeyedArchiver.archivedDataWithRootObject(Array)

device.setValue(data, forKey: "dataOfArray")
do {
    try managedContext.save()
    devices.append(device)
} catch let error as NSError  {
    print("Could not save \(error), \(error.userInfo)")
}

Select Binary Data

Convert NSData to Array

let appDelegate =
    UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Device")

do {
    let results =
        try managedContext.executeFetchRequest(fetchRequest)

    if results.count != 0 {

        for result in results {

                let data = result.valueForKey("dataOfArray") as! NSData
                let unarchiveObject = NSKeyedUnarchiver.unarchiveObjectWithData(data)
                let arrayObject = unarchiveObject as AnyObject! as! [[String: String]]
                Array = arrayObject
        }

    }

} catch let error as NSError {
    print("Could not fetch \(error), \(error.userInfo)")
}

For Example : https://github.com/kkvinokk/Event-Tracker

Vtarj answered 18/10, 2016 at 7:4 Comment(2)
Using NSData indeed makes more sense and simpler.Herodotus
If you want to add a predicate to that attribute, it may not work.Saied
E
13

If keeping it simple and store an array as a string

Try this:

// Array of Strings
let array: [String] = ["red", "green", "blue"]
let arrayAsString: String = array.description
let stringAsData = arrayAsString.data(using: String.Encoding.utf16)
let arrayBack: [String] = try! JSONDecoder().decode([String].self, from: stringAsData!)

For other data types respectively:

// Set of Doubles
let set: Set<Double> = [1, 2.0, 3]
let setAsString: String = set.description
let setStringAsData = setAsString.data(using: String.Encoding.utf16)
let setBack: Set<Double> = try! JSONDecoder().decode(Set<Double>.self, from: setStringAsData!)
Edible answered 24/5, 2018 at 17:30 Comment(4)
Your solution saved my time lotSaurischian
I'm not sure why, but none of these solutions are working for me. I tried your solution of storing the data of the string array in Core Data and retrieving it, then decoding it to a string array afterwards, but it's still not working. Any advice?Quark
This is a fairly crude way of doing it -- the data won't be indexable in the DB because its being stored as a BLOB. There's also quite a bit of overhead in reading / writing data -- you can't use any of the collection's methods to easily make changes, for instance. Using a transformable is the preferred way.Vhf
Nice! I would also add the following as a possibility, that could work in some cases! var array = ["red", "green", "blue"] var arrayString = array.joined(separator: "%20") var arrayBack = arrayString.components(separatedBy: "%20")Dahlberg
P
4

Make entity attribute type as "Binary Data"

NSData *arrayData = [NSKeyedArchiver archivedDataWithRootObject:TheArray];
myEntity.arrayProperty = arrayData;
[self saveContext]; //Self if we are in the model class

Retrive original array as:

NSMutableArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:anEntity.arrayProperty];

That's all.

Pustule answered 16/7, 2018 at 15:28 Comment(0)
W
2

Following code works for me to store array of JSON in CoreData

func saveLocation(model: [HomeModel],id: String){

    let newUser = NSEntityDescription.insertNewObject(forEntityName: "HomeLocationModel", into: context)

    do{
        var dictArray = [[String: Any]]()
        for i in 0..<model.count{
            let dict = model[i].dictionaryRepresentation()
            dictArray.append(dict)
        }
        let data = NSKeyedArchiver.archivedData(withRootObject: dictArray)
        newUser.setValue(data, forKey: "locations")
        newUser.setValue(id, forKey: "id")
        try context.save()
    }catch {
       print("failure")
    }

}
Worship answered 22/5, 2019 at 8:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.