Home > Enterprise >  Can I use @ObservedObject to make instant View updates?
Can I use @ObservedObject to make instant View updates?

Time:09-26

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
    }
}
  • Related