Anviking / Decodable

[Probably deprecated] Swift 2/3 JSON unmarshalling done (more) right
MIT License
1.04k stars 73 forks source link

Inverse relationship mapping #53

Open valeriomazzeo opened 8 years ago

valeriomazzeo commented 8 years ago

How would you map an inverse relationship using decodable?

Think of the following scenario:

A <-->> B

With A being in a to-many relationship with B and B being in a to-one relationship with A.

When trying to decode A (assuming an array of B objects are contained in the payload), the decoding of B will be triggered as well.

I can't think of a way using decodable for populating the B's A reference (inverse relationship).

In theory all will be needed is to pass Self (A) to the mapping of B (in the decode function is the only thing I can think of), then having B recognise that object, maybe as root or parent and assign it to the A's reference it holds.

Anviking commented 8 years ago

decodeArray can be used since it takes takes a elementDecodeClosure

class A: Decodable {
    var array: [B] = []

    required init(json: AnyObject) throws {
        self.array = try decodeArray(B.decode(a: self))(json: json)
    }

    static func decode(json: AnyObject) throws -> Self {
        return try self.init(json: json)
    }
}

class B {
    weak var a: A?
    let name: String

    required init(name: String) {
        self.name = name
    }

    static func decode(a a: A)(json: AnyObject) throws -> Self {
        let b = try self.init(name: String.decode(json))
        b.a = a
        return b
    }
}

let array: NSArray = ["San Fransisco", "Sparks", "Snow"]
let a = try A.decode(array)
a === a.array[1].a
valeriomazzeo commented 8 years ago

What if it's not an array and just a one-to-one relationship?

Can't we have an overloaded version of

public static func decode(j: AnyObject) throws -> NSDictionary {

that takes an elementDecodeClosure as well?

Anviking commented 8 years ago

How do you mean?

Anviking commented 8 years ago

Oh, sorry, was confused by the latter part. You perhaps figured it out by now, but with one-to-one relationships we can just write a custom decode or init function for B (still not Decodable). In this case, if we're not using it with decodeArray, the function does not need to be curried.

But in the (curried) example above it would be:

// Instead of: self.array = try decodeArray(B.decode(a: self))(json: json)
self.b = try B.decode(a: self)(json: json)

And without currying, perhaps this order would be nicer:

self.b = try B.decode(json, a: self)