Closed beeirl closed 3 years ago
@beeirl i have this issue as well. Not sure if this is a Resolver issue but maybe a Combine one.
@Prince2k3 oh no way. Could you find a workaround that works at least temporarily or does it block you as much as it blocks me?
Currently pull to refresh since this issue primarily happens on the feed portion of the app. And since it is the first to appear when app come load or comes from the background it happens to it the most.
I guess my first question would be what happens if you take Resolver out of the picture?
final class Service {
static var shared = Service()
}
final class MainViewModel: ObservableObject {
let service = Service,shared
}
If the behavior is the same then it's not an injection issue.
In fact... (hint, hint) I'd suggest reading an article of mine: Deep Inside Views, State and Performance in SwiftUI
As I STRONGLY suspect that things aren't happening when you think they're happening. In particular, I'd put some print/log statements inside your initializers. If you do that you'll probably see something like...
INIT MainView
INIT Service
INIT MainViewModel
MAIN foo == bar
INIT ChildView
INIT ChildViewModel
CHILD foo = bar
SERVICE Timer fires
MAIN foo == newValue
CHILD foo == newValue
INIT ChildView
In particular, note that you're going to get the initial values from your published object and that -- perhaps more to the point -- that "INIT ChildViewModel" is occurring BEFORE the timer fires (see code below).
In fact, I suspect it's occurring BEFORE you think that ChildView is even shown!
Some key points here are that...
In SwiftUI, doing the sort of heavy lifting you're doing inside of your initializers is NOT recommended.
final class Service {
static var shared = Service()
@Published var foo: String = "bar"
init() {
print("INIT Service")
// I subscribe to some async data here
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
print("SERVICE Timer fires")
self.foo = "newValue"
}
}
}
final class MainViewModel: ObservableObject {
let service = Service.shared
@Published var someCondition = false
private var cancellables = Set<AnyCancellable>()
init() {
print("INIT MainViewModel")
service.$foo
.sink { [weak self] foo in
print("MAIN foo == \(foo)")
self?.someCondition = true
}
.store(in: &cancellables)
}
}
struct MainView: View {
@StateObject var mainViewModel = MainViewModel()
init() {
print("INIT MainView")
}
var body: some View {
ZStack {
if mainViewModel.someCondition {
ChildView()
}
}
}
}
final class ChildViewModel: ObservableObject {
let service = Service.shared
@Published var foo: String = ""
private var cancellables = Set<AnyCancellable>()
init() {
print("INIT ChildViewModel")
service.$foo
.sink { [weak self] foo in
print("CHILD foo == \(foo)")
self?.foo = foo
}
.store(in: &cancellables)
}
}
struct ChildView: View {
@StateObject var childViewModel = ChildViewModel()
init() {
print("INIT ChildView")
}
var body: some View {
ZStack {
Text(childViewModel.foo)
}
}
}
Just noting I ran the above with and without Resolver with the same log trace.
So... one other nit.
You're subscribing to some "async" data and storing the value into the published value. But your code doesn't show when you switch that data to the main thread for UI updates.
final class Service {
@Published var foo: String = "bar"
private let background = DispatchQueue(label: "bg", qos: .background)
private var cancellables = Set<AnyCancellable>()
init() {
print("INIT Service")
// I subscribe to some async data here
Future<String, Never> { promise1 in
self.background.asyncAfter(deadline: .now() + 10) {
print("SERVICE Timer fires")
promise1(.success("newValue"))
}
}
.receive(on: RunLoop.main) // Note switch to main thread
.sink { [weak self] (value) in
self?.foo = value
}
.store(in: &cancellables)
}
}
SwiftUI is like UIKit. Data updates that trigger UI updates should occur on the main thread, otherwise those events may appear to be "eaten".
I am currently facing the problem that my nested SwiftUI Views are not getting the latest values published by my Injected Service. I would like to describe my problem using a highly simplified representation of my current project structure to ensure traceability.
Perhaps there is also a relation to this post on SO. Please lmk if you need more details.