Recorded – watch the full episode on YouTube.
If you were to come to a new codebase and say to a developer, okay, let's start identifying the problems here. How would you do that? What would you use to identify these performance problems?
Janina Kutyn: Well, I guess, first of all, I would start looking for problems for what's causing them or what if I actually saw that a problem is happening, so visually on a device.
Or I guess if you don't have a full range of devices, then you need to start profiling and using instruments to see if there's somewhere where you're spending too much time.
For example, you can use Time Profiler to identify if you have CPU bottleneck somewhere, if there's some functions, they're performing layout and they're doing a lot of calculation.
“The older the device you have kicking around, the better because that device will give you a preview for any issues so much more than your newest iPhone.”
Or maybe it's you have a GPU bottleneck somewhere, so it's taking a lot of time to perform expensive rendering operations, like compositing and blending. For that, there's also an instrument where you can measure frame rates and a GPU bottleneck can be identified by consistent, but low frame rates.
Paul Hudson: So, it sounds like this is one of the nice things about doing display, and layout and so forth. The first step to identifying a problem is literally run the app on an old device, and scroll around and see if you can get, well, it's stuttering a little bit, now what's going on here? Is that a fair first step?
Janina Kutyn: Yeah, that's exactly what I would do. And the older the device you have kicking around, the better because that device will give you a preview for any issues so much more than your newest iPhone.
Paul Hudson: I remember it used to be the case that the iPad 3 was my preferred test device. That was a while ago, but that was the very first retina iPad. And it wasn't really ready for retina, the GPU wasn't up to the power of handling that big screen. So, that's the only time I've seen them release a new iPad, iPad 4, in less than a year. It came out six months later because the 3 was so very bad at performance, but it made for a great test bed.
If your code worked great, if it was a super fast UI on an iPad 3, boom, you're flying. You could work on iPad 2, 4, 1 and they'll all work basically. So, yes, find that worst case device. So, once you found that slow screen, if you've seen it's stuttering on one screen, the next step then is to go to Time Profiler in instruments and really dig into it, right?
Janina Kutyn: You could do that, or you can take a look at the code and the layouts of maybe your cells or your scroll view, whatever it is that's causing you issues and kind of think about what are the operations that could be causing a bottleneck here.
So, for example, are you doing a lot of blending? Are there a lot of transparencies? Are there a lot of shadows or rounded corners? Are you making a lot of work for getting the cell on the screen? And if you are, what could you do to kind of minimize that work?
“You could fix transparency. I think that's one of the biggest things that people often forget about is making sure their views aren't transparent when they can be.”
Paul Hudson: Right. And that's actually an area where I think UIKit gives you a lot of control, really. You can think about every small part of your cell and control it exactly. Whereas in SwiftUI, you just say I want this thing and it's done for you. In UIKit, you have the UIView
, you have a CALayer
behind it. You could monkey around all over the code and make it exactly as bad or as good as you like in terms of performance.
Janina Kutyn: Yeah, exactly. So, you could fix transparency. I think that's one of the biggest things that people often forget about is making sure their views aren't transparent when they can be.
But, transparency isn't just using view.backgroundColor = .clear
– transparencies are also the corners of a view whose corners you've rounded off, or transparencies are shadows, or transparencies are alpha = 0.5
.
There's so many ways that you can cause a transparency in your code without realizing it. And then, that causes a lot of expensive blending operations because for every transparent layer, the GPU has to compute the color of every pixel by compositing every single pixel on top of each other.
Or for example, on the Simulator debug tools, transparencies is one of the things you can identify by turning on Color Transparent Layers.
Another one is pixel misalignment. That doesn't happen as much with Auto Layout, but with frame-based layout what you can get into is where your view doesn't start or doesn't end on a full pixel.
So, instead of, for example, starting at (0, 0), you start at (0, 0.5) and this is an issue because even if you've used solidly colored, now to compute the color of the pixel at (0, 0), anti-aliasing is triggered and the colors of surrounding pixels are being pulled in to calculate the color of that one pixel.
“I think sometimes people over-optimize. I think just like you should think about performance when you're writing your code and think about what will make sure I don't have too many layout passes. What is the minimum that I need to do that I'm doing?”
So, that's probably not what you wanted, you want it to start on (0, 0), not to start half way.
Paul Hudson: Definitely. So, that's two common examples of performance problems, folks hit in UIKit. Give me a third. What's the third, most common thing people do when they're trying to make UIKit faster?
Janina Kutyn: Well, I think sometimes people over-optimize. I think just like you should think about performance when you're writing your code and think about what will make sure I don't have too many layout passes. What is the minimum that I need to do that I'm doing?
It's also really easy to jump down the rabbit hole of, “oh, I'll just optimize this. Oh, this might be a little bit better. Oh, this might be a little bit better also.” And then, you have this over-engineered, over-optimized, overly commented code that you have comments everywhere explaining this does this because of that and this does this because of that.
Now, you've written a book instead of a single class and you really over-engineered the solution when you didn't really even have a problem yet, this was done preemptively.
This transcript was recorded as part of Swiftly Speaking. You can watch the full original episode on YouTube, or subscribe to the audio version on Apple Podcasts.
SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure and A/B test your entire paywall UI without any code changes or app updates.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.