Asynchronous 如何链接可观察对象?

Asynchronous 如何链接可观察对象?,asynchronous,swiftui,combine,Asynchronous,Swiftui,Combine,我有一个可以保存图像的游戏对象。每当为游戏找到图像URL时,都应创建GameImage对象的新实例。然后,它将获取图像并填充UIImage属性。发生这种情况时,应更新显示图像的UI class Game: ObservableObject { @Published var image: GameImage? } class GameImage: ObservableObject { let url: URL @Published var image: UIImage?

我有一个可以保存图像的游戏对象。每当为游戏找到图像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: $0.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不会更新以显示图像。这里我缺少了什么?

有比链接
可观察对象更简单的解决方案,只需将依赖部分分离到独立的子视图中。。。所有这些都将自动工作

以下是可能的方法。使用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.")
        }
    }
}

你会显示依赖于这个视图模型的UI吗?我不知道你的意思。游戏实例
Game
应包含真理的单一来源。游戏对象还可以包含其他属性,如名称,我从这个示例中删除了这些属性,以避免使事情过于复杂。无论何时图像可用,它都应该显示它,这样它是依赖的。不强制展开选项不是更好吗?难道不是
游戏.image?.fetch()
?我知道这是一个问题,但你总是可以改进它。是的,虽然在这个例子中我把game.image设置在上面一行,所以它非常安全。我刚刚测试了代码,它工作了。“我仍在试图理解这个概念。@当然可以,但作为一种良好的做法,我建议不要强制展开选项。以后你可以把这一行复制到别的地方,然后忘掉它。