Open XITRIX opened 1 year ago
Sorry, but the code posted is not self-contained. Please update the code to a self contained example that exhibits the problem?
Ok, I've replaced self contained code with repo, cause I have no idea how to fit everything there
The issue with this code is here with the ViewModel class. You made it Hashable. It doesn't make sense to put a BehaviorRelay in a Hashable type. The diffable data source has no notion of the view model getting updated so it doesn't allow the cell to change.
When the text in a ViewModel needs to change, the proper way to communicate that is by updating the data
BehaviorRelay.
It has to be Hashable to use it with DiffableDataSource whom is responsible to create cell's view and animate it's insert\delete\move, and BehaviorRelay is responsible for cell's content.
Also, it's just an example, I uses more complex ViewModels than just text in it. Here is the place I've noticed this issue in the first place
My main problem is that UIKit is able to run it's UI stuff on main thread, but not on main queue, and because of it MainScheduler's DispatchQueue.isMain check fails which could cause UI glitches because of View's update delay
I understand it has to be Hashable, but you cannot put a BehaviorRelay in a Hashable type. It doesn't make any sense. Additionally, you are not creating your Hashable type correctly in the link you posted. The type has to compare all the internal elements.
Understand, that the way the diffable data source works is that it looks at the values of the elements and compares them to the previous values. If the values change when the data source isn't looking (for example because they are bound to an Observable) then the data source will not acknowledge those changes and will not update the collection view.
I'm not seeing any problems with isMain
failing in the code.
I don't want allow DiffableDataSource to compare model's values cause I don't want it to be responsible for updating cell's content (I want to put this responsibility onto RX), I want it to be responsible only for cell's position in collection, it's insert\delete\move animation, that's why I do not include value of BehaviorRelay info hasher.
Also, my question is not about "how to use DiffableDataSource" my question is "UIKit is able to run it's UI stuff on main thread, but not on main queue, and because of it MainScheduler's DispatchQueue.isMain check fails which could cause UI glitches because of View's update delay" and this DiffableDataSource case is just an example of that problem.
Please, don't take it as an attack, I just think we are discussing a wrong thing. If you think it's important to discuss so be it.
I have not seen any issues with DispatchQueue.isMain
in the code you have provided.
If you'll check the screen at the moment breakpoint triggers you'll see that collection presents cell's with it's default values.
Because DispatchQueue.isMain says false scheduleInternal delays calling action(state) with self.mainQueue.async
Where are you putting the breakpoint?
I thought I included it in repo, it should be in Reactive.swift -> 42: base[keyPath: keyPath] = value
It will be called with delay by self.mainQueue.async
cause MainScheduler's 62: DispatchQueue.isMain
returned false, while in fact, it was called by UIKit on main thread (but not main queue)
Replacing DispatchQueue.isMain
with Thread.current.isMainThread
fixed this "old data UI blink" but I'm afraid it could cause problems with something else (I'm not sure that mainThread is always UI thread), so I don't think it is a solution
I see it now. It feels like the "DispatchQueue+Extensions.swift" file needs to be changed to:
extension DispatchQueue {
static var isMain: Bool {
Thread.current.isMainThread
}
}
But I'm not sure why that extension was made the way it way in the first place. @kzaher or @freak4pc might have some input here.
I'm not sure that fix should be like that, cause this extension is about queue, and it is correct that the queue is not main.
As I know, in some cases it is possible that mainThread could be not UI thread, and as we can see here, UI thread could be not main queue also, so we cannot trust neither mainThread nor the mainQueue
Short description of the issue:
MainScheduler
'sDispatchQueue.isMain
containsfalse
on binding toBehaviorRelay
in context ofUICollectionViewDiffableDataSource
's cellForRow function, which leads to UI blink with old data for a momentExpected outcome:
Binding ViewModel's
BehaviorRelay
to Cell's view should update it's UI instantlyWhat actually happens:
Cells are able to appear on screen with old data before value of
BehaviorRelay
applies to Cell's viewsSelf contained code example that reproduces the issue:
Link to repo with issue
Breakpoint should be in
Reactive.swift
-> 42:base[keyPath: keyPath] = value
, It will show the main problem of this issueAll code needed presents in
ViewController
andTestCell
files. Everything else in this repo are default iOS app template from xCodeRxSwift/RxCocoa/RxBlocking/RxTest version/commit
RxSwift 6.5.0
Platform/Environment
How easy is to reproduce? (chances of successful reproduce after running the self contained code)
Xcode version:
:warning: Fields below are optional for general issues or in case those questions aren't related to your issue, but filling them out will increase the chances of getting your issue resolved. :warning:
Installation method:
I have multiple versions of Xcode installed: (so we can know if this is a potential cause of your issue)
Level of RxSwift knowledge: (this is so we can understand your level of knowledge and formulate the response in an appropriate manner)
Some more details:
UICollectionViewDiffableDataSource
'scellForRow
function calls on queue named "Thread 1 Queue : com.apple.uikit.datasource.diffing (serial)", so it's not a 'main' queue, but it calls on Thread 1 which in fact IS the main threadAs experiment, I've added to
MainScheduler
->scheduleInternal
's function additional condition -(Thread.current.isMainThread || DispatchQueue.isMain)
and it fixed my problem with diffableDataSource but I'm afraid it could damage anything else, so I'd like to find better solution to that problem