如何链接 ObservableObject?

How to chain ObservableObject?

我有一个可以保存图像的游戏对象。每当为游戏找到图像 URL 时,应创建一个新的 GameImage 对象实例。然后它将获取图像并填充 UIImage 属性。发生这种情况时,UI 应该更新以显示图像。

class Game: ObservableObject {
    @Published var image: GameImage?
} 

class GameImage: ObservableObject {
    let url: URL
    @Published var image: UIImage?
    
    private var cancellable: AnyCancellable?
    
    init(url: URL) {
        self.url = url
    }
    
    func fetch() {
        self.cancellable = URLSession.shared.dataTaskPublisher(for: self.url)
            .map { UIImage(data: [=10=].data) }
            .replaceError(with: nil)
            .receive(on: DispatchQueue.main)
            .sink(receiveValue: { [weak self] (image) in
                guard let self = self else { return }
                self.image = image
                print(self.url)
                print(self.image)
            })
    }
    
    func cancel() {
        cancellable?.cancel()
    }
    
    deinit {
        cancel()
    }
}

struct ContentView: View {
    
    @StateObject var game = Game()

    var body: some View {
        VStack {
            if let image = game.image?.image {
                Image(uiImage: image)
            } else {
                Text("No image.")
            }
        }
        .onAppear(perform: {
            guard let gameImageURL = URL(string: "https://cf.geekdo-images.com/itemrep/img/oVEpcbtyWkJjIjk1peTJo6hI1yk=/fit-in/246x300/pic4884996.jpg") else { return }
            game.image = GameImage(url: gameImageURL)
            game.image!.fetch()
        })
    }
}

问题是。获取完成后,调试控制台将显示图像包含 UIImage。但是 UI 不会更新以显示图像。我在这里错过了什么?

有比链接更简单的解决方案 ObservableObject,只需将依赖部分分离到独立的子视图中......所有的都会自动工作。

这是可能的方法。测试 Xcode 12 / iOS 14.

struct ContentView: View {

    @StateObject var game = Game()

    var body: some View {
        VStack {
            if nil != game.image {
                GameImageView(vm: game.image!)
            }
        }
        .onAppear(perform: {
            guard let gameImageURL = URL(string: "https://cf.geekdo-images.com/itemrep/img/oVEpcbtyWkJjIjk1peTJo6hI1yk=/fit-in/246x300/pic4884996.jpg") else { return }
            game.image = GameImage(url: gameImageURL)
            game.image!.fetch()
        })
    }
}

struct GameImageView: View {
    @ObservedObject var vm: GameImage

    var body: some View {
        if let image = vm.image {
            Image(uiImage: image)
        } else {
            Text("No image.")
        }
    }
}