Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Related issue
#8
Disclaimer
This PR should not be thought as a complete solution, it's a research and an explanation about the root cause of performance issues in Cross Check functionality, and it will involve technical and product input from people who know more about the project.
Summary
I found two problems in the code that runs when the user taps on "Cross Check" button on the right menu:
1. Async code misuse
This is the code inside
reloadData
method, inOSTCrossCheckViewController.m
, inmaster
:It:
fetchNotExpected
). The result of that API call is not awaited, that means, the API call starts, but the code continues with the next instruction (setFiltersQuantities
) without waiting for that API call to finish with some result.efforts
, applies some filters, and stops the loading indicator.The main goal of the main queue is to perform UI related code. In general, a good practice related to concurrency is to free the main queue to only run UI related code, and try to perform heavy tasks (like API calls) in different threads, taking advantage of the advantage of a multithreading environment.
Because of that, and because this chunk of code was implemented a couple of years before the complete app codebase (for some reason I'm not able to see the PR), I suspect that there is some misuse of async code.
I refactored that code e little bit, to follow better practices (and also what's done in other parts of the app), but for that I assumed a couple of things:
fetchNotExpected
) should wait to the result of that call.fetchNotExpected
.The result is the following:
reloadData
:fetchNotExpected
:The API call is fired in a background queue, decreasing the amount of work in the main queue, and then the completion handlers (for success or error) are fired in the main queue, because they involve UI related stuff.
If you take a look at others API calls inside the codebase, they are following the same pattern. One example is this one.
2. Expensive manipulations of the entries fetched from API and local DB
In
reloadData
, the app is fetching data from API and from local DB. After that, it's performing a lot of manipulations to decide which efforts should it show in the collection view and which of them should filter out. There are a bunch of nestedfor
statements to decide that. These manipulations are causing a severe hang in the main thread, as this screenshot of Xcode Instruments show:I think we should try to understand the reason behind those nested
for
s in a better way in order to see if we can delete or merge some of them, to decrease the time complexity of the whole algorithm. But for that, we need to understand the feature itself and each entity involved here.