Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Codable protocol with reference types?

Example:

import Foundation

class Player: Codable {
   let name: String
   init(name: String) {
      self.name = name
   }
}

class Team: Codable {
   let players: [Player]
   let capitan: Player
   init(players: [Player], capitan: Player) {
      self.players = players
      self.capitan = capitan
   }
}

let player1 = Player(name: "p1")
let player2 = Player(name: "p2")

let team = Team(players: [player1, player2], capitan: player1)

print(team.players[0] === team.capitan) // true

let encoder = JSONEncoder()
let data = try encoder.encode(team)

let decoder = JSONDecoder()
let team2 = try decoder.decode(Team.self, from: data)

print(team2.players[0] === team2.capitan) // false

Output:

true
false

How to use Codable protocol with reference types?

The behavior may change in the future? https://github.com/apple/swift-evolution/blob/master/proposals/0167-swift-encoders.md

like image 463
rtnm Avatar asked Apr 05 '26 16:04

rtnm


2 Answers

This is fundamental to JSON, not Codable. JSON is an encoding of values. It does not keep track of references; it's not part of the format. If you want to keep track of references, you would have to store that in the JSON yourself, by encoding objectIdentifier. You'd then need to join up things that have the same identifier. But that's beyond the scope of Codable, since the underlying format doesn't have the concept. You'd need to create some other format (perhaps that used JSON as its underlying value format, in the way that NSKeyedArchiver uses property lists as its underlying value format).

JSON isn't an object graph storage format; as I said it's a value encoding. If you want an object graph, that's what NSKeyedArchiver is for. If you want a non-Cocoa solution, you would need to implement a new Encoder and Decoder that implemented the same basic idea as the archivers. That isn't what JSONEncoder is for. But I'd use NSKeyedArchiver if possible; it's designed for this.

Note that SE-0167 says that there is a new encodeCodable method on NSKeyedArchiver that is supposed to let you intermix Codable types with NSCoding types (and allow Swift types to participate in NSKeyedArchiver), but I don't see it in Xcode 9 beta 1.

like image 134
Rob Napier Avatar answered Apr 08 '26 04:04

Rob Napier


It seems like the encoder currently does not respect references and instead saves a separate instance of the same object each time it is referenced, which is why === fails. You can change it to == and implement Equatable for your classes which should fix this, or as the comment on the question states, use structs.

like image 22
Dima Avatar answered Apr 08 '26 06:04

Dima



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!