Search code examples
swiftcompiler-errorsdecodable

Custom Decoder error due to self before all stored properties are initialized


I have the following Decodable model:

struct VideoResponse: Decodable {
    let videos: [Video]
    let lastVideos: [Video]

    enum CodingKeys: String, CodingKey {
        case videos
    } 

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
    
        let videos = try container.decode([Video].self, forKey: .videos)
        self.videos = sort(videos)
        self.lastVideos = computeLastVideos(from: self.videos)
    }
}

The above does not compile since I'm trying to use both sort and compute(from:) before before all stored properties are initialized.

'self' used before all stored properties are initialized.

To fix the problem I could indeed move the logic within the the two method inside init(from:) decoder. I would avoid it since the VideoResponse could be used as a plain struct.

extension VideoReponse {
    init(videos: [Videos]) { 
        // same setup here...
    }
}

Any idea on how to fix this in order to avoid duplicated code?


Solution

  • You can't call methods on self before stored properties are all initialised because the methods could potentially access an uninitialised property (See also this answer of mine), and who knows what happens then? You can tell Swift that sort and computeLastVideos won't access self at all, by putting the word static:

    static func sort(_ videos: [Video]) -> [Video] { ... }
    static func computeLastVideos(from videos: [Video]) -> [Video] { ... }
    

    You would also have to put the sorted videos into a temporary variable sortedVideos first, because you can't access self.videos:

    let container = try decoder.container(keyedBy: CodingKeys.self)
    let videos = try container.decode([Video].self, forKey: .videos)
    let sortedVideos = VideoResponse.sort(videos)
    self.lastVideos = VideoResponse.computeLastVideos(from: sortedVideos)
    self.videos = sortedVideos
    

    If sort and computerLastVideos does access self though, you are out of luck, and have to make videos a non-let, and initialise it first, then change it later. You can't guarantee to Swift that sort and computeLastVideos will only access the initialised part of self.