My app gets its data from an API endpoint.
The data is a Review and the model looks like this:
struct Review: Identifiable, Decodable {
var id: Int
var reviewable_type: String
var score: Int?
var reviewable: <THIS CAN BE A MOVIE, A SHOW OR AN EPISODE>
var user: User
var created_at: String?
var created: String?
var text: String?
}
As you can see the reviewable
property can be of multiple types.
How do I implement this? I have different data models for Movie, Show and Episode.
Note: the reviewable_type holds the value of the type that's in the reviewable
variable. I don't know if that helps.
I tried things with protocols and extensions, but can't seem to get it to work.
I like AntonioWar's answer if the objects all have the same fields, which they don't. Maybe it still works that way but it wasn't clear to me from his answer.
What I did to solve it was this: I created a Reviewable
struct with all the fields from the Show
, Episode
and Movie
structs:
struct Reviewable : Decodable {
var id: Int
var tmdb_id: Int?
var name: String?
var show_id: Int?
var season: String?
var number: String?
var type: String?
var airdate: String?
var airtime: String?
var airstamp: String?
var average_rating: Float?
var vote_average: String?
var vote_count: String?
var image_medium: String?
var image_original: String?
var summary: String?
var watched_by_users: [User]?
var show: Show?
var comments_count: Int?
var likes_count: Int?
var episode_number: String?
var new_airstamp: String?
var backdrop_image: String?
var poster_image: String?
var likes: [Like]?
var reviews: [Review]?
var imdb_id: String?
var adult: Int?
var backdrop_path: String?
var budget: String?
var homepage: String?
var original_language: String?
var original_title: String?
var tagline: String?
var title: String?
var overview: String?
var popularity: String?
var poster_path: String?
var release_date: String?
var trending: String?
var revenue: String?
var status: String?
var movie_users: [MovieUser]?
var tv_maze_id: Int?
var tvrage_id: String?
var thetvdb_id: String?
var original_name: String?
var language: String?
var origin_country: String?
var average_runtime: Int?
var premiered: String?
var schedule: String?
var updated: String?
var episodes: [Episode]?
var tracked_by: [User]?
}
Then, I added this as the type in the Review
struct:
struct Review: Identifiable, Decodable {
var id: Int
var reviewable_type: String
var score: Int?
var reviewable: Reviewable
var user: User
var created_at: String?
var created: String?
var text: String?
}
Then, I created extensions for the 3 Reviewable
objects, with a function that converts the Reviewable
to the Movie
, Episode
or Show
:
extension Movie {
static func fromReviewable(reviewable: Reviewable) -> Movie {
let movie = Movie(
id: reviewable.id,
tmdb_id: reviewable.tmdb_id,
imdb_id: reviewable.imdb_id,
adult: reviewable.adult,
backdrop_path: reviewable.backdrop_path,
budget: reviewable.budget,
homepage: reviewable.homepage,
original_language: reviewable.original_language,
original_title: reviewable.original_title,
tagline: reviewable.tagline,
title: reviewable.title,
overview: reviewable.overview,
popularity: reviewable.popularity,
poster_path: reviewable.poster_path,
release_date: reviewable.release_date,
trending: reviewable.trending,
revenue: reviewable.revenue,
status: reviewable.status,
vote_average: reviewable.vote_average,
vote_count: reviewable.vote_count,
likes: reviewable.likes,
movie_users: reviewable.movie_users,
reviews: reviewable.reviews,
comments_count: reviewable.comments_count,
likes_count: reviewable.likes_count
)
return movie
}
}
Because I know which type it is from the reviewable_type
variable on the Review
, I can use this to decode it in to the right struct:
if(review.reviewable_type == "App\\Models\\Movie") {
let movie = Movie.fromReviewable(reviewable: review.reviewable!)
} else if(review.reviewable_type == "App\\Models\\Show") {
let show = Show.fromReviewable(reviewable: review.reviewable!)
} else if(review.reviewable_type == "App\\Models\\Episode") {
let episode = Episode.fromReviewable(reviewable: review.reviewable!)
}
Maybe it is a very shabby way of doing it but this is what worked for me (and maybe someone else can use it).