Blog Post

A Tryst with SwiftUI, Or: How I Learned to Stop Worrying and Enjoy Building UIs

Illustration: A Tryst with SwiftUI, Or: How I Learned to Stop Worrying and Enjoy Building UIs

SwiftUI is one of the shiny new tools used for developing apps on Apple that we recently started using at PSPDFKit. It comes from a different school of building user interfaces (UIs), which is the declarative style.

SwiftUI has been the center of some very interesting discourse over the last few years. That said, the intention of this post isn’t to rehash any of those topics. Instead, I’ll be talking about my experience using it extensively for the first time for building a component that’s a part of the PSPDFKit for iOS SDK.

Experimenting with SwiftUI

Late last year, we on the iOS team decided to revamp the UI that enables sharing documents. The UI is controlled by a configuration API that shows the options to be displayed on the screen.

We decided to build the revamped UI using SwiftUI, as it seemed fairly simple (or “mostly harmless,” as Ford Prefect might have said). In March 2021, we shipped our Electronic Signatures component, which was from built from the ground up using SwiftUI, so this wasn’t the first time we’ve done this. That project also worked out well, and we were able to gather some firsthand SwiftUI expertise as a result. I personally hadn’t dabbled with SwiftUI all that much, but when building the sharing UI, it was a perfect opportunity to do so. Better late than never, eh?

So, I rolled up my metaphorical sleeves (it was too warm for actual ones) and got right into it.

I created a new empty project in SwiftUI and started building the UI for the new sharing flow. In the process of using SwiftUI for our Electronic Signatures component, we quickly learned that it’s a good idea to begin building the UI in a new empty project if we want to leverage SwiftUI Previews, which is why I did the same this time around. The reasoning for this is because the codebase of our SDK is huge, with a mixture of both Objective-C and Swift — primarily the former — which doesn’t allow the previews to work reliably.

It was very satisfying to see changes in the previews immediately. This has the benefit of making experimenting easier and more accessible. I was constantly tweaking things and trying out the different modifiers available to see how they made the UI behave. This helped not only with experimentation, but also in learning more about the APIs available for SwiftUI itself.

SwiftUI’s declarative style of building the UI involves a different way of thinking, and this approach wasn’t something I was used to. However, once I let go of my hesitation and just started playing around with things, it was easy to get into the flow of it. I created a blank SwiftUI view, approaching it as an empty playground. Then I started adding the other UI components necessary for building the sharing UI without thinking much about the structure of the code.

That said, I struggled with structuring the code so that it’s maintainable. One reason for this is that best practices for SwiftUI aren’t yet well defined, as the technology is new, and most developers are still trying different things to see which ones should be favored. This is where our team’s previous experience with using SwiftUI came in handy; it helped me in better architecting the code, as I was able to ask for help and get valuable feedback from others.

The new UI itself was fairly straightforward, but there were some basic components that we take for granted when using mature frameworks like UIKit — like the different table cell types and the multi-component picker — that were missing in SwiftUI. These were indeed points of annoyance. However, it was also incredibly easy to build these custom components ourselves, which took a lot less time than I had anticipated.

We also didn’t have to spend additional time making the UI accessible and RTL compatible, as those came for free, with the exception of a couple of places where we made custom adjustments.

When the time came for integrating the newly built UI back into our project, it seemed to me like the communication between SwiftUI and UIKit wasn’t well defined, as I was still learning my way around SwiftUI.

We had to use a Swift bridge to use SwiftUI in Objective-C. However, I anticipate this problem will eventually not be an issue, as we’re modernizing our codebase with more Swift.

Another difficulty along these lines was that of defining customization hooks for a view built using SwiftUI. It isn’t as easy as subclassing a view controller to add more views in the desired places. SwiftUI views are tightly bound to the state management system for its views, so there’s little scope of adding customization hooks for customizing the existing views being displayed or adding new ones. Everything has to be done by manipulating the state of SwiftUI views for customizing the existing views. This was not as easy as it is using UIKit.

On the flip side, the declarative style of building and the state management system that we built for our new UI helped us in squashing a lot of the smaller issues related to the UI and the underlying state not being in sync, all of which had gone unnoticed earlier.

Testing and Tweaking

As we neared the finish line for development, we began updating our tests for this freshly baked UI.

Testing views built using SwiftUI seemed difficult at first, and it wasn’t easy to interact with the UI to carry out an action. We most likely would’ve had to modify the state and see if the UI was updated accordingly.

However, it wasn’t what we needed, because we test an entire flow from beginning to end to verify the final result, along with our private internal states. For this, we need to be able carry out a UI interaction. We use EarlGrey for our UI tests, and it turns out that using the accessibility labels for interacting with the UI was good enough and gave us what we wanted.

SwiftUI works across all Apple platforms, and this made adding support for Mac Catalyst a breeze. We had some issues with UIPickerView on Catalyst when working on another project, which is why we had to resort to using a text field. However, enter SwiftUI, and this just worked on Catalyst out of the box.

We tracked this issue of ensuring proper support of the picker on Catalyst, which now was no longer needed, in turn making our work a tiny bit easier: one item checked off the to-do list for free.

The next step was to get feedback from our Design team, which had designed this UI in the first place. There were a few things that needed to tweaked, including a couple of minor layout changes. The turnaround time for these updates was extremely short. I had gotten the hang of SwiftUI by this point, and some of the things which would’ve taken more than a few lines with UIKit sometimes took only a line or two.

The new UI is now out as of version 12.1.0, making this the second PSPDFKit component built completely using SwiftUI.


Using SwiftUI ties in very well with one of our core values of experimenting and being willing to embrace the latest technology. This enabled us to take steps toward modernizing our SDK, as well as gaining more SwiftUI expertise within the team.

Whether or not to use SwiftUI for a project depends on the use case, so everyone’s mileage with it will vary. During WWDC23, newer capabilities such as SwiftData for data modeling and management, animation APIs in the form of PhaseAnimator, and more have been added, making it a compelling choice, if it wasn’t already.

Overall, my experience was that it was an absolute joy using SwiftUI for this particular component — a joy a lot similar to the one I had felt while building a UI for the first time using Objective-C and UIKit. Surely, there were some moments of frustration, but overall it was, dare I say, a pleasant experience.

Related Products
Share Post
Free 60-Day Trial Try PSPDFKit in your app today.
Free Trial

Related Articles

Explore more
DEVELOPMENT  |  iOS • Swift • SwiftUI • Insights

Our Attempt at Rewriting PSPDFKit Catalog in SwiftUI

DEVELOPMENT  |  iOS • SwiftUI • Tips

Presenting Popovers on iPhone with SwiftUI


SwiftUI In Production