In my app, I need to parse a response which is directly going to be used to render a part of the screen. The response is huge as it contains other stuff too. Since, i don’t want to do this parsing on the main thread to avoid causing issues, but I do need this response to be parsed (from gzipped format) immediately so that I can take a decision and show the expected UI quickly. In this scenario, is it ok to use
DispatchQueue.global(qos: .userInteractive // .userInitiated)
to achieve this asynchronously but quickly? Is there anything I should be aware of when doing this? Any risks?
Thanks
2
Answers
No. This is exactly what
.userInitiated
means. And you’re doing it in the background, so the main thread (the interface) won’t be hampered.In fact, just the contrary, for best response I would recommend using a dedicated DispatchQueue you create yourself, not the global DispatchQueue, because it is concurrent and may be doing other stuff, which will slow you down.
(Even better would be to adopt Swift Concurrency and put this work in an actor.)
In short, avoid
userInteractive
for background work. UsinguserInitiated
is better, and probably would be sufficient in your use-case. Even consider usingdefault
orutility
. Benchmark your app and see.I would avoid
userInteractive
for this. This QoS (quality of service) is intended for work that is actually driving the UI:Overuse of
userInteractive
for background work risks starving the main thread. Now, in your example (where you are using a single background thread and you likely have enough processors such that the UI could do its work on one processor while the background work runs on another), you probably would not manifest problems, but this is not a scalable approach. UsinguserInteractive
for background threads is not advisable (except, of course, where it is essential, e.g., offloading calculations explicitly needed by animation of the UI, or what have you).Using
userInitiated
would be much better:The
userInitiated
is probably fine for your use-case.As an aside, I must confess that, in extreme situations, when really taxing the system (e.g., using
concurrentPerform
to perform very intense computational tasks in parallel on all of my computer’s processors at the same time), I found that evenuserInitiated
was sometimes too high: The UI (e.g., showing a progress indicator while the compute task was underway) was not as responsive as I would like on some hardware. Obviously,userInitiated
has a lower priority than the main queue’suserInteractive
QoS, and one would assume that this would ensure that the UI would not be affected, but, in practice, sometimes it is. So, I have had more consistent and responsive UI when I useddefault
for the background work:In my tests (in massively parallel compute tasks, at least), the overall performance of
default
was not observably different thanuserInitiated
, butdefault
ensured a smoother UI. (As an aside, people frequently over-estimate the performance impact of one QoS over another and neglect to benchmark performance to validate their assumptions.)Bottom line, I would advise benchmarking
default
(or evenutility
) againstuserInitiated
for your use-case, and see if there is any consistent, observable difference. Since you are only using one background thread, it probably does not matter, but if doing something more intensive, even.userInitiated
might be too high of a QoS.