I am trying to update @Published
arrays in separate view models, one view model is a singleton whilst the other is not as there can be multiple instances.
- The purpose of the code below is to allow a user to save or unsave
movie recommendations to their personal movie collection
(
savedMovies
). - The
MyMovies
class is a singleton, this is intentional as there is only one user. - The
MovieShop
class is not a singleton as there can be multiple movie shops and therefore multiple instances. - When a user presses "Save" on a movie name, the movie should be added to their saved movies (MyMovies class) and the movie must be shown as "Saved" in the Movie Recommendation list (MovieShop class).
- When the movie is removed from the users
savedMovie
array, in all movie shops (only one shown in the example code) the movie that was removed from thesavedMovie
array must have itssaved
state reset tofalse
so so it can be saved again (by the user pressing "Save").
The implementation below works when pressing the save/unsave button for each movie in the movie recommendations view but not for the users saved movies view (as it removes the movie entirely).
My question is this:
I have been looking at the Apple Developer Docs in an attempt to understand the correct way to go about doing this when working within the SwiftUI framework. So far I haven't had much luck finding an answer. Is the proposed solution within the Apple Guidelines or is there another way in which i should go about solving this problem?
Any help would be greatly appreciated.
// Movie type
struct Movie: Hashable {
let name: String
var saved: Bool
}
@MainActor final class MyMovies: ObservableObject {
@Published var savedMovies: [Movie] = [Movie(name: "Ice Age", saved: true)]
static let shared = MyMovies()
private init() {}
// function to remove a movie from the users saved movies
func unsaveMovie(_ movie: Movie) {
savedMovies.removeAll(where: {$0.name == movie.name})
// Need to also toggle saved state of unsaved/removed movie so
// its state is set to "Save" in movie shop (not sure how)
}
}
@MainActor class MovieShop: ObservableObject {
@Published var recommendations: [Movie] = [
Movie(name: "Ice Age", saved: true),
Movie(name: "Star Wars", saved: false),
]
// function to save or unsave a movie depending on if it is
// already in the users saved movies array or not
func toggleMovieSaved(_ movie: Movie) {
let index = recommendations.firstIndex(where: {$0.name == movie.name})!
recommendations[index].saved.toggle()
// Check if user already has the movie saved
let alreadySaved = MyMovies.shared.savedMovies.contains(where: {$0.name == movie.name})
if alreadySaved {
// Remove movie from users saved movies
MyMovies.shared.savedMovies.removeAll(where: {$0.name == movie.name})
} else {
// Save/Add movie to users saved movies
var newMovie = movie
newMovie.saved = true
MyMovies.shared.savedMovies.append(newMovie)
}
}
}
// I have combined the movie shop view and user saved movies view into one
// for demo purposes
struct ContentView: View {
@StateObject private var myMovies = MyMovies.shared
@StateObject private var movieShop = MovieShop()
var body: some View {
VStack {
Text("My Saved Movies")
.font(.title)
ForEach(myMovies.savedMovies, id: \.name) { movie in
HStack {
Text(movie.name)
Button("Remove") {
myMovies.unsaveMovie(movie)
}
}
}
Divider()
Text("Movie Recommendations")
.font(.title)
ForEach(movieShop.recommendations, id: \.name) { movie in
HStack {
Text(movie.name)
Button(movie.saved ? "Unsave" : "Save") {
movieShop.toggleMovieSaved(movie)
}
}
}
}
}
}