zerohedge
zerohedge

Reputation: 3725

Swift: Why doesn't JSONDecoder (Codable/Decodable) call the init() method of the struct?

I feel there's something I must be missing. Swift 5.1.

I have the following struct which is set to be Decodable so I can parse an external JSON URL:

struct ReleaseGroup: Decodable, Identifiable {
  let id: Int
  let externalServiceID: String
  let title: String
  let externalServiceImageURL: URL
  let type: String
  var image: UIImage?

  private enum CodingKeys: String, CodingKey {
    case id
    case externalServiceID = "external_service_id"
    case title
    case externalServiceImageURL = "external_service_image_url"
    case type
  }

  // do I have to do this just because UIImage is an optional?
  init(id: Int, externalServiceID: String, title: String, externalServiceImageURL: URL, type: String) {
    let url = URL(string: "https://someurl.com/some-image.jpeg")
    let data = try? Data(contentsOf: url!)
    self.image = UIImage(data: data!)
    self.id = id
    self.externalServiceID = externalServiceID
    self.title = title
    self.type = type
    self.externalServiceImageURL = externalServiceImageURL
  }
}

I've also defined this piece of code elsewhere, which is responsible for actually creating the instances from the API call:

init() {
  guard let url = URL(string: "http://127.0.0.1:8000/api/releases/") else { return }
  URLSession.shared.dataTask(with: url) { (data, _, _) in

    guard let data = data else { return }

    let releaseGroups = try! JSONDecoder().decode([ReleaseGroup].self, from: data)
    DispatchQueue.main.async {
        self.releaseGroups = releaseGroups
      }
    }
    }.resume()
}

However, the above doesn't call the init() method of ReleaseGroup, which I thought is called any time an instance of ReleaseGroup is created.

If I explicitly create them though, it works:

for releaseGroup in releaseGroups {
          let rg = ReleaseGroup.init(id: releaseGroup.id, externalServiceID: releaseGroup.externalServiceID, title: releaseGroup.title, externalServiceImageURL: releaseGroup.externalServiceImageURL, type: releaseGroup.type)
            self.releaseGroups.append(rg)
        }

Upvotes: 1

Views: 708

Answers (1)

Joakim Danielson
Joakim Danielson

Reputation: 51892

The decoder will call init(from:) which is part of the Decodable protocol. This init is synthesized so if you need to do something special with your object construction this is the init method you need to implement yourself.

See this article from Apple for more information

Upvotes: 2

Related Questions