Wrapping Core Data into Codable structs
up vote
0
down vote
favorite
I am wrapping Core Data objects into structs to make them Codable.
[NB: Before your direct me to writing the swift file for each Core Data class, I would like to say that wrapping the NSManagedObject children result from a conscious choice in favour of code maintainability, as the data model may evolve in the future.]
I have the few classes like these, here is an example:
struct CodableNeed : Codable {
enum CodingKeys: String, CodingKey {
...
}
var need:Need
init (_ need:Need) {
self.need = need
}
init(from decoder: Decoder) throws {
....
}
func encode(to encoder: Encoder) throws {
....
}
}
This works actually pretty well, as any update in the init(from:decoder) of the struct is actually stored in the ManagedObjectContext.
In order to let each NSManagedObject class instance return their own struct, I defined a protocol where each class instance may return their own Codable struct:
protocol CodableWhenWrapped {
func wrapToCodable() -> Codable
}
extension Need : CodableWhenWrapped {
func wrapToCodable() -> Codable {
return CodableNeed(self)
}
}
I then use this in an encoding function:
func jsonDataOfCodable<T:Encodable>(_ object:T) throws -> Data {
let encoder = JSONEncoder()
let data = try encoder.encode(object)
return data
}
and I call this function to generate an URLSessionUploadTask
:
func updateTaskFor<T: NSManagedObject> (_ object:T, withSession session:URLSession) throws -> URLSessionUploadTask
where T: CodableWhenWrapped
{
let encoder = JSONEncoder()
// Here is the compile error:
// " Cannot invoke 'jsonDataOfCodable' with an argument list of type '(Codable)' "
let jsonData = try jsonDataOfCodable(object.wrapToCodable())
// then continue with generating the uploadTask
let url = "https://myurl.com/"
let request = URLRequest(url: url)
let updateTask = session.uploadTask(with: request, from: jsonData) { (data, response, error) in
....
}
}
Here is the issue: the code does not compile when calling jsonDataOfCodable
: Cannot invoke 'jsonDataOfCodable' with an argument list of type '(Codable)'
.
Any idea why the compiler does not like this?
Note that I have the same issue when I specify <T:Codable>
instead of <T:Encodable>
in the jsonDataOfCodable
prototype.
swift core-data wrapper swift-protocols codable
add a comment |
up vote
0
down vote
favorite
I am wrapping Core Data objects into structs to make them Codable.
[NB: Before your direct me to writing the swift file for each Core Data class, I would like to say that wrapping the NSManagedObject children result from a conscious choice in favour of code maintainability, as the data model may evolve in the future.]
I have the few classes like these, here is an example:
struct CodableNeed : Codable {
enum CodingKeys: String, CodingKey {
...
}
var need:Need
init (_ need:Need) {
self.need = need
}
init(from decoder: Decoder) throws {
....
}
func encode(to encoder: Encoder) throws {
....
}
}
This works actually pretty well, as any update in the init(from:decoder) of the struct is actually stored in the ManagedObjectContext.
In order to let each NSManagedObject class instance return their own struct, I defined a protocol where each class instance may return their own Codable struct:
protocol CodableWhenWrapped {
func wrapToCodable() -> Codable
}
extension Need : CodableWhenWrapped {
func wrapToCodable() -> Codable {
return CodableNeed(self)
}
}
I then use this in an encoding function:
func jsonDataOfCodable<T:Encodable>(_ object:T) throws -> Data {
let encoder = JSONEncoder()
let data = try encoder.encode(object)
return data
}
and I call this function to generate an URLSessionUploadTask
:
func updateTaskFor<T: NSManagedObject> (_ object:T, withSession session:URLSession) throws -> URLSessionUploadTask
where T: CodableWhenWrapped
{
let encoder = JSONEncoder()
// Here is the compile error:
// " Cannot invoke 'jsonDataOfCodable' with an argument list of type '(Codable)' "
let jsonData = try jsonDataOfCodable(object.wrapToCodable())
// then continue with generating the uploadTask
let url = "https://myurl.com/"
let request = URLRequest(url: url)
let updateTask = session.uploadTask(with: request, from: jsonData) { (data, response, error) in
....
}
}
Here is the issue: the code does not compile when calling jsonDataOfCodable
: Cannot invoke 'jsonDataOfCodable' with an argument list of type '(Codable)'
.
Any idea why the compiler does not like this?
Note that I have the same issue when I specify <T:Codable>
instead of <T:Encodable>
in the jsonDataOfCodable
prototype.
swift core-data wrapper swift-protocols codable
JSONEncoder().encode
requires a concrete type conforming to(En)codable
, not the protocolCodable
itself. You could use anassociatedtype
. It might be easier to implementinit(from decoder:
andencode(to encoder:
in eachNSManagedObject
sublcass. Especially if there are relationships it's the only choice.
– vadian
2 days ago
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
I am wrapping Core Data objects into structs to make them Codable.
[NB: Before your direct me to writing the swift file for each Core Data class, I would like to say that wrapping the NSManagedObject children result from a conscious choice in favour of code maintainability, as the data model may evolve in the future.]
I have the few classes like these, here is an example:
struct CodableNeed : Codable {
enum CodingKeys: String, CodingKey {
...
}
var need:Need
init (_ need:Need) {
self.need = need
}
init(from decoder: Decoder) throws {
....
}
func encode(to encoder: Encoder) throws {
....
}
}
This works actually pretty well, as any update in the init(from:decoder) of the struct is actually stored in the ManagedObjectContext.
In order to let each NSManagedObject class instance return their own struct, I defined a protocol where each class instance may return their own Codable struct:
protocol CodableWhenWrapped {
func wrapToCodable() -> Codable
}
extension Need : CodableWhenWrapped {
func wrapToCodable() -> Codable {
return CodableNeed(self)
}
}
I then use this in an encoding function:
func jsonDataOfCodable<T:Encodable>(_ object:T) throws -> Data {
let encoder = JSONEncoder()
let data = try encoder.encode(object)
return data
}
and I call this function to generate an URLSessionUploadTask
:
func updateTaskFor<T: NSManagedObject> (_ object:T, withSession session:URLSession) throws -> URLSessionUploadTask
where T: CodableWhenWrapped
{
let encoder = JSONEncoder()
// Here is the compile error:
// " Cannot invoke 'jsonDataOfCodable' with an argument list of type '(Codable)' "
let jsonData = try jsonDataOfCodable(object.wrapToCodable())
// then continue with generating the uploadTask
let url = "https://myurl.com/"
let request = URLRequest(url: url)
let updateTask = session.uploadTask(with: request, from: jsonData) { (data, response, error) in
....
}
}
Here is the issue: the code does not compile when calling jsonDataOfCodable
: Cannot invoke 'jsonDataOfCodable' with an argument list of type '(Codable)'
.
Any idea why the compiler does not like this?
Note that I have the same issue when I specify <T:Codable>
instead of <T:Encodable>
in the jsonDataOfCodable
prototype.
swift core-data wrapper swift-protocols codable
I am wrapping Core Data objects into structs to make them Codable.
[NB: Before your direct me to writing the swift file for each Core Data class, I would like to say that wrapping the NSManagedObject children result from a conscious choice in favour of code maintainability, as the data model may evolve in the future.]
I have the few classes like these, here is an example:
struct CodableNeed : Codable {
enum CodingKeys: String, CodingKey {
...
}
var need:Need
init (_ need:Need) {
self.need = need
}
init(from decoder: Decoder) throws {
....
}
func encode(to encoder: Encoder) throws {
....
}
}
This works actually pretty well, as any update in the init(from:decoder) of the struct is actually stored in the ManagedObjectContext.
In order to let each NSManagedObject class instance return their own struct, I defined a protocol where each class instance may return their own Codable struct:
protocol CodableWhenWrapped {
func wrapToCodable() -> Codable
}
extension Need : CodableWhenWrapped {
func wrapToCodable() -> Codable {
return CodableNeed(self)
}
}
I then use this in an encoding function:
func jsonDataOfCodable<T:Encodable>(_ object:T) throws -> Data {
let encoder = JSONEncoder()
let data = try encoder.encode(object)
return data
}
and I call this function to generate an URLSessionUploadTask
:
func updateTaskFor<T: NSManagedObject> (_ object:T, withSession session:URLSession) throws -> URLSessionUploadTask
where T: CodableWhenWrapped
{
let encoder = JSONEncoder()
// Here is the compile error:
// " Cannot invoke 'jsonDataOfCodable' with an argument list of type '(Codable)' "
let jsonData = try jsonDataOfCodable(object.wrapToCodable())
// then continue with generating the uploadTask
let url = "https://myurl.com/"
let request = URLRequest(url: url)
let updateTask = session.uploadTask(with: request, from: jsonData) { (data, response, error) in
....
}
}
Here is the issue: the code does not compile when calling jsonDataOfCodable
: Cannot invoke 'jsonDataOfCodable' with an argument list of type '(Codable)'
.
Any idea why the compiler does not like this?
Note that I have the same issue when I specify <T:Codable>
instead of <T:Encodable>
in the jsonDataOfCodable
prototype.
swift core-data wrapper swift-protocols codable
swift core-data wrapper swift-protocols codable
asked 2 days ago
harrouet
445
445
JSONEncoder().encode
requires a concrete type conforming to(En)codable
, not the protocolCodable
itself. You could use anassociatedtype
. It might be easier to implementinit(from decoder:
andencode(to encoder:
in eachNSManagedObject
sublcass. Especially if there are relationships it's the only choice.
– vadian
2 days ago
add a comment |
JSONEncoder().encode
requires a concrete type conforming to(En)codable
, not the protocolCodable
itself. You could use anassociatedtype
. It might be easier to implementinit(from decoder:
andencode(to encoder:
in eachNSManagedObject
sublcass. Especially if there are relationships it's the only choice.
– vadian
2 days ago
JSONEncoder().encode
requires a concrete type conforming to (En)codable
, not the protocol Codable
itself. You could use an associatedtype
. It might be easier to implement init(from decoder:
and encode(to encoder:
in each NSManagedObject
sublcass. Especially if there are relationships it's the only choice.– vadian
2 days ago
JSONEncoder().encode
requires a concrete type conforming to (En)codable
, not the protocol Codable
itself. You could use an associatedtype
. It might be easier to implement init(from decoder:
and encode(to encoder:
in each NSManagedObject
sublcass. Especially if there are relationships it's the only choice.– vadian
2 days ago
add a comment |
1 Answer
1
active
oldest
votes
up vote
0
down vote
I am answering my own question after reflecting on vadian's comment.
I solved the issue by updating my protocol:
protocol CodableWhenWrapped : Encodable {
func wrapToCodable<T>() -> CodableWrapper<T>
func update(from decoder:Decoder) throws
}
Then I added a Generic wrapper for my objects (all inheriting from a Synchronizable
class):
struct CodableWrapper<T> : Codable
where T:CodableWhenWrapped, T: Synchronizable
{
enum SynchronizableCodingKeys: String, CodingKey {
case pk = "idOnServer"
case lastModificationDate = "modified_on"
}
var object:T
init(_ obj:T){
self.object = obj
}
init(from decoder: Decoder) throws {
// This is how the NSManagedObjectContext is passed through the decoder
guard let codingUserInfoKeyManagedObjectContext = CodingUserInfoKey.managedObjectContext,
let managedObjectContext = decoder.userInfo[codingUserInfoKeyManagedObjectContext] as? NSManagedObjectContext
else {
fatalError("Failed to decode Need: could not retriever the NSManagedObjectContext. Was it included in Decoder.userInfo as CodingUserInfoKey.managedObjectContext ?")
}
// check if there is a an existing object with the same idOnServer. If not, create a new one
let container = try decoder.container(keyedBy: SynchronizableCodingKeys.self)
let pk = try container.decode(UUID.self, forKey: .pk)
object = try Synchronizable.withIDOnServer(pk.uuidString, inMOC: managedObjectContext) ?? T(context: managedObjectContext)
try object.update(from: decoder)
}
func encode(to encoder: Encoder) throws {
try object.encode(to: encoder)
}
}
I can then have a generic function to serialize to JSON:
func jsonDataFor<T>(_ object:T) throws -> Data
where T:CodableWhenWrapped, T:Synchronizable {
let encoder = JSONEncoder()
let wrappedObject:CodableWrapper<T> = object.wrapToCodable()
let jsonData = try encoder.encode(wrappedObject)
return jsonData
}
Decoding is just:
let _ = try decoder.decode(CodableWrapper<T>.self, from: data!)
Thanks @vadian for your help, I hope this is useful for others!
BTW, the CodableWhenWrapped protocol can easilt be reused for other objects types.
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
I am answering my own question after reflecting on vadian's comment.
I solved the issue by updating my protocol:
protocol CodableWhenWrapped : Encodable {
func wrapToCodable<T>() -> CodableWrapper<T>
func update(from decoder:Decoder) throws
}
Then I added a Generic wrapper for my objects (all inheriting from a Synchronizable
class):
struct CodableWrapper<T> : Codable
where T:CodableWhenWrapped, T: Synchronizable
{
enum SynchronizableCodingKeys: String, CodingKey {
case pk = "idOnServer"
case lastModificationDate = "modified_on"
}
var object:T
init(_ obj:T){
self.object = obj
}
init(from decoder: Decoder) throws {
// This is how the NSManagedObjectContext is passed through the decoder
guard let codingUserInfoKeyManagedObjectContext = CodingUserInfoKey.managedObjectContext,
let managedObjectContext = decoder.userInfo[codingUserInfoKeyManagedObjectContext] as? NSManagedObjectContext
else {
fatalError("Failed to decode Need: could not retriever the NSManagedObjectContext. Was it included in Decoder.userInfo as CodingUserInfoKey.managedObjectContext ?")
}
// check if there is a an existing object with the same idOnServer. If not, create a new one
let container = try decoder.container(keyedBy: SynchronizableCodingKeys.self)
let pk = try container.decode(UUID.self, forKey: .pk)
object = try Synchronizable.withIDOnServer(pk.uuidString, inMOC: managedObjectContext) ?? T(context: managedObjectContext)
try object.update(from: decoder)
}
func encode(to encoder: Encoder) throws {
try object.encode(to: encoder)
}
}
I can then have a generic function to serialize to JSON:
func jsonDataFor<T>(_ object:T) throws -> Data
where T:CodableWhenWrapped, T:Synchronizable {
let encoder = JSONEncoder()
let wrappedObject:CodableWrapper<T> = object.wrapToCodable()
let jsonData = try encoder.encode(wrappedObject)
return jsonData
}
Decoding is just:
let _ = try decoder.decode(CodableWrapper<T>.self, from: data!)
Thanks @vadian for your help, I hope this is useful for others!
BTW, the CodableWhenWrapped protocol can easilt be reused for other objects types.
add a comment |
up vote
0
down vote
I am answering my own question after reflecting on vadian's comment.
I solved the issue by updating my protocol:
protocol CodableWhenWrapped : Encodable {
func wrapToCodable<T>() -> CodableWrapper<T>
func update(from decoder:Decoder) throws
}
Then I added a Generic wrapper for my objects (all inheriting from a Synchronizable
class):
struct CodableWrapper<T> : Codable
where T:CodableWhenWrapped, T: Synchronizable
{
enum SynchronizableCodingKeys: String, CodingKey {
case pk = "idOnServer"
case lastModificationDate = "modified_on"
}
var object:T
init(_ obj:T){
self.object = obj
}
init(from decoder: Decoder) throws {
// This is how the NSManagedObjectContext is passed through the decoder
guard let codingUserInfoKeyManagedObjectContext = CodingUserInfoKey.managedObjectContext,
let managedObjectContext = decoder.userInfo[codingUserInfoKeyManagedObjectContext] as? NSManagedObjectContext
else {
fatalError("Failed to decode Need: could not retriever the NSManagedObjectContext. Was it included in Decoder.userInfo as CodingUserInfoKey.managedObjectContext ?")
}
// check if there is a an existing object with the same idOnServer. If not, create a new one
let container = try decoder.container(keyedBy: SynchronizableCodingKeys.self)
let pk = try container.decode(UUID.self, forKey: .pk)
object = try Synchronizable.withIDOnServer(pk.uuidString, inMOC: managedObjectContext) ?? T(context: managedObjectContext)
try object.update(from: decoder)
}
func encode(to encoder: Encoder) throws {
try object.encode(to: encoder)
}
}
I can then have a generic function to serialize to JSON:
func jsonDataFor<T>(_ object:T) throws -> Data
where T:CodableWhenWrapped, T:Synchronizable {
let encoder = JSONEncoder()
let wrappedObject:CodableWrapper<T> = object.wrapToCodable()
let jsonData = try encoder.encode(wrappedObject)
return jsonData
}
Decoding is just:
let _ = try decoder.decode(CodableWrapper<T>.self, from: data!)
Thanks @vadian for your help, I hope this is useful for others!
BTW, the CodableWhenWrapped protocol can easilt be reused for other objects types.
add a comment |
up vote
0
down vote
up vote
0
down vote
I am answering my own question after reflecting on vadian's comment.
I solved the issue by updating my protocol:
protocol CodableWhenWrapped : Encodable {
func wrapToCodable<T>() -> CodableWrapper<T>
func update(from decoder:Decoder) throws
}
Then I added a Generic wrapper for my objects (all inheriting from a Synchronizable
class):
struct CodableWrapper<T> : Codable
where T:CodableWhenWrapped, T: Synchronizable
{
enum SynchronizableCodingKeys: String, CodingKey {
case pk = "idOnServer"
case lastModificationDate = "modified_on"
}
var object:T
init(_ obj:T){
self.object = obj
}
init(from decoder: Decoder) throws {
// This is how the NSManagedObjectContext is passed through the decoder
guard let codingUserInfoKeyManagedObjectContext = CodingUserInfoKey.managedObjectContext,
let managedObjectContext = decoder.userInfo[codingUserInfoKeyManagedObjectContext] as? NSManagedObjectContext
else {
fatalError("Failed to decode Need: could not retriever the NSManagedObjectContext. Was it included in Decoder.userInfo as CodingUserInfoKey.managedObjectContext ?")
}
// check if there is a an existing object with the same idOnServer. If not, create a new one
let container = try decoder.container(keyedBy: SynchronizableCodingKeys.self)
let pk = try container.decode(UUID.self, forKey: .pk)
object = try Synchronizable.withIDOnServer(pk.uuidString, inMOC: managedObjectContext) ?? T(context: managedObjectContext)
try object.update(from: decoder)
}
func encode(to encoder: Encoder) throws {
try object.encode(to: encoder)
}
}
I can then have a generic function to serialize to JSON:
func jsonDataFor<T>(_ object:T) throws -> Data
where T:CodableWhenWrapped, T:Synchronizable {
let encoder = JSONEncoder()
let wrappedObject:CodableWrapper<T> = object.wrapToCodable()
let jsonData = try encoder.encode(wrappedObject)
return jsonData
}
Decoding is just:
let _ = try decoder.decode(CodableWrapper<T>.self, from: data!)
Thanks @vadian for your help, I hope this is useful for others!
BTW, the CodableWhenWrapped protocol can easilt be reused for other objects types.
I am answering my own question after reflecting on vadian's comment.
I solved the issue by updating my protocol:
protocol CodableWhenWrapped : Encodable {
func wrapToCodable<T>() -> CodableWrapper<T>
func update(from decoder:Decoder) throws
}
Then I added a Generic wrapper for my objects (all inheriting from a Synchronizable
class):
struct CodableWrapper<T> : Codable
where T:CodableWhenWrapped, T: Synchronizable
{
enum SynchronizableCodingKeys: String, CodingKey {
case pk = "idOnServer"
case lastModificationDate = "modified_on"
}
var object:T
init(_ obj:T){
self.object = obj
}
init(from decoder: Decoder) throws {
// This is how the NSManagedObjectContext is passed through the decoder
guard let codingUserInfoKeyManagedObjectContext = CodingUserInfoKey.managedObjectContext,
let managedObjectContext = decoder.userInfo[codingUserInfoKeyManagedObjectContext] as? NSManagedObjectContext
else {
fatalError("Failed to decode Need: could not retriever the NSManagedObjectContext. Was it included in Decoder.userInfo as CodingUserInfoKey.managedObjectContext ?")
}
// check if there is a an existing object with the same idOnServer. If not, create a new one
let container = try decoder.container(keyedBy: SynchronizableCodingKeys.self)
let pk = try container.decode(UUID.self, forKey: .pk)
object = try Synchronizable.withIDOnServer(pk.uuidString, inMOC: managedObjectContext) ?? T(context: managedObjectContext)
try object.update(from: decoder)
}
func encode(to encoder: Encoder) throws {
try object.encode(to: encoder)
}
}
I can then have a generic function to serialize to JSON:
func jsonDataFor<T>(_ object:T) throws -> Data
where T:CodableWhenWrapped, T:Synchronizable {
let encoder = JSONEncoder()
let wrappedObject:CodableWrapper<T> = object.wrapToCodable()
let jsonData = try encoder.encode(wrappedObject)
return jsonData
}
Decoding is just:
let _ = try decoder.decode(CodableWrapper<T>.self, from: data!)
Thanks @vadian for your help, I hope this is useful for others!
BTW, the CodableWhenWrapped protocol can easilt be reused for other objects types.
answered 2 days ago
harrouet
445
445
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53350354%2fwrapping-core-data-into-codable-structs%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
JSONEncoder().encode
requires a concrete type conforming to(En)codable
, not the protocolCodable
itself. You could use anassociatedtype
. It might be easier to implementinit(from decoder:
andencode(to encoder:
in eachNSManagedObject
sublcass. Especially if there are relationships it's the only choice.– vadian
2 days ago