1

I have this data structure in my app. As you can see, I assume that each car brand is sold by one dealer and a dealer may sell more than one brand.

I'm retrieving CarBrand data from a web service. Since I'm getting data in bulk how do I save data so that the carbrand data is associated with Delear record (thus maintaining the relationship that I established)

This is my attempt :

        let moc = DataController().managedObjectContext

        let carbrandEntity = NSEntityDescription.insertNewObjectForEntityForName("carbrandEntity", inManagedObjectContext: moc) as! CarBrand

        let dealerEntity = NSEntityDescription.insertNewObjectForEntityForName("DealerEntity", inManagedObjectContext: moc) as! Dealer

        for carbrand in self.carbrandArray {
            // add our data
            carbrandEntity.setValue(carbrand[0], forKey: "brandname")
            carbrandEntity.setValue(carbrand[1], forKey: "makeyear")

            do {
                try moc.save()
            } catch {
                fatalError("Failure to save context: \(error)")
            }

        }

        dealerEntity.setValue("Steven&Sons", forKey: "name")
        // How do I save dealer entity so that it is associated with the carbrand data that I just added.

UPDATE

This is my attempt but when I fetch specific dealer, I get the dealer information but CarBrand(NSSet) is always empty.

        let moc = DataController().managedObjectContext

        let carbrandEntity = NSEntityDescription.insertNewObjectForEntityForName("carbrandEntity", inManagedObjectContext: moc) as! CarBrand

        let dealerEntity = NSEntityDescription.insertNewObjectForEntityForName("DealerEntity", inManagedObjectContext: moc) as! Dealer

        dealerEntity.setValue("Steven&Sons", forKey: "name")

        for carbrand in self.carbrandArray {
            // add our data
            carbrandEntity.setValue(carbrand[0], forKey: "brandname")
            carbrandEntity.setValue(carbrand[1], forKey: "makeyear")


        }

        do {
                try moc.save()
        } catch {
                fatalError("Failure to save context: \(error)")
        }

UPDATE

I'm still not getting any carband data. Here's an updated code

        let dealerEntity = NSEntityDescription.insertNewObjectForEntityForName("DealerEntity", inManagedObjectContext: moc) as! Dealer

        dealerEntity.setValue("Steven&Sons", forKey: "name")

        for c in self.carbrandArray {
            dealerEntity.carbrand?.setValue(c[0], forKey: "brandname")
            dealerEntity.carbrand?.setValue(c[1], forKey: "makeyear")

        }

Just in case if I'm making any mistake in fetching, here's my FETCH CODE

        let moc = DataController().managedObjectContext
        let dealerEntity = NSFetchRequest(entityName: "DealerEntity")

        do {
            let dealers = try moc.executeFetchRequest(dealerEntity) as! [Dealer]
            for d in dealers {
               print(d.carbrand!.count) // Returns 0
            }

        } catch {
           fatalError("Failed to fetch saved data: \(error)")
    }
6
  • 1
    dealerEntity has an attribute named "carbrand", just use setValue to add your set of carbrandEntities to your dealerEntity. You also don't have to save each carbandEntity individually, Core Data will do that for you when you save your dealerEntity. You can just save your dealerEntity after you've added your set of carbandEntities to it. Commented Jul 12, 2016 at 21:01
  • @dan-beaulieu can you place your comment in an answer and show a code example? Commented Jul 13, 2016 at 15:39
  • You have to insert a carbrand object for each carbrand. Set the dealer of the carbrand and Core Data will add the carbrand to the dealer. Don't mix up 'entity' (table) and 'object' (row). Commented Jul 14, 2016 at 0:48
  • @Dan Beaulieu : Sorry for the late reply. I tried your approach but the carbrand NSSet is always empty. I probably doing something wrong. Commented Jul 18, 2016 at 15:00
  • @Dan Beaulieu Could you give me pseduocode? I updated my post with last attempt and it's still not working. Commented Jul 18, 2016 at 18:25

1 Answer 1

1

So I simplified an example for you to check out. I've uploaded a project file here that works perfectly for me.

project file

I'm not claiming that this is a perfect solution nor am I claiming these are best practices. I'm just showing you the concept and how I approach this.

Here's what my data model looks like.

enter image description here enter image description here

Here's the code to accomplish your task. Notice that we're encapsulating the logic necessary to store our objects within our Managed Object classes themselves. This is not a requirement but it's an approach I use to keep my code cleaner.

Post

public final class Post: NSManagedObject {

    @NSManaged var title: String
    @NSManaged var body: String
    @NSManaged var date: NSDate?
    @NSManaged var comments: Set<Comment>?

    // Insert code here to add functionality to your managed object subclass
    public static func insertIntoContext(moc: NSManagedObjectContext, json: AnyObject) {
        print("\n==============\n Post: \n\(json) \n==============\n")

       let post = NSEntityDescription.insertNewObjectForEntityForName("Post", inManagedObjectContext: moc) as! Post

        guard let 
            title = json["title"] as? String,
            body = json["body"] as? String 
            else { assertionFailure("post body failed"); return }

        post.title = title 
        post.body = body
        post.date = NSDate()

        if let jsonComments = json["comments"] as? [AnyObject] {

            // pass comments data into our comments class to get inserted into
            // our context
            let comments = Comment.returnSet(moc, jsonArray: jsonComments)
            post.comments = comments

        } 

        do {
            try moc.save()
        } catch let error {
            print("failed: \n\(error)\n\n<- - - - ->")
        }
    }
}

Comment

public final class Comment: NSManagedObject {

    @NSManaged var text: String
    @NSManaged var name: String

    public static func returnSet(moc: NSManagedObjectContext, jsonArray: [AnyObject]) -> Set<Comment> {
        var comments = Set<Comment>()

        for answer in jsonArray {
            // this guard statement will continue to the next object if our insert fails
            guard let c = Comment.insertIntoContext(moc, json: answer) else { continue }
            comments.insert(c)
        }

        return comments
    }

    public static func insertIntoContext(moc: NSManagedObjectContext, json: AnyObject) -> Comment? {
        //print("\ncomment : \n \(json)\n")
        guard let
            jsonText = json["text"] as? String,
            jsonName = json["name"] as? String
            else { assertionFailure("");return nil }

        let comment = NSEntityDescription.insertNewObjectForEntityForName("Comment", inManagedObjectContext: moc) as! Comment

        comment.text = jsonText
        comment.name = jsonName

        return comment
    }
}

ViewController

class ViewController: UIViewController {

    var moc: NSManagedObjectContext!

    override func viewDidLoad() {
        super.viewDidLoad()

        // you'd get your data from a webserver ideally
        let postObj : [String: AnyObject] = [
            "title" : "some title",
            "body" : "this is some body text",
            "comments" : [
                [
                    "text":"some comment",
                    "name":"dan beaulieu"
                ],
                [
                    "text":"another comment comment",
                    "name":"user30646"
                ],
                [
                    "text":"a much wiser comment",
                    "name":"Rob"
                ]
            ]
        ]

        // then you pass in your object and your context
        Post.insertIntoContext(moc, json: postObj)

        let posts = NSFetchRequest(entityName: "Post")

        do {
            let posts = try moc.executeFetchRequest(posts) as! [Post]
            for p in posts {
                print("------- Comments ------")
                print(p.comments) // Returns 0
            } 
        } catch {
            fatalError("Failed to fetch saved data: \(error)")
        }

    }
}

AppDelegate

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Override point for customization after application launch.

    guard let vc = window?.rootViewController as? ViewController
        else { fatalError("Wrong View Controller Type") }
    vc.moc = managedObjectContext

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

1 Comment

I incorparted your logic and it works perfect! Thanks for awesome post!

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.