Question, if I am writing async code in the notification service extension, I understand it terminates after 30 seconds.
Correct, though I always recommend that anyone setting up things like timeouts use a shorter value "just in case". So I'd probably build around ~25s, not 30s.
If I want to wait until these async methods finish before calling the content handler, I believe an option I have is to use dispatch groups. However, I am open to other solutions if there are better options.
What are you actually waiting on? In general, I've become very nervous anytime I see code that uses dispatch groups because they seem to be used as a slightly awkward "band-aid" trying to make something work that doesn't really want to work. Case in point here, the main reason an NSE would be waiting is network activity, in which case the simpler solution would be to simply set the right timeout on that network activity.
Having said that....
My question is, if I use dispatch groups, is there any issue in using the main queue here? Or does the main thread not make sense to use in the context of the NSE?
...the main queue will fine here. GCD requires the main queue to be defined as part of its core architecture, and I believe NSE is actually using a standard run loop (not dispatch_main). Assuming you're not doing something silly (like blocking the main thread for 20+s), I don't see any issue with using the main queue for something like this.
Or is it recommended to instead use a different queue in the NSE?
OR am I overthinking this? :) Thanks ahead of time, relatively new to iOS so just looking to learn/understand better.
Well, you said you wanted to learn...
The first thing to understand was that GCD was built to provide a low-level thread pool API that all of the system could use, and that dynamic heavily drove a lot of its core design. For example, it was written as a C API (not ObjC) because it had to be usable by components underneath Foundation, not because anyone at Apple thought C was the best language for managing threads. In most cases, the best option is to use "something else" as there is some other, higher-level, API option that is simply "better".
However, if you're going to build on GCD, my actual recommendation will sound somewhat contradictory. Basically:
-
Queues are great! Make as many of them as it takes to create a good representation of your app’s internal architecture and data flow.
-
Queues are TERRIBLE! You probably only need one, and you should NEVER use the global concurrent queues.
You'll find a long discussion of this on this thread, but what's going on here is that GCD queues actually serve two very different roles.
The first role (queues are great!) is about "labeling" work and managing how that work is serialized against itself. In that context, more queues are great because they let you better notate the specific work your app is doing, and more information is almost always better.
The second role (queues are TERRIBLE!) is in how they're used to actually schedule and manage work. In practice, it turns out that the "best" approach for LOTS of situations ends looking a lot like this:
-
Use the "right" (small) number of threads. (Fastest, not too complicated)
-
Use one thread (Pretty fast, really simple)
-
Let GCD run wild with lots of queues (Really slow, really complicated)
...and, unfortunately, it turned out that GCD’s general structure and the way we taught people about it tended to encourage number 3. In the worst case, that basically turned GCD into a great tool for making slower, buggier apps.
In any case, the solution to these issues is DispatchQueue.setTarget(queue:), using the approach I described here. That approach lets you manage how your work is logically represented (#1) while maintaining a simple, serial implementation (avoiding #2).
Also, quoting myself from that post:
*One detail to understand here is that queues aren't objects that work actually "moves through". So a block doesn't "arrive" at your queue, then move to its target queue, then the next target queue, then the next... until it reaches a global queue. Simply having lots of queues or nested target queues doesn't affect performance.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware