1

Here is my Model:

class Channel: Identifiable, Decodable, ObservableObject {
    
    var id = UUID()
    var channelId = ""
    var title = ""
    var thumbnail = ""
    @Published var thumbnailImage : UIImage?
    
    enum CodingKeys: String, CodingKey {
        
        //Keys not in the model
        case snippet
        case thumbnails
        case high
         
        //Keys in the model
        case channelId
        case title
        case thumbnail = "url"
    }
    
    required init (from decoder: Decoder) throws {
        
        let container =  try decoder.container(keyedBy: CodingKeys.self)
        let snippetContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .snippet)
        let thumbnailsContainer = try snippetContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .thumbnails)
        let highContainer = try thumbnailsContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .high)
        
        self.title = try snippetContainer.decode(String.self, forKey: .title)
        self.thumbnail = try highContainer.decode(String.self, forKey: .thumbnail)
        self.channelId = try snippetContainer.decode(String.self, forKey: .channelId)
    }
}
class ChannelStore: ObservableObject {    
    @Published var allChannels = [Channel]()
}

I have an EnvironmentObject linked to ChannelStore and it all works as expected.

The issue is that my view needs to be updated when the property thumbnailImage is changed. (Since it's from a network call, the view gets generated before the call returns and uses a stock image in the mean time). I tried adding the Published property wrapper to that property but it didn't do anything.

In my view I have: @EnvironmentObject var channelStore: ChannelStore

How to subscribe to a property change that is managed by an EnvironmentObject?

Edit

It was suggested to change the Channel class to a struct. The only reason I made a class was to avoid the "cannot assign to property: 'channel' is a 'let' constant:

for channel in channelStore.allChannels {
    networking.setThumbnail(channel: channel) { image in
        channel.thumbnailImage = image
    }
}

Final Comments

I was able to solve the entire problem by using the answer provided below and change my for loop to the following:

for (index, var channel) in channelStore.allChannels.enumerated() {
    networking.setThumbnail(channel: channel) { image in
        channel.thumbnailImage = image
        channelStore.allChannels[index] = channel
        print("Images LOADED")
    }
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209
AvsBest
  • 435
  • 1
  • 4
  • 9

1 Answers1

1

If you can make your Channel a struct:

struct Channel: Identifiable, Decodable { // <- replace `class` with `struct`
    ...
    var thumbnailImage : UIImage? // <- remove `@Published`
}

Then every modification of the thumbnailImage will create a new copy of the Channel object and trigger the @Published property in the ChannelStore:

class ChannelStore: ObservableObject {
    @Published var allChannels = [Channel]()
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209