Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flat Json, CoreData, support Optional and Requeried fields #15

Open
TofPlay opened this issue Feb 2, 2018 · 0 comments
Open

Flat Json, CoreData, support Optional and Requeried fields #15

TofPlay opened this issue Feb 2, 2018 · 0 comments

Comments

@TofPlay
Copy link

TofPlay commented Feb 2, 2018

Can you generate something like that?

JSON:

{
    "id": 42,
    "name": "nixzhu",
    "twitter": {
        "profile_url": "https://twitter.com/nixzhu",
        "created_at": "2009-05-12T10:25:43.511Z"
    }
}

Class Codable:

import Foundation

// "XXX" prefix of each class of the application
public class XXXUser: Codable {
    
    public enum CodingKeys: String, CodingKey {
        // Fields define on the json
        case id
        case name
        case twitter

        // Fields for flat version
        case profileUrl
        case createdAt
    }

    // Use only internally to extact sub-structs
    private class Twitter: Codable {
        public enum CodingKeys: String, CodingKey {
            case profileUrl = "profil_url"
            case createdAt = "created_at"
        }

        public var profileUrl: String = ""
        public var createdAt: Date = Date.distantPast
    }
    
    public var id:Int16 = 0
    public var name:String = ""
    public var profileUrl: String = ""
    public var createdAt: Date = Date.distantPast
    
    public init() {
    }
    
    // MARK: -> Public protocol Encodable
    
    public func encode(to pEncoder: Encoder) throws {
        var lContainer = pEncoder.container(keyedBy: CodingKeys.self)
        
        try lContainer.encode(self.id, forKey: .id)
        try lContainer.encode(self.name, forKey: .name)
        try lContainer.encode(self.profileUrl, forKey: .profileUrl)
        try lContainer.encode(self.createdAt, forKey: .createdAt)
    }

    // MARK: -> Public protocol Decodable
    
    public required init(from pDecoder: Decoder) throws {
        if let lContainer = try? pDecoder.container(keyedBy: CodingKeys.self) {
            var lInvalidFields:[String] = []
            
            // Required
            if let lId = try? lContainer.decode(Int.self, forKey: .id) {
                self.id = lId
            } else {
                lInvalidFields.append(CodingKeys.id.stringValue)
            }
            
            // Required
            if let lName = try? lContainer.decode(String.self, forKey: .name) {
                self.name = lName
            } else {
                lInvalidFields.append(CodingKeys.name.stringValue)
            }
            
            // Optional
            if let lTwitter = try? lContainer.decode(Twitter.self, forKey: .twitter) {
                self.profileUrl = lTwitter.profileUrl
                self.createdAt = lTwitter.createdAt
            }
            
            // Store object in CoreData when all required fields are valid
            // otherwise ignore record and generate an output on debug mode
            if lInvalidFields.isEmpty {
                CDUser.setup(id: self.id, name: self.name, profileUrl: self.profileUrl, createdAt: self.createdAt)
            } else {
                #if DEBUG
                    // lData.json format Data to a json string
                    if let lData = try? JSONEncoder().encode(self), let lJsonValue = lData.json(format: true, padding: "  ") {
                        print("\n\(type(of: self)): Invalid fields:\n  [\(lInvalidFields.joined(separator: ","))]\nValue:\n\(lJsonValue)\n")
                    }
                #endif
            }
        } else {
            #if DEBUG
                print("\(type(of: self)): Invalid data")
            #endif
        }
    }
}

Class CoreData:

import Foundation
import CoreData

// "CD" for CoreData 
@objc(CDUser)
open class CDUser: NSManagedObject {
    
    @NSManaged public var id: Int16
    @NSManaged public var name: String
    @NSManaged public var profileUrl: String
    @NSManaged public var createdAt: Date

    public static func setup(id pId: Int16,
                             name pName: String,
                             profileUrl pProfileUrl: String,
                             createdAt pCreatedAt: Date)
    {
        // App.database is a CoreData layer abstraction object
        
        // Check if the object exist
        guard let lInstance = App.database.objectFor(entity: "CDUser", id: pId) as? CDUser else {
            
            // Object doesn't exists, create a new CoreData instance of CDUser
            let lNew = App.database.instance(CDUser.self)
            
            // Set properties of new CoreData object of CDUser
            lNew.properties(id: pId, name: pName, profileUrl: pProfileUrl, price: pPrice, position: pPosition)
            return
        }
        
        // Object already exists, we only update his properties
        lInstance.properties(id: pId, name: pName, profileUrl: pProfileUrl, createdAt: pCreatedAt)
    }
    
    public func properties(id pId: Int16,
                           name pName: String,
                           profileUrl pProfileUrl: String,
                           createdAt pCreatedAt: Date)
    {
        self.id = pId
        self.name = pName
        self.profileUrl = pProfileUrl
        self.createdAt = pCreatedAt
        
        // Save data in CoreData
        App.database.save()
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant