I have a Song View where I can add or remove a song as favourite, all works well and the update takes place, now what I want is a View where I can see list of all songs that are favourites, so I use an @ObservedObject property wrapper and then use it to display the list …
The problem comes when I go to a songs page and add/remove a song as favourite, the favourite View is unable to redraw it self and continues to show old list of favourites, however if I restart the app it works, how can I overcome this, the @ObservedObject wrapper must be watching any changes to the @Published property which the array of songs is , so why is it not working instantly, thanks …
Where I define ObservableObject class
struct Song: Identifiable, Codable {
var id = UUID()
var name: String
var album: String
var isFavorite: Bool
var genre: String
var artist: String
init(name: String, album: String, isFavorite: Bool, genre: String, artist: String) {
self.name = name
self.album = album
self.artist = artist
self.genre = genre
self.isFavorite = isFavorite
}
static let `default` = Song(name: "demo", album: "demo", isFavorite: false, genre: "demo", artist: "demo")
}
class Songs: ObservableObject {
var songsData: [Song] =
[
Song(name: "demo", album: "demo", isFavorite: false, genre: "demo", artist: "demo"),
Song(name: "demo", album: "demo", isFavorite: false, genre: "demo", artist: "demo"),
Song(name: "demo", album: "demo", isFavorite: false, genre: "demo", artist: "demo"),
Song(name: "demo", album: "demo", isFavorite: false, genre: "demo", artist: "demo"),
Song(name: "demo", album: "demo", isFavorite: false, genre: "demo", artist: "demo"),
]
@Published var songs = [Song]() {
didSet {
if let encoded = try? JSONEncoder().encode(songs) {
UserDefaults.standard.set(encoded, forKey: "demosongs")
}
}
}
init(){
if let savedItems = UserDefaults.standard.data(forKey: "demosongs") {
if let decodedItems = try? JSONDecoder().decode([Song].self, from: savedItems) {
songs = decodedItems
return
}
}
songs = songsData
}
}
Where I add/remove favourites
struct SongView: View {
@Binding var song: Song
var body: some View {
Text("\(song.name)")
Button(song.isFavorite ? "Remove from favorite" : "add to favorites") {
song.isFavorite.toggle()
}
Text("Hello World")
}
}
where I try and display favourites
struct FavoritesView: View {
@ObservedObject var songs: Songs
var body: some View {
List(songs.songs) { song in
if song.isFavorite {
Text(song.name)
}
}
}
}
// How I use SongView
struct AlbumView: View {
@ObservedObject var songData: Songs
var body: some View {
ZStack {
Image("cover")
.resizable().opacity(0.5)
.zIndex(1)
ScrollView {
VStack {
ForEach($songData.songs) { $song in
NavigationLink {
SongView(song: $song)
} label: {
customText(image: "joinus", str: song.name)
.frame(maxWidth: .infinity)
}
}
}.padding()
}.zIndex(2)
}
}
func customText(image: String, str: String) -> some View {
Group {
VStack {
RoundedRectangle(cornerRadius: 20)
.fill(.blue)
.padding()
.frame(width: 300, height: 150)
.overlay(Image(image)
.resizable()
.frame(width: 300, height: 150))
Text(str)
.scaledFont(name: "Gothic", size: 20)
.foregroundColor(.white)
.padding()
.background(RoundedRectangle(cornerRadius: 20, style: .continuous).fill(Color.gray))
}
}
}
}
CodePudding user response:
Here is some example code that lets you change the song.isFavorite
and
have it reflected in AlbumView
. It shows AlbumView
is updated as you
press song.isFavorite.toggle()
in SongView
. It really depends on how you use your code.
struct ContentView: View {
@StateObject var songs = Songs() // <-- here
var body: some View {
NavigationView { // <-- here
AlbumView(songData: songs) // <-- here
}
}
}
struct AlbumView: View {
@ObservedObject var songData: Songs
var body: some View {
ZStack {
Image("cover")
.resizable().opacity(0.5)
.zIndex(1)
ScrollView {
VStack (spacing: 44) {
ForEach($songData.songs) { $song in
NavigationLink {
SongView(song: $song)
} label: {
// for testing
Text(song.name) Text(song.isFavorite ? " is favorite" : " is NOT favorite")
// customText(image: "joinus", str: song.name)
// .frame(maxWidth: .infinity)
}
}
}.padding()
}.zIndex(2)
}
}
func customText(image: String, str: String) -> some View {
Group {
VStack {
RoundedRectangle(cornerRadius: 20)
.fill(.blue)
.padding()
.frame(width: 300, height: 150)
.overlay(Image(image)
.resizable()
.frame(width: 300, height: 150))
Text(str)
// .scaledFont(name: "Gothic", size: 20)
// .foregroundColor(.white)
.padding()
.background(RoundedRectangle(cornerRadius: 20, style: .continuous).fill(Color.gray))
}
}
}
}
struct SongView: View {
@Binding var song: Song
var body: some View {
VStack {
// -- here for testing
Text(song.name) Text(song.isFavorite ? " is favorite" : " is NOT favorite")
Button(song.isFavorite ? "Remove from favorite" : "add to favorites") {
song.isFavorite.toggle()
}
}
}
}
struct Song: Identifiable, Codable {
var id = UUID()
var name: String
var album: String
var isFavorite: Bool
var genre: String
var artist: String
init(name: String, album: String, isFavorite: Bool, genre: String, artist: String) {
self.name = name
self.album = album
self.artist = artist
self.genre = genre
self.isFavorite = isFavorite
}
static let `default` = Song(name: "demo", album: "demo", isFavorite: false, genre: "demo", artist: "demo")
}
class Songs: ObservableObject {
var songsData: [Song] =
[
Song(name: "demo1", album: "demo", isFavorite: false, genre: "demo", artist: "demo"),
Song(name: "demo2", album: "demo", isFavorite: true, genre: "demo", artist: "demo"),
Song(name: "demo3", album: "demo", isFavorite: false, genre: "demo", artist: "demo"),
Song(name: "demo4", album: "demo", isFavorite: true, genre: "demo", artist: "demo"),
Song(name: "demo5", album: "demo", isFavorite: false, genre: "demo", artist: "demo"),
]
@Published var songs = [Song]() {
didSet {
if let encoded = try? JSONEncoder().encode(songs) {
UserDefaults.standard.set(encoded, forKey: "demosongs")
}
}
}
init(){
if let savedItems = UserDefaults.standard.data(forKey: "demosongs") {
if let decodedItems = try? JSONDecoder().decode([Song].self, from: savedItems) {
songs = decodedItems
return
}
}
songs = songsData
}
}