When we announced version 67 of the Mac app, we put a lot of focus on the performance improvements it brought. Our team worked hard behind the scenes to streamline things and make the whole app feel a lot smoother and more responsive. Take a look at our announcement post to find out more.
Now, we want to give you a look at our approach to speeding up Symbols. This is the first in a new series of posts where we’ll dive a little deeper into the technical side of the app. Stay tuned for more in future.
We spent a lot of time working on Sketch 67’s performance — to make the Mac app faster. We’ve improved a number of things, from how we render background blurs with saturation, to how we time our display updates to stay closer to the screen’s refresh rate.
We’ve tried to make working with Symbols feel smoother in general, too, and a few people have asked how we went about this. While the answer isn’t some crazy story involving advanced computer science or math, there also wasn’t just one thing that made all the difference. But we do think there are a few interesting things we can share that might also ring true for other developers.
What makes a Symbol?
When we introduced Symbols, we started with a basic foundation. Symbols consist of two parts; a Symbol master, which is like an Artboard, and contains multiple layers (such as text layers, groups and shapes), and a Symbol instance, which is a regular layer that just contains a reference to the master. Whenever we need to draw an instance to the canvas, we find the Symbol master and draw that in its place.
A Symbol instance can also contain overrides, which is a set of derivations from the values in the master. Text overrides are the most common. Before Sketch 67, if an instance had overrides we would make a full copy of the master, update the text layers with the overrides values, and then draw that copy.
When we first released Symbols, they only supported text overrides. Since then, we’ve added more types of overrides — nested Symbol overrides, style overrides, resize rules, Smart Layout and more. Having a growing team working on these presented challenges, and while we always try to document our code and share knowledge, we’re not a hive mind. We had a nagging feeling we could be doing better.
As features evolve, the basic assumptions that they were built on may no longer hold true, and the architecture you put in place on day one may no longer suffice. But rather than rewrite it at the first sign of trouble, it usually makes more sense to stretch the system a little further. After all, we can’t rewrite every line of code every time we create a new feature.
Eventually, though, you hit a breaking point, and we reached one while developing Smart Layout. We used to render Symbol instances as copies of Symbol masters, but that had to change so we could deal with layout adjustments affecting nested symbols. Now we render Symbols as detached groups — a Symbol instance is turned into a group that contains a copy of all those layers, recursively down into allContinue reading