Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 875 Vote(s) - 3.45 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to use swift 4 Codable in Core Data?

#1
Codable seems a very exciting feature. But I wonder how we can use it in Core Data? In particular, is it possible to directly encode/decode a JSON from/to a NSManagedObject?

I tried a very simple example:

[![enter image description here][1]][1]

and defined `Foo` myself:

import CoreData

@objc(Foo)
public class Foo: NSManagedObject, Codable {}

But when using it like this:

let json = """
{
"name": "foo",
"bars": [{
"name": "bar1",
}], [{
"name": "bar2"
}]
}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let foo = try! decoder.decode(Foo.self, from: json)
print(foo)

The compiler failed with this errror:

super.init isn't called on all paths before returning from initializer

and the target file was the file that defined `Foo`

I guess I probably did it wrong, since I didn't even pass a `NSManagedObjectContext`, but I have no idea where to stick it.

Does Core Data support `Codable`?

[1]:
Reply

#2
As an alternative for those who would like to make use of XCode's modern approach to `NSManagedObject` file generation, I have created a `DecoderWrapper` class to expose a `Decoder` object which I then use within my object which conforms to a `JSONDecoding` protocol:



class DecoderWrapper: Decodable {

let decoder:Decoder

required init(from decoder:Decoder) throws {

self.decoder = decoder
}
}

protocol JSONDecoding {
func decodeWith(_ decoder: Decoder) throws
}

extension JSONDecoding where Self:NSManagedObject {

func decode(json:[String:Any]) throws {

let data = try JSONSerialization.data(withJSONObject: json, options: [])
let wrapper = try JSONDecoder().decode(DecoderWrapper.self, from: data)
try decodeWith(wrapper.decoder)
}
}

extension MyCoreDataClass: JSONDecoding {

enum CodingKeys: String, CodingKey {
case name // For example
}

func decodeWith(_ decoder: Decoder) throws {

let container = try decoder.container(keyedBy: CodingKeys.self)

self.name = try container.decode(String.self, forKey: .name)
}
}

This is probably only useful for models without any non-optional attributes, but it solves my problem of wanting to use `Decodable` but also manage relationships and persistence with Core Data without having to manually create all my classes / properties.

**Edit:** Example of it in use

If I have a json object:

let myjson = [ "name" : "Something" ]

I create the object in Core Data (force cast here for brevity):

let myObject = NSEntityDescription.insertNewObject(forEntityName: "MyCoreDataClass", into: myContext) as! MyCoreDataClass

And I use the extension to have the object decode the json:

do {
try myObject.decode(json: myjson)
}
catch {
// handle any error
}

Now `myObject.name` is `"Something"`
Reply

#3
You can use the Codable interface with CoreData objects to encode and decode data, however it's not as automatic as when used with plain old swift objects. Here's how you can implement JSON Decoding directly with Core Data objects:

First, you make your object implement Codable. This interface must be defined on the object, and not in an extension. You can also define your Coding Keys in this class.

<!-- language: swift -->

class MyManagedObject: NSManagedObject, Codable {
@NSManaged var property: String?

enum CodingKeys: String, CodingKey {
case property = "json_key"
}
}

Next, you can define the init method. This must also be defined in the class method because the init method is required by the Decodable protocol.

<!-- language: swift -->

required convenience init(from decoder: Decoder) throws {
}

However, the proper initializer for use with managed objects is:

<!-- language: swift -->

NSManagedObject.init(entity: NSEntityDescription, into context: NSManagedObjectContext)

So, the secret here is to use the *userInfo* dictionary to pass in the proper context object into the initializer. To do this, you'll need to extend the `CodingUserInfoKey` struct with a new key:

<!-- language: swift -->

extension CodingUserInfoKey {
static let context = CodingUserInfoKey(rawValue: "context")
}

Now, you can just as the decoder for the context:

<!-- language: swift -->

required convenience init(from decoder: Decoder) throws {

guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext else { fatalError() }
guard let entity = NSEntityDescription.entity(forEntityName: "MyManagedObject", in: context) else { fatalError() }

self.init(entity: entity, in: context)

let container = decoder.container(keyedBy: CodingKeys.self)
self.property = container.decodeIfPresent(String.self, forKey: .property)
}

Now, when you set up the decoding for Managed Objects, you'll need to pass along the proper context object:

<!-- language: swift -->

let data = //raw json data in Data object
let context = persistentContainer.newBackgroundContext()
let decoder = JSONDecoder()
decoder.userInfo[.context] = context

_ = try decoder.decode(MyManagedObject.self, from: data) //we'll get the value from another context using a fetch request later...

try context.save() //make sure to save your data once decoding is complete

To encode data, you'll need to do something similar using the **encode** protocol function.
Reply

#4
# Swift 4.2:

Following casademora's solution,

`guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else { fatalError() }`

should be

`guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext else { fatalError() }`.

This prevents errors that Xcode falsely recognizes as array slice problems.

Edit: Use implicitly unwrapped optionals to remove the need to force unwrap `.context` every time it is being used.
Reply

#5
CoreData is its own persistence framework and, per its thorough documentation, you must use its designated initializers and follow a rather specific path to creating and storing objects with it.

You can still use `Codable` with it in limited ways just as you can use `NSCoding`, however.

One way is to decode an object (or a struct) with either of these protocols and transfer its properties into a new `NSManagedObject` instance you've created per Core Data's docs.

Another way (which is very common) is to use one of the protocols only for a non-standard object you want to store in a managed object's properties. By "non-standard", I mean anything thst doesn't conform to Core Data's standard attribute types as specified in your model. For example, `NSColor` can't be stored directly as a Managed Object property since it's not one of the basic attribute types CD supports. Instead, you can use `NSKeyedArchiver` to serialize the color into an `NSData` instance and store it as a Data property in the Managed Object. Reverse this process with `NSKeyedUnarchiver`. That's simplistic and there is a much better way to do this with Core Data (see [Transient Attributes](

[To see links please register here]

)) but it illustrates my point.

You could also conceivably adopt `Encodable` (one of the two protocols that compose `Codable` - can you guess the name of the other?) to convert a Managed Object instance directly to JSON for sharing but you'd have to [specify coding keys](

[To see links please register here]

) and your own custom `encode` implementation since it won't be auto-synthesized by the compiler with custom coding keys. In this case you'd want to specify *only* the keys (properties) you want to be included.

Hope this helps.
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through