Core Animation Background Thread CATransaction

Hey everyone 👋

I'm trying to initialize a part of a CALayer hierarchy on a background thread and then attach the root of that hierarchy to a CALayer that backs a UIView.

The motivation is to keep the main thread responsive when constructing a complex layer hierarchy. This isn't a case where I'm creating two or three layers and then switching back to the main thread. The hierarchy can potentially contain a large number of layers, with animations being created/configured for those layers as well.

My first approach was to create and configure the layers entirely on a background thread. While the output might be the expected one (not always), CoreAnimation emits an assertion along the lines of: "Modifications to the layer tree from a background thread may not be committed" (or something like this).

This makes sense to me if implicit CATransaction is thread-local. In that case, the implicit transaction opened by the layer modification on the background thread would not be part of the transaction that is already open on the main thread. Therefore, committing the main-thread transaction would not commit the changes made on the background thread.

My second approach was to explicitly create and commit a CATransaction on the background thread. This appears to be accepted by Core Animation's threading model, but I'm seeing unreliable results. Sometimes parts of the hierarchy are missing, and in other cases the hierarchy is present but the animations don't appear to run at all.

I do understand that this is private behavior of the framework, but I wanted to know if what I am trying to achieve is possible and, if so, what the solution would be (obviously if you can share this information).

Besides this, I would also like to know what behavior CATransactions have when they are created on different threads. What I mean by this is that the transactions work as a stack, and the changes are committed when the stack is empty. Does this behavior still apply when having transactions on different threads? Any weird behaviours that might appear between transactions operated on main vs background threads?

Thank you!

Vlad.

The hierarchy can potentially contain a large number of layers, with animations being created/configured for those layers as well.

What is "large"?

What happens if you do it all on the main thread? Do you notice any hitches? Does instruments pick up any problem? If not, then there's no problem.

If there is a problem, this seems like a great opportunity to use Task yield to do everything on the main thread.

Thanks for the answer! I appreciate the suggestion, and I definitely understand the reasoning behind first checking whether the layer construction actually causes noticeable hitches before trying to optimize it.

In this particular case, I'm looking at this in the context of the Lottie library, where the layer hierarchy can become quite large and convoluted, with many layers and animations being created/configured. So I'm trying to understand whether there is a way to move as much of that work as possible off the main thread. (More context here: https://github.com/airbnb/lottie-ios/discussions/1886?utm_source=chatgpt.com).

To come back to your question, I don't have an exact number at the moment. I would say, though, that even one of the simplest Lottie files, Bouncing Ball, creates a hierarchy of around 10 layers with 7 animations added.

Of course, that's still a very small hierarchy and isn't an issue to construct on the main thread. I'm mentioning it mainly as a reference point, since more complex Lottie files can result in significantly larger and more convoluted hierarchies, especially when nested compositions are involved.

The Task.yield() suggestion is interesting and may well be a good solution for keeping the main thread responsive. That said, the library's current implementation doesn't really support this approach, so it would require a significant change for this. Before considering it, I'd like to focus on optimizing the interaction with Core Animation itself, if that's possible.

Vlad.

Core Animation Background Thread CATransaction
 
 
Q