Reputation: 303
I am trying to make an Actor
class as well as a Movie
class that can be decoded from a JSON file. There is also a dictionary, allActors
, containing each Actor
instance so that each movie that is decoded references the same Actor
instance. Also, when the Actor
is added to the Movie
's cast
array, the Movie
is appended to the Actor
's filmography
array. Here is the code I have so far:
public var allActors = [String: Actor]()
public final class Actor {
public let name: String
public var filmography: [Movie]
public init(name: String, filmography: [Movie] = Array()) {
self.name = name
self.filmography = filmography
}
}
public final class Movie: Codable {
public let title: String
public var cast: [Actor]
enum CodingKeys: String, CodingKey {
case title
case cast
}
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
title = try values.decode(String.self, forKey: .title)
cast = Array() // fourth line of this initializer
cast = try values.decode([String].self, forKey: .cast).map {
let actor: Actor
if let recurringActor = allActors[$0] {
actor = recurringActor
} else {
let newActor = Actor(name: $0)
allActors[$0] = newActor
actor = newActor
}
actor.filmography.append(self)
return actor
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(title, forKey: .title)
try container.encode(cast.map {$0.name}, forKey: .cast)
}
}
This code works, however, at the fourth line of the Movie
's init(from:)
initializer, cast
is set to an empty array. This seems redundant, but if I remove it, I get the following error:
'self' captured by a closure before all members were initialized
Is there a way I can avoid this error? I'm not using any properties of self
, so I don't see why it shouldn't be possible to use the Movie
's reference in a closure.
Upvotes: 0
Views: 162
Reputation: 236340
Not a direct answer to your question but you can simply decode your movies, reduce it into a dictionary and map its key/value pairs into your actors array:
struct Movie: Codable {
let title: String
let cast: [String]
}
struct Actor: CustomStringConvertible {
let name: String
let filmography: [String]
var description: String { "Name: \(name) - Movies: \(filmography)" }
}
Playground testing:
let movies = try! JSONDecoder().decode([Movie].self, from: Data(json.utf8))
let actors = movies.reduce(into: [:]) { dict, movie in
for name in movie.cast {
dict[name, default: []].append(movie.title)
}
}.map(Actor.init)
print(actors)
This will print:
[Name: Kevin Bacon - Movies: ["Diner", "Footloose", "Flatliners"], Name: Mark Ruffalo - Movies: ["Spotlight"], Name: John Laughlin - Movies: ["Footloose"], Name: Beth Grant - Movies: ["Flatliners"], Name: Liev Schreiber - Movies: ["Spotlight"], Name: John Slattery - Movies: ["Spotlight"], Name: Paul Guilfoyle - Movies: ["Spotlight"], Name: Len Cariou - Movies: ["Spotlight"], Name: Neal Huff - Movies: ["Spotlight"], Name: Colette Blonigan - Movies: ["Diner"], Name: Frances Lee McCain - Movies: ["Footloose"], Name: Dianne Wiest - Movies: ["Footloose"], Name: Ellen Barkin - Movies: ["Diner"], Name: Michael Tucker - Movies: ["Diner"], Name: Rachel McAdams - Movies: ["Spotlight"], Name: Joshua Rudoy - Movies: ["Flatliners"], Name: Jamey Sheridan - Movies: ["Spotlight"], Name: Patricia Belcher - Movies: ["Flatliners"], Name: Stanley Tucci - Movies: ["Spotlight"], Name: Hope Davis - Movies: ["Flatliners"], Name: Maureen Keiller - Movies: ["Spotlight"], Name: Tim Daly - Movies: ["Diner"], Name: Billy Crudup - Movies: ["Eat Pray Love", "Spotlight"], Name: Steve Guttenberg - Movies: ["Diner"], Name: Hadi Subiyanto - Movies: ["Eat Pray Love"], Name: Mike O 'Malley - Movies: ["Eat Pray Love"], Name: Brian d'Arcy James - Movies: ["Spotlight"], Name: Gene Amoroso - Movies: ["Spotlight"], Name: Rushita Singh - Movies: ["Eat Pray Love"], Name: Julia Roberts - Movies: ["Flatliners", "Eat Pray Love"], Name: Kiefer Sutherland - Movies: ["Flatliners"], Name: Clement Fowler - Movies: ["Diner"], Name: Michael Cyril Creighton - Movies: ["Spotlight"], Name: Kimberly Scott - Movies: ["Flatliners"], Name: Viola Davis - Movies: ["Eat Pray Love"], Name: Sophie Thompson - Movies: ["Eat Pray Love"], Name: Javier Bardem - Movies: ["Eat Pray Love"], Name: Tuva Novotny - Movies: ["Eat Pray Love"], Name: John Lithgow - Movies: ["Footloose"], Name: Laurie Heineman - Movies: ["Spotlight"], Name: Tim Progosh - Movies: ["Spotlight"], Name: Arlene Tur - Movies: ["Eat Pray Love"], Name: Lynne Marta - Movies: ["Footloose"], Name: Jim Youngs - Movies: ["Footloose"], Name: Daniel Stern - Movies: ["Diner"], Name: James Franco - Movies: ["Eat Pray Love"], Name: Gita Reddy - Movies: ["Eat Pray Love"], Name: Jessica James - Movies: ["Diner"], Name: Kelle Kipp - Movies: ["Diner"], Name: Christine Hakim - Movies: ["Eat Pray Love"], Name: Douglas Dirkson - Movies: ["Footloose"], Name: Kathryn Dowling - Movies: ["Diner"], Name: William Baldwin - Movies: ["Flatliners"], Name: Benjamin Mouton - Movies: ["Flatliners"], Name: Sarah Jessica Parker - Movies: ["Footloose"], Name: Lori Singer - Movies: ["Footloose"], Name: Paul Reiser - Movies: ["Diner"], Name: Chris Penn - Movies: ["Footloose"], Name: Oliver Platt - Movies: ["Flatliners"], Name: Michael Keaton - Movies: ["Spotlight"], Name: Mickey Rourke - Movies: ["Diner"], Name: Claudia Cron - Movies: ["Diner"], Name: Richard Jenkins - Movies: ["Eat Pray Love", "Spotlight"], Name: Luca Argentero - Movies: ["Eat Pray Love"]]
Upvotes: 0
Reputation: 534987
You can’t say
actor.filmography.append(self)
until self
is fully initialized. That doesn’t happen until its cast
property has a value. (And all its other properties too of course.) That fully explains the phenomenon.
Upvotes: 2