Search code examples
jsonswiftrealm

Migrating Swift Codable Class to Realm


So I have some JSON data that I pull from an api using alamofire. When the data comes back the structure looks like this

   {
      "sneakers" : {
        "brands" : {
          "Adidas" : [ {
            "brand" : "adidas",
            "category" : "lifestyle",
            "colorway" : "Cream White/Cream White/Core White",
            "description" : "First released on April 29, 2017, the Yeezy Boost 350 V2 ‘Cream White’ combines a cream Primeknit upper with tonal cream SPLY 350 branding, and a translucent white midsole housing full-length Boost. Released again in October 2018, this retro helped fulfill Kanye West’s oft-repeated ‘YEEZYs for everyone’ Twitter mantra, as adidas organized the biggest drop in Yeezy history by promising pre-sale to anyone who signed up on the website. Similar to the first release, the ‘Triple White’ 2018 model features a Primeknit upper, a Boost midsole and custom adidas and Yeezy co-branding on the insole.",
            "designer" : "Kanye West",
            "imagesrc" : "https://image.goat.com/crop/1250/attachments/product_template_additional_pictures/images/014/822/695/original/116662_03.jpg.jpeg",
            "maincolor" : "White",
            "name" : "Yeezy Boost 350 V2 'Cream White / Triple White'",
            "nickname" : "Cream White / Triple White",
            "price" : "Buy New - $220",
            "productlink" : "$190YEEZY BOOST 350 V2 'CREAM WHITE / TRIPLE WHITE'",
            "productlinkhref" : "https://www.goat.com/sneakers/yeezy-boost-350-v2-cream-white-cp9366",
            "releasedate" : "2017-04-29",
            "silhouette" : "Yeezy Boost 350",
            "technology" : "Boost",
            "webscraperorder" : "1554084922-147",
            "webscraperstarturl" : "https://www.goat.com/sneakers"
          }
]
}
}
}

Where there can be potentially more brands( Nike, Air Jordan) that have more sneakers under them. Using Swift Decodable protocol I was able to properly parse the JSON data into objects that are easily readable for any user. This code can be seen below.

import Foundation
import RealmSwift
import Realm


struct Sneaker: Decodable {
    var sneakers : Sneakers

}

struct Sneakers: Decodable {
    let brands: Shoe
}



struct Shoe: Decodable {
    let adidas: [SneakerInfo]
    let nike: [SneakerInfo]
    let airjordan: [SneakerInfo]
    let vans: [SneakerInfo]
    enum CodingKeys: String, CodingKey {
        case adidas = "Adidas"
        case nike = "Nike"
        case airjordan = "Air Jordan"
        case vans = "Vans"
    }
}


struct SneakerInfo: Decodable {
    let brand, category, colorway, description: String?
    let designer: String?
    let imagesrc: String?
    let maincolor, name, nickname, price: String?
    let productlink: String?
    let productlinkhref: String?
    let releasedate, silhouette, technology, webscraperorder: String?
    let webscraperstarturl: String?
    enum CodingKeys: String, CodingKey {
        case brand, category, colorway, description, designer, imagesrc, maincolor, name, nickname, price, productlink, productlinkhref, releasedate, silhouette, technology,webscraperorder,webscraperstarturl
    }
}

Currently I am trying to migrate this same code to be ready to use with realm and codable but I keep getting errors.

My issue is complaining about my implementation and the inits. I will include the class below. I have been stuck on this all day.

import Foundation
import Realm
import RealmSwift

@objcMembers class SneakerTemp: Object, Decodable {
    dynamic var sneaker: SneakersTemp

    enum CodingKeys: String, CodingKey {
        case sneaker
    }

    required init()
    {
        super.init()
    }

 override static func primaryKey() -> String?
    {
        return  UUID().uuidString
    }

    required init(from decoder: Decoder) throws
    {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        sneaker = try container.decode(SneakersTemp.self, forKey: .sneaker)

        super.init()
    }

    required init(value: Any, schema: RLMSchema)
    {
        super.init(value: value, schema: schema)
    }

    required init(realm: RLMRealm, schema: RLMObjectSchema)
    {
        super.init(realm: realm, schema: schema)
    }
}

@objcMembers class SneakersTemp: Object, Decodable {
    dynamic var brands:ShoeTemp
    enum CodingKeys: String, CodingKey {
        case brands
    }

    required init()
    {
        super.init()
    }
 override static func primaryKey() -> String?
{
    return  UUID().uuidString
}

    required init(from decoder: Decoder) throws
    {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        brands = try container.decode(ShoeTemp.self, forKey: .brands)

        super.init()
    }

    required init(value: Any, schema: RLMSchema)
    {
        super.init(value: value, schema: schema)
    }

    required init(realm: RLMRealm, schema: RLMObjectSchema)
    {
        super.init(realm: realm, schema: schema)
    }
}

@objcMembers class ShoeTemp: Object, Decodable {
        let adidas = RealmSwift.List<SneakerInfoTemp>()
        let nike = RealmSwift.List<SneakerInfoTemp>()
        let airjordan = RealmSwift.List<SneakerInfoTemp>()
        let vans = RealmSwift.List<SneakerInfoTemp>()

        enum CodingKeys: String, CodingKey {
            case adidas = "Adidas"
            case nike = "Nike"
            case airjordan = "Air Jordan"
            case vans = "Vans"
        }

        required init(from decoder: Decoder) throws
        {
            let container = try decoder.container(keyedBy: CodingKeys.self)

            adidas = try container.decode(String.self, forKey: .adidas)
            nike = try container.decode(String.self, forKey: .nike)
            airjordan = try container.decode(String.self, forKey: .airjordan)
            vans = try container.decode(String.self, forKey: .vans)

            super.init()
        }

        required init()
        {
            super.init()
        }



override static func primaryKey() -> String?
    {
        return  UUID().uuidString
    }

        required init(value: Any, schema: RLMSchema)
        {
            super.init(value: value, schema: schema)
        }

        required init(realm: RLMRealm, schema: RLMObjectSchema)
        {
            super.init(realm: realm, schema: schema)
        }
    }

 @objcMembers class SneakerInfoTemp: Object, Decodable {
        dynamic var brand: String?
        dynamic var category: String?
        dynamic var colorway: String?
        dynamic var descriptionTemp: String?
        dynamic var designer: String?
        dynamic var imagesrc: String?
        dynamic var maincolor: String?
        dynamic var name: String?
        dynamic var nickname: String?
        dynamic var price: String?
        dynamic var productlink: String?
        dynamic var productlinkhref: String?
        dynamic var releasedate: String?
        dynamic var silhouette: String?
        dynamic var technology: String?
        dynamic var webscraperorder: String?
        dynamic var webscraperstarturl: String?

        enum CodingKeys: String, CodingKey {
            case brand, category, colorway, designer, imagesrc, maincolor, name, nickname, price, productlink, productlinkhref, releasedate, silhouette, technology,webscraperorder,webscraperstarturl
            case descriptionTemp = "description"
        }

        required init(from decoder: Decoder) throws
        {
            let container = try decoder.container(keyedBy: CodingKeys.self)

            brand = try container.decode(String.self, forKey: .brand)
            category = try container.decode(String.self, forKey: .category)
            colorway = try container.decode(String.self, forKey: .colorway)
            descriptionTemp = try container.decode(String.self, forKey: .descriptionTemp)
            designer = try container.decode(String.self, forKey: .designer)
            imagesrc = try container.decode(String.self, forKey: .imagesrc)
            maincolor = try container.decode(String.self, forKey: .maincolor)
            name = try container.decode(String.self, forKey: .name)
            nickname = try container.decode(String.self, forKey: .nickname)

            price = try container.decode(String.self, forKey: .price)
            productlink = try container.decode(String.self, forKey: .productlink)
            productlinkhref = try container.decode(String.self, forKey: .productlinkhref)
            releasedate = try container.decode(String.self, forKey: .releasedate)
            silhouette = try container.decode(String.self, forKey: .silhouette)
            technology = try container.decode(String.self, forKey: .technology)
            webscraperorder = try container.decode(String.self, forKey: .webscraperorder)
            webscraperstarturl = try container.decode(String.self, forKey: .webscraperstarturl)
            super.init()
        }


        required init()
        {
            super.init()
        }

        required init(value: Any, schema: RLMSchema)
        {
            super.init(value: value, schema: schema)
        }

        required init(realm: RLMRealm, schema: RLMObjectSchema)
        {
            super.init(realm: realm, schema: schema)
        }


    }

Main error is

Property 'self.sneaker' not initialized at super.init call

and

Property 'self.brands' not initialized at super.init call


Solution

  • In a class, sneaker and brands need to be initialized or made optional which initializes them to nil. Structs have default memberwise initializers.

    import Foundation
    import Realm
    import RealmSwift
    
    class SneakerTemp: Object, Decodable {
        @objc dynamic var id = UUID().uuidString
        @objc dynamic var sneaker = SneakersTemp()
        // or @objc dynamic var sneaker: SneakersTemp?
        override static func primaryKey() -> String? {
          return "id"
        }
    }
    
    class SneakersTemp: Object, Decodable {
        @objc dynamic var id = UUID().uuidString
        @objc dynamic var brands = ShoeTemp()
        // or @objc dynamic var brands: ShoeTemp?
        override static func primaryKey() -> String? {
          return "id"
        }
    }
    
    class ShoeTemp: Object, Decodable {
        @objc dynamic var id = UUID().uuidString
        let adidas = List<SneakerInfoTemp>()
        let nike = List<SneakerInfoTemp>()
        let airjordan = List<SneakerInfoTemp>()
        let vans = List<SneakerInfoTemp>()
        override static func primaryKey() -> String? {
          return "id"
        }
    
        enum CodingKeys: String, CodingKey {
            case adidas = "Adidas"
            case nike = "Nike"
            case airjordan = "Air Jordan"
            case vans = "Vans"
        }
    }
    
    class SneakerInfoTemp: Object, Decodable {
        @objc dynamic var brand: String?
        @objc dynamic var category: String?
        @objc dynamic var colorway: String?
        @objc dynamic var descriptionTemp: String?
        @objc dynamic var designer: String?
        @objc dynamic var imagesrc: String?
        @objc dynamic var maincolor: String?
        @objc dynamic var name: String?
        @objc dynamic var nickname: String?
        @objc dynamic var price: String?
        @objc dynamic var productlink: String?
        @objc dynamic var productlinkhref: String?
        @objc dynamic var releasedate: String?
        @objc dynamic var silhouette: String?
        @objc dynamic var technology: String?
        @objc dynamic var webscraperorder: String?
        @objc dynamic var webscraperstarturl: String?
    
        enum CodingKeys: String, CodingKey {
            case brand, category, colorway, designer, imagesrc, maincolor, name, nickname, price, productlink, productlinkhref, releasedate, silhouette, technology,webscraperorder,webscraperstarturl
            case descriptionTemp = "description"
        }
    }