-
-
Save Akhu/5ea1ecbd652fb269f7c4e7db27bc79cc to your computer and use it in GitHub Desktop.
// | |
// Article.swift | |
// Veille | |
// | |
// Created by Anthony Da Cruz on 26/01/2018. | |
// Copyright © 2018 Anthony Da Cruz. All rights reserved. | |
// | |
import Foundation | |
import CoreData | |
class Article: NSManagedObject, Decodable { | |
@NSManaged var title:String! | |
@NSManaged var summary:String? | |
@NSManaged var link:URL! | |
@NSManaged var image:URL? | |
@NSManaged var createdDate: Date | |
//var tags:[String]? | |
@NSManaged var id:UUID | |
enum CodingKeys: String, CodingKey { | |
case title | |
case summary = "description" | |
case link | |
case image = "imageURL" | |
case createdDate = "date" | |
} | |
required convenience init(from decoder: Decoder) throws { | |
guard let contextUserInfoKey = CodingUserInfoKey.context else { fatalError("cannot find context key") } | |
guard let managedObjectContext = decoder.userInfo[contextUserInfoKey] as? NSManagedObjectContext else { fatalError("cannot Retrieve context") } | |
guard let entity = NSEntityDescription.entity(forEntityName: "Article", in: managedObjectContext) else { fatalError() } | |
self.init(entity: entity, insertInto: nil) | |
let values = try decoder.container(keyedBy: CodingKeys.self) | |
self.createdDate = Date() | |
self.title = try values.decode(String.self, forKey: .title) | |
self.summary = try values.decode(String.self, forKey: .summary) | |
guard let linkString = try values.decodeIfPresent(String.self, forKey: .link) else { return } | |
if let linkUrl = URL(string: linkString) { | |
self.link = linkUrl | |
} | |
if let imageURLString = try values.decodeIfPresent(String.self, forKey: .image) { | |
if let imageURL = URL(string: imageURLString){ | |
self.image = imageURL | |
} | |
} | |
} | |
} | |
extension Article: Encodable{ | |
public func encode(to encoder: Encoder) throws { | |
var container = encoder.container(keyedBy: CodingKeys.self) | |
try container.encode(self.title, forKey: .title) | |
try container.encodeIfPresent(self.summary, forKey: .summary) | |
try container.encodeIfPresent(self.image, forKey: .image) | |
try container.encode(self.link, forKey: .link) | |
try container.encode(self.createdDate.toIso8601(), forKey: .createdDate) | |
} | |
} | |
extension CodingUserInfoKey { | |
static let context = CodingUserInfoKey(rawValue: "context") | |
} | |
// Use it : | |
let context = CoreDataStack.store.persistentContainer.newBackgroundContext() //Getting context | |
let plistDecoderForArticle = PropertyListDecoder() | |
plistDecoderForArticle.userInfo[CodingUserInfoKey.context!] = context //Pass it to CodingUserInfoKey which is made for that | |
let decodedData = try plistDecoderForArticle.decode([Article].self, from: data) //Decoding init got the managedObjectContext | |
Yes, this was a great example.
@Akhu thanks for sharing this example. Do you have an idea how to handle not only an insert operation but also an updae one? Let's say some data comes from the backend and what it's need is to merge it with the existing CoreData entity.
one option might be using:
override func awakeAfterUsingCoder(aDecoder: NSCoder) -> AnyObject? {
where a decision whether we need to call insertObject
might be made.
I would go for a DTO in that case : https://en.wikipedia.org/wiki/Data_transfer_object
If you want to go deeper in the subject I recommend this book https://www.objc.io/books/core-data/ They explain every scenario even the most complex (sync with network etc.) They also deliver some code example here https://github.com/objcio/core-data/tree/master/Moody/MoodySync . Hope it helps :)
since when is placing coding into a constructor a good practice? where did the single responsibility principle go?
hi everyone I think It should works but it throws me this error dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not a valid property list.". Do you know how to solve it ?
@Akhu thanks for sharing this example. Do you have an idea how to handle not only an insert operation but also an updae one? Let's say some data comes from the backend and what it's need is to merge it with the existing CoreData entity.
Did you find out any good solution for this case? I am looking for the same
@Akhu thanks for sharing this example. Do you have an idea how to handle not only an insert operation but also an updae one? Let's say some data comes from the backend and what it's need is to merge it with the existing CoreData entity.
Did you find out any good solution for this case? I am looking for the same
No. I did not. For a production code I would split into three models DTO and DAO and actual model used elsewhere expect decoding a stream from the Internet and CoreData. For ex.
/// Exposed to the other parts
public struct Article {
let name: String
}
/// Used privately to decode data that comes from the Internet
struct ArticleDTO: Decodable {
let name: String
func toModel() -> Article {
Article(name: name)
}
}
/// Used privately to decode data from CoreData
struct ArticleDAO: NSManagedObject {
@NSManaged var summary:String?
func toModel() -> Article {
Article(name: name)
}
}
Appreciate you putting such a concise example up. Exactly what I was looking for. Thanks.