Codable Nsmanagedobject Fail on Decodeifpresent Data Type

Codable NSManagedObject fail on decodeIfPresent Data type

There is a general misunderstanding, the error is about Date not Data

The error occurs because the JSON decoder does not decode ISO8601 dates by default. The default is a TimeInterval aka Double.

That's what the error message says: It expects a Double but found a String

You have to set the DateDecodingStrategy of the decoder to .iso8601

decoder.dateDecodingStrategy = .iso8601

And remove the exclamation mark in try! to hand over the error

CoreData + Codable not saving

NSPersistentStoreDescription() creates an empty description where not even the store URL is set. Use NSPersistentStoreDescription(url:) instead.

You can use container.persistentStoreDescriptions.first!.url! to get the default url, which is being initialized with NSPersistentStoreContainer(_) and so usable before loading.

You can display the currently used url with:

print(container.persistentStoreCoordinator.persistentStores.first!.url!)

Updated NSPersistentContainer creation code

var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "ELModel")

let storeURL = container.persistentStoreDescriptions.first!.url!
let description = NSPersistentStoreDescription(url: storeURL)

container.persistentStoreDescriptions = [description]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
print("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()

The given data did not contain a top-level value error when attempting to decode json into NSManagedObject

I have finally resolved the issue. Instead using "Codegen: Class Definition", I should have used "Codegen: Manual" and add the subclass like this:

  1. Open the data model file
  2. Under Entities, select the relevant entity
  3. From the upper menu, choose Editor -> Create NSManagedObject subclass...

Swift: Insert codable object into Core Data

A smart solution is to adopt Decodable in Stuff

Write an extension of CodingUserInfoKey and JSONDecoder

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

extension JSONDecoder {
convenience init(context: NSManagedObjectContext) {
self.init()
self.userInfo[.context] = context
}
}

In Stuff adopt Decodable and implement init(from:), it must be implemented in the class, not in the extension

@objc(Stuff)
public class Stuff: NSManagedObject, Decodable {

private enum CodingKeys: String, CodingKey { case name, quantity, location }

public required convenience init(from decoder: Decoder) throws {
guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else { fatalError("Error: context doesn't exist!") }
let entity = NSEntityDescription.entity(forEntityName: "Stuff", in: context)!
self.init(entity: entity, insertInto: context)
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decodeIfPresent(String.self, forKey: .name)
quantity = try values.decodeIfPresent(Int64.self, forKey: .quantity) ?? 0
location = try values.decodeIfPresent(String.self, forKey: .location)
}
}

To decode the JSON you have to initialize the decoder with the convenience initializer

let decoder = JSONDecoder(context: context)

where context is the current NSManagedObjectContext instance.

Now you can create Stuff instances directly from the JSON.

Codable CoreData models crash with `Unrecognized selector sent to instance`

Your issue in this lines at Air.swift file
I'm really don't know why don't decode Gun at Air file Decoder

self.gun = try container.decodeIfPresent(Gun.self, forKey: .gun)
self.agm = try container.decodeIfPresent(Gun.self, forKey: .agm)
self.aam = try container.decodeIfPresent(Gun.self, forKey: .aam)
self.asm = try container.decodeIfPresent(Gun.self, forKey: .asm)

Swift: Loading Codable Class into CoreData from JSON is generating Objects with all properties to nil

To summarize from the questions comments:

It's important to remember that CoreData still relies heavily on Objective-C. In your example code, the properties on your class, although expressible as CoreData attributes, the implementation is not being handled by CoreData.

You'll need to add the @NSManaged attribute to your properties like this:

@NSManaged var name: String?
@NSManaged var desc: String?
@NSManaged var image: String?
@NSManaged var quantity: Double?

This will expose them to Obj-C as dynamic and allow CoreData to handle the implementation. This would also help to explain your debugging, in that at runtime the print statement would show values, but the saved managed object had nil values.

How to use swift 4 Codable in Core Data?

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.

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.

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

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

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:

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

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

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:

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.



Related Topics



Leave a reply



Submit