Louis Lac
Louis Lac

Reputation: 6436

Swift Codable - How to make types with references to each other codable?

I got two classes like this:

class Session {
  var name: String
  var data: [Double]
  var routes: [Route]

  init(named name: String, data: [Double] = [], routes: [Route] = []) {
    self.name = name
    self.data = data
    self.routes = routes
  }
}
class Route {
  var session: Session
  var startIndex: Int
  var endIndex: Int

  // A route stores only references to the real underlying data stored in a Session
  var data: [Double] { Array(session.data[startIndex...endIndex]) }

  init(session: Session, startIndex: Int, endIndex: Int) {
    self.session = session
    self.startIndex = startIndex
    self.endIndex = endIndex
  }
}

Note that a Route object cannot be initialised without a corresponding Session, it always depends on it. The goal is to store in Route only references to the real data stored in the Session object.

First, is there a potential retain cycle in this pattern, and how to break it if there is one?

Second question: I want to make a Session Codable. The problem is as Session relies on Route and Route on Session I can't make one Codable without making the other too and I fall into an endless loop.

I don't necessarily require that Route conforms to codable as a route will always depends on a Session, an exemple JSON would be:

{
  "name": "Session Name"
  "data": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  "routes": [
    {
      "start_index": 3
      "end_index": 8
    }
    ...
  ]
}

From this I would be able to recover a Session from a JSON.


I tried:

Upvotes: 1

Views: 440

Answers (1)

InkGolem
InkGolem

Reputation: 2762

What you've modeled is a circular dependency and can't be represented in JSON. The easiest solution is to change the session property on route.

weak var session: Session?

This prevents a retain cycle and makes it so that session isn't required in Route.init. With that change in place you can then loop through all the Routes at the end of your Session initializer and assign the session.

The downside to this solution is that you need to write your own initializers, but it sounds like you were experimenting with that anyway.

Upvotes: 3

Related Questions