I am rewriting the existing UIKit cell with SwiftUI. ChartView inside of it I wrapped with UIViewRepresentable because it is out of scope right now to rewrite it as well. The data for the chart I am getting async and update the view once I have it. The viewModel for the chart I marked as @Published so SwiftUI knows when to update the view. The problem is the view flickers once the update is there.
View:
struct DashboardWatchlistView: View {
@ObservedObject var viewModel: DashboardWatchlistViewModel
var body: some View {
VStack(alignment: .center) {
ZStack {
HStack(spacing: 4) {
HStack(spacing: 16) {
Image(systemName: "person")
VStack(alignment: .leading, spacing: .designSystem(8) {
HStack {
Text(viewModel.leftTitle).font(.subheadline).fontWeight(.semibold).lineLimit(1)
}
Text(viewModel.leftSubtitle).font(.caption2).foregroundColor(Color(ThemeManager.current.neutral50))
}
}
Spacer()
if let chartViewModel = viewModel.chartViewModel {
AssetLineChartViewRepresentable(viewModel: chartViewModel).frame(width: 65, height: 20)
}
VStack(alignment: .trailing, spacing: .designSystem(.extraSmall3)) {
percentChangeView(assetPercentChangeType: viewModel.assetPercentChangeType)
Text(viewModel.rightSubtitle).font(.caption2).foregroundColor(Color(ThemeManager.current.neutral50))
}.padding(.leading, .designSystem(.medium))
}
.padding([.leading, .trailing])
if !viewModel.hideSeparator {
VStack {
Spacer()
Divider().padding(.leading)
}
}
}
}
}
}
ViewModel:
final class DashboardWatchlistViewModel: ObservableObject {
@Published var logoURL: URL?
@Published var leftTitle = ""
@Published var leftSubtitle = ""
@Published var rightTitle = ""
@Published var rightSubtitle = ""
@Published var percentageChange: Decimal = .zero
@Published var placeholderColor = ""
@Published var corners: Corners = .none
@Published var assetPercentChangeType: AssetPercentChangeType = .zero
@Published var hideSeparator = false
@Published var isHighlighted = false
@Published var chartViewModel: AssetLineChartViewRepresentable.ViewModel?
let chartDataEvent: AnyPublisher<BPChartData?, Never>
init(tradable: Tradable,
priceString: String,
percentageChange: Decimal,
corners: Corners,
hideSeparator: Bool,
assetPercentChangeType: AssetPercentChangeType,
chartDataEvent: AnyPublisher<BPChartData?, Never>) {
self.logoURL = tradable.logoURL
self.leftTitle = tradable.name
self.rightTitle = PercentageFormatter.default.string(from: abs(percentageChange))
self.leftSubtitle = tradable.symbol
self.rightSubtitle = priceString
self.percentageChange = percentageChange
self.placeholderColor = tradable.placeholderColor ?? ""
self.corners = corners
self.chartDataEvent = chartDataEvent
self.hideSeparator = hideSeparator
self.assetPercentChangeType = assetPercentChangeType
}
}
Cell where I set the chartViewModel:
final class DashboardWatchlistCell: UICollectionViewCell, HostingCell {
var hostingController: UIHostingController<DashboardWatchlistView>?
private var dashboardWatchlistView: DashboardWatchlistView?
private var viewModel: DashboardWatchlistViewModel?
private var cancellables = Set<AnyCancellable>()
override var isHighlighted: Bool {
didSet {
viewModel?.isHighlighted = isHighlighted
}
}
func configure(with viewModel: DashboardWatchlistViewModel) {
self.dashboardWatchlistView = DashboardWatchlistView(viewModel: viewModel)
self.viewModel = viewModel
// HERE I SET THE VIEWMODEL
viewModel.chartDataEvent.compactMap { $0 }
.receive(on: DispatchQueue.main)
.sink { chartData in
viewModel.chartViewModel = AssetLineChartViewRepresentable.ViewModel(chartData: chartData, percentageChange: viewModel.percentageChange)
}
.store(in: &cancellables)
}
func addHostedView(to parent: UIViewController) {
guard let dashboardWatchlistView = dashboardWatchlistView else { return }
addHostedView(dashboardWatchlistView, to: parent)
backgroundColor = .clear
}
}
