Search code examples
iosswiftxcodeswift5

Swift model not reading data from Firestore


I have a base model called Requirements and another more specific model called AccountRequirements.

When I try to read the currentDeadline property, if i use Requirements it works fine. If I use AccountRequirements it comes out as nil.

I do not understand why. I'm guessing it has to do somehow with the class. I always use struct in my models but since I can not inherit from a struct I'm using class here.

class Requirements: Codable {
    var commonProperty: String

    // works
    var currentDeadline: Int?
    
    enum CodingKeys: String, CodingKey {
        case commonProperty = "common_property"

        case currentDeadline = "current_deadline"
    }
}

class AccountRequirements: Requirements {
    // doesnt work 
    var currentDeadline: Int?
    
    enum CodingKeys: String, CodingKey {
        case currentDeadline = "current_deadline"
    }
}

I decode data like this:

documentReference.addSnapshotListener { [self] documentSnapshot, error in
    guard let document = documentSnapshot else {
        self.error = error!.localizedDescription
        return
    }
    
    self.user = try? document.data(as: Requirements.self)
}

Solution

  • If you want to decode it as the subclass then you need to give that class and not the superclass to document.data(as:). You also need to implement init(from:) for the subclass to decode it properly

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        currentDeadline = try container.decodeIfPresent(Int.self, forKey: .currentDeadline)
        try super.init(from: decoder)
    }
    

    Below is an example with a hardcoded json value

    let data = """
        { "common_property": "Some text",
          "current_deadline": 42
        }
        """.data(using: .utf8)!
    
    
    do {
        let result = try JSONDecoder().decode(Requirements.self, from: data)
        print(type(of: result), result.commonProperty)
        let result2 = try JSONDecoder().decode(AccountRequirements.self, from: data)
        print(type(of: result2), result2.commonProperty, result2.currentDeadline ?? "")
    } catch {
        print(error)
    }
    

    Requirements Some text
    AccountRequirements Some text 42