2

I have two Entities in CoreData Model (Products and Images), there is a one-to-many relationship between products(one)-images(many). I have also subclassed my entities, and i am trying to populate the database from json file; however i cant seem to understand how to insert images into Images that are related to Products, so that when i pick that product in the app i get all the related images.

extension Products {
    @NSManaged var price: NSNumber?
    @NSManaged var stock: NSNumber?
    @NSManaged var desc: String?
    @NSManaged var name: String?
    @NSManaged var images: NSSet?
    @NSManaged var sales: NSSet?
}

extension Images {
    @NSManaged var image: String?
    @NSManaged var products: Products?
}

Then the json file data (which i dont have any problem serialising it with NSJSONObjectWithData:

{ "records" : [{ "name" : "Apple iMac 27in", "description" : "The stunning all-in-one iMac features a beautiful 27-inch Retina 5K widescreen display, quad-core Intel Core i5 processors and AMD Radeon R9 M380 graphics processor with 2GB of GDDR5 memory.", "image" : ["imac1.jpg", "imac2.jpg"], "stock" : 32, "price" : 1699.00 }, { "name" : "Apple iPhone 6s Plus 128 GB", "description" : "The moment you use iPhone 6s Plus, you know you’ve never felt anything like it. With just a single press, 3D Touch lets you do more than ever before. Live Photos bring your memories to life in a powerfully vivid way. And that’s just the beginning. Take a deeper look at iPhone 6s Plus, and you’ll find innovation on every level.", "image" : ["iphone6splus1.jpg", "iphone6splus2.jpg", "iphone6splus3.jpg", "iphone6splus4.jpg"], "stock" : 144, "price" : 1042.00 } ]}

this is the function which i use to populate the database in the AppDelegate just after checking that the database is empty.

func fillDatabase() {
    let jsonURL = NSBundle.mainBundle().URLForResource("products", withExtension: "json");
    let data = NSData(contentsOfURL: jsonURL!);
    do {
        let jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions(rawValue: 0)) as! Dictionary<String, AnyObject>;
        let recordset = jsonData["records"] as! Array<Dictionary<String, AnyObject>>;
        for record in recordset {
            let product = Products(context: managedObjectContext);

            if let name = record["name"] {
                product.name = name as? String;
            }

            if let stock = record["stock"] as? NSNumber {
                product.stock = stock;
            }

            if let price = record["price"] as? Double {
                product.price = price;
            }

            if let desc = record["description"] as? String {
                product.desc = desc;
            }

            if let images = record["image"] as? NSMutableArray {    
                // *********************************************************
                // Im getting the array of images, but dont know where to go from here.
            }

            do {
                try managedObjectContext.save();
            } catch let error as NSError {
                print(error.debugDescription);
            }
        }
    } catch let error as NSError {
        print("error parsing json object:  \(error.debugDescription)");
        return;
    }
}
Abizern
  • 146,289
  • 39
  • 203
  • 257
Maz
  • 245
  • 3
  • 11

3 Answers3

1

Given that you set up the relationships correctly in Core Data, you should be able to associate the images by using something like this:

product.images = NSSet(array: imageArray)

Make sure that the imageArray variable contains objects of type Images.

So to summarize, first you need to parse the array and create Images products from that array:

var imageArray = [Images]()

for yourRawImageFromJSON in json{
    let image1 = Images(context: managedObjectContext)
    // set properties on image
    yourImages.append(image1)
}

then, once you have that array of Images, do as I said above (adding it here once again for the sake of completeness):

product.images = NSSet(array: imageArray)

Side note

You should follow the general rule that classes are named in a singular format, so instead of using Images and Products as your class names, you should be using Image and Product. Even though a class represents a generalised concept (much like in a database system, where conceptual classes are named plurally), it's more common and more convenient to use a singular form (for example it's more concise to use let product = Product() as opposed to let product = Products() because the latter conveys the notion that you are instantiating multiple instances, even though that's clearly not the case)

the_critic
  • 12,720
  • 19
  • 67
  • 115
  • thanks for the reply, the images are stored as string because they are included in the image assets. so Images.image attribute is string in the Model. One core question pls just to clarify that my understanding is correct. when i have a relationship between two entities i should be entering the common data in the relationship rather than the second table right? because if i entered into the "many" table they wont be related to the "one" table right? – Maz Mar 03 '16 at 21:18
  • I don't really know what you are referring to... Did you get the basic idea of my answer ? – the_critic Mar 03 '16 at 21:19
  • what i meant is that i am not storing the UIImage into the database, i am just storing the file names in strings. i will convert that to UIImage in the app – Maz Mar 03 '16 at 21:21
  • @Maz I'm aware of that, I never mentioned anything about storing `UIImages` into Core Data ? – the_critic Mar 03 '16 at 21:25
  • i created a string array with the name of the files and then i created an NSSet constant from that array like you said, and when i tried to save it to the database this is the error i got: 2016-03-03 22:26:58.356 GadgetStore[27315:2857974] -[NSTaggedPointerString managedObjectContext]: unrecognized selector sent to instance 0xa011883a81d625a9 2016-03-03 22:26:58.400 GadgetStore[27315:2857974] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString managedObjectContext]: unrecognized selector sent to instance 0xa011883a81d625a9' – Maz Mar 03 '16 at 21:29
  • Look at my answer again... You'll have to create `Images` instances and put them in the array, **NOT** the image strings. You cannot represent relationships using strings as the base class. Relationships are between entities (`NSManagedObject` instances) not arbitrary objects. – the_critic Mar 03 '16 at 21:31
  • i think the array i am converting to NSSet must be of type Images because i used them as string. right? – Maz Mar 03 '16 at 21:31
  • thanks, and sorry for the frustration, its my first coredata app. will try that. – Maz Mar 03 '16 at 21:32
  • @Maz Don't be sorry, I've been there it's hard to grasp the concepts when you're just starting out. I'm here to help if you need something – the_critic Mar 03 '16 at 21:33
  • it worked, thanks alot! i now understand the concept behind it. when i use the product.images = NSSet(array) its like i am putting instances of that table Images inside the "product.images" column. (in the database concept that is) now i understand how they are really related. again thanks alot, you cant imagine how much i googled for this solution. – Maz Mar 03 '16 at 21:43
  • I'm very glad it helped and more importantly that you understood it! Good luck with your project! – the_critic Mar 03 '16 at 21:44
  • hello @ the_critic please can you check out this question i asked recently on coredata? http://stackoverflow.com/questions/35858236/swift-ios-insert-data-into-related-entities-in-coredata – Maz Mar 08 '16 at 08:36
0

I'm not using CoreData like you do here in my project, but if the other parts of your code are working as intended, this should do the job.

if let images = record["image"] as? NSMutableArray {       
    for i in 0..<images.count {
        let imageObject = Images(context: managedObjectContext)

        imageObject.image = images[i]

        let images = product.mutableSetValueForKey("images")
        images.addObject(imageObject)
    }
}

And about the naming of your CoreData objects I agree with the @the_critic, you actually have image objects and product objects. And your product objects has multiple image objects. Which means making the name of the object "image" and name of the relationship "images" in your data model could be much better.

enoktate
  • 725
  • 6
  • 12
0

Is not a good practice to store Arrays in core data, Therefore, this is how I would do it:

First: you need to create an Image Object.

    extension Images { /* Images+Methods */
      class func save(images: [NSDictionary]) -> [Images]? {

        let imagesArray = [Images]() 
        for imageObject in images {

          // creates a new Images NSManagedObject
          guard let imageEntity = NSEntityDescription.insertNewObjectForEntityForName("Images", inManagedObjectContext: managedObjectContext) as? Images else {
            return nil
          }

          if let image = imageObject["image"] as? String {
            imageEntity.image = image
            imagesArray.append(imageEntity)
          }
        }
        return imagesArray
      }

Then, call the Save(images: [NSDictionary]) method from your previous method.

    if let images = record["image"] as? NSMutableArray {

      // after what you already have, add the following line
      if let images = record["images"] as? NSMutableArray, let imagesObjectArray = Images.save(images) {
        product.images = NSSet(array:imagesObjectArray)
      }
    }

This should do it. Because is creating an Images NSManagedObject for every image that you have.

Hope this helps

P.S You really need to change your classes name.

dmlebron
  • 861
  • 6
  • 16