2

I'm trying to pass key value pairs and have the attributes updated through a loop.

func update(storableClass : NSManagedObject.Type, id: ID, fields: [String : Any]) throws {

    // retreive manaagedObject
    for (key, value) in fields {
        manaagedObject.setValue(value, forKey: key)
    }
}

Apparently the setValue(_:forKey:) will throw an exception if the key doesn't exist.

From what I've learned you're not to catch an Objective-C exception in Swift.

Is there any safe way to update core data properties through a dictionary?

I know I can have a function like below:

func update(storableClass : ManagedObject.Type, id: ID, closure: (ManagedObject) -> ()) throws {}

and then call it as such:

update(storableClass: UserEntity.self, id: "123123", closure: { userEntity in
    userEntity.name = "new name" 
})

I like to have both options...and any other safe/swifty option...

rmaddy
  • 314,917
  • 42
  • 532
  • 579
mfaani
  • 33,269
  • 19
  • 164
  • 293
  • @rmaddy Correct me if I'm wrong. I think objective-c is related because `NSManagedObject` are based off of `NSObject` and the `setValue` function is from objective-c... – mfaani Dec 10 '18 at 22:03
  • Every UI class is written in Objective-C too. Your question is about Swift and how to deal with mistakenly calling `setValue`, in Swift, with an unknown key. The better question for you is why are you calling `setValue` with an unknown key? That's a programming error. You should try to catch the exception, you should avoid causing it. – rmaddy Dec 10 '18 at 22:11
  • Hmmm. You mean _That's a programming error_ vs. a **user** error or something out my control e.g. **server**? – mfaani Dec 10 '18 at 22:20
  • the server isn't supposed to return a key that has no existence in your CD – Shehata Gamal Dec 10 '18 at 22:21
  • Correct. There a difference between writing incorrect code and properly handling data. – rmaddy Dec 10 '18 at 22:22
  • @Sh_Khan right. But I have no control over what server sends... – mfaani Dec 10 '18 at 22:22
  • then loop for the keys before inserting to verify it's in your model or not – Shehata Gamal Dec 10 '18 at 22:24
  • @rmaddy How do I catch programming errors? I would then needs some extensive manual or automated testing. My current NSManabgedObject has 15 fields and some other relationships... – mfaani Dec 10 '18 at 22:25
  • @Sh_Khan how do I verify if it's among the keys? I'm not aware of NSObject's `keys` property... – mfaani Dec 10 '18 at 22:26

1 Answers1

4

I've had this exact problem. Here's what you do.

With Core Data, you can easily find out what property names are valid for the object. Get a reference to the managed object's entity, and then ask the entity for the properties. You'd use managedObject.entity.propertiesByName. The result is a dictionary where the keys are valid properties. The code would be something like

let properties = managedObject.entity.propertiesByName
for (key, value) in fields {
    if properties[key] != nil {
        managedObject.setValue(value, forKey: key)
    } else {
        print("Unknown key: \(key)")
    }
}

This is fine as long as value is the right type. If you start getting bogus data types in JSON, you have a more complex problem to solve.

For relationships, do the same thing with managedObject.entity.relationshpsByName.

Tom Harrington
  • 69,312
  • 10
  • 146
  • 170
  • FWIW there is also a type func called `entity()` which will have the same `propertiesByName` & `relationshpsByName`. Also a very good related read from Tom can be found [here](http://www.cimgf.com/2011/06/02/saving-json-to-core-data/) – mfaani Dec 12 '18 at 22:08
  • Hi Tom, happy holidays. When you get a chance can you take a look [here](https://stackoverflow.com/questions/53891194/what-are-the-functional-differences-between-coredatas-codegen-manual-none-cr). I don't think it would require any coding, hopefully you can validate my observations off top of your head :) – mfaani Dec 24 '18 at 17:31