Issue #448
Use ObservableObject
and onReceive
to receive event. URLSession.dataTask
reports in background queue, so need to .receive(on: RunLoop.main)
to receive events on main queue.
For better dependency injection, need to use ImageLoader
from Environment
There should be a way to propagate event from Publisher
to another Publisher
, for now we use sink
ImageLoader.swift
import Combine
import WatchKit
class ImageLoader: ObservableObject {
private var cancellable: AnyCancellable?
let objectWillChange = PassthroughSubject<UIImage?, Never>()
func load(url: URL) {
self.cancellable = URLSession.shared
.dataTaskPublisher(for: url)
.map({ $0.data })
.eraseToAnyPublisher()
.receive(on: RunLoop.main)
.map({ UIImage(data: $0) })
.replaceError(with: nil)
.sink(receiveValue: { image in
self.objectWillChange.send(image)
})
}
func cancel() {
cancellable?.cancel()
}
}
RemoteImage.swift
import SwiftUI
import WatchKit
struct RemoteImage: View {
let url: URL
let imageLoader = ImageLoader()
@State var image: UIImage? = nil
var body: some View {
Group {
makeContent()
}
.onReceive(imageLoader.objectWillChange, perform: { image in
self.image = image
})
.onAppear(perform: {
self.imageLoader.load(url: self.url)
})
.onDisappear(perform: {
self.imageLoader.cancel()
})
}
private func makeContent() -> some View {
if let image = image {
return AnyView(
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
)
} else {
return AnyView(Text("😢"))
}
}
}