Video hosted by Apple at devstreaming-cdn.apple.com

Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: meet-with-apple-274
$eventId
ID of event: meet-with-apple
$eventContentId
ID of session without event part: 274
$eventShortId
Shortened ID of event: meet-with-apple
$year
Year of session: 2026
$extension
Extension of original filename: mp4
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

Meet with Apple • Session 274

AllTrails: Momentum without a rewrite

2026-02-12 • iOS, watchOS • 14:14

Go behind the scenes with AllTrails CTO James Graham to learn how his team brought SwiftUI into their UIKit app. Explore strategies for incremental adoption that let you build new features faster while maintaining your existing codebase. And find out how SwiftUI’s full interoperability with UIKit makes it a practical choice for your app.

This session was originally presented as part of the Meet with Apple activity “SwiftUI foundations: Build great apps with SwiftUI”. Watch the full video for more insights and related sessions.

Speaker: James Graham

Open in Apple Developer site

Part of: SwiftUI foundations: Build great apps with SwiftUI

Transcript

Good afternoon everyone. I have a question for you. Maybe this resonates. Have you ever looked at your legacy UIKit code base? Maybe a huge view controller written four years ago and thought, yikes, this needs a serious refactor. Let’s just put a pin in this and rebuild the whole thing in SwiftUI. Quick show of hands. Has something like this happened to you?

Wow. A lot. I see a lot of a lot of hands raised. I’m sure there’s more online watching. It’s a tempting thought, but at the scale we operate a rewrite, whether it’s from an engineer’s effort or an AI native workflow, it introduces a huge risk. So hi, my name is James Graham.

I’m the CTO at Alltrails, and today I want to talk to you about how we achieved modern velocity without a rewrite. I want to show you how we let SwiftUI adopt us rather than forcing adoption from the top down. Before we dive in, here’s the shape of the story.

First, I’ll share what alltrails is, our scale and our constraints. Then I’ll talk about how SwiftUI entered our code base without a rewrite or a mandate. After that, I’ll show you the tipping point. When SwiftUI stopped being an experiment and started becoming the default choice. And finally, I’ll close with what things look like today and how we view our hybrid architecture.

To understand our technical decisions, you have to understand our scale. Today, Alltrails is the world’s most popular and trusted platform for outdoor exploration. Our mission is simple to help the world find its way outside. We help people discover trails, navigate confidently and elevate their experiences on the trail, with up to date details of the trail and features like photo tour, which highlights photos along the way, whether it’s a walk in your local park or a multi-day hike. We’ve got you covered. Alltrails has over 90 million community members. We have 500,000 trails worldwide, and our members have logged over 1.9 billion miles.

We’re available in 14 languages, and this means every technical decision we make affects millions of members across different devices, different regions, and, crucially, different connectivity levels. We serve a wide spectrum of members with different interests and preferences. On one hand, we have the casual member looking for an easy and relatively flat afternoon walk with a nice view of a local lake, and on the other, we have an avid hiker who’s tackling an all day Half dome hike, navigating offline with no cell phone service. That diversity creates strict constraints around reliability, battery life, and UI performance. We cannot ship broken code, but our app isn’t static. It’s constantly evolving with new surfaces and new feature depth.

By the time SwiftUI arrived, it promised Alltrails was already a very large and mature, successful UIKit app. As an example of that evolution, here’s our home page over the years. We were shipping with weekly release cycles, and we were powering both free and paid experiences. And here’s the most important thing that I’ll say about our legacy code. UIKit wasn’t a problem to be fixed. It was the foundation that enabled our scale. Because of that, we couldn’t shut down the trail and make changes. We had to maintain it and we had to upgrade it mid hike.

When SwiftUI arrived, it promised things we desperately wanted. Cleaner state management views that automatically update and when data changes, eliminating that whole class of bugs where UI falls out of sync with the model less code. We’re talking about a 40% reduction compared to UIKit equivalents. That’s 40% less code to maintain, to update, and to read live previews. The ability to iterate on design changes instantly.

But rewriting a mature app wasn’t an option. Technically or organizationally. We needed a different approach. So SwiftUI entered our code base quietly. It wasn’t a mandate or a roadmap item. We created a sandbox, and we used it for low risk experimentation for prototypes and isolated services. It gave us a place to learn the framework without betting our release candidate on it. The first real decision wasn’t UIKit or SwiftUI. It was how do they thrive together? We invested early in interoperability. Take a look at this code snippet here.

The oh yeah, this is our bridge. We take SwiftUI feature like our trail coordinator. We wrap it in a hosting view, and we drop it right into a standard UIKit Stack View. And then we add it to the scroll view. As we scroll down the page here, you can see some sections of the hosting view that contain the SwiftUI Subviews. We established this pattern early.

This was a process decision, ensuring the boundaries were clear and the two worlds could speak the same language over time. Two parallel tracks form naturally. UIKit still handles the heavy lifting for us app lifecycle navigation, and complex or deeply integrated surfaces like our trail page or our Community Activity View.

In a mature app, it didn’t make sense to rewrite stable, battle tested screens just to change frameworks, so we avoided rerouting of a well-marked trail mid hike and focused our new investments where it delivered clear user and velocity gains on the SwiftUI side. We intentionally are using it where it fits best on those isolated, well bounded surfaces, rendering heavy views and new experiments.

You can see two examples of that here. The trail review flow is a self-contained surface with dynamic state and UI updates, which maps well to Swiftui’s declarative model. The Ask the trail Anything experience built on Apple Intelligence is a newer, more experimental service. SwiftUI lets us iterate quickly here and evolve the experience without coupling it tightly to the app’s core architecture.

Another area where SwiftUI shines very bright as our design system. Let’s take a look at our app in debug mode that visualizes our design system called Denali. Denali is growing larger every day, and we’ve partnered across our design, system, design and engineering teams to ensure all new features leverage this system.

All of our core components, buttons, segments, controls, badges that you see here are part of our design system, viewable in our app in debug mode and now built in SwiftUI. What used to take hundreds of lines of boilerplate. Now take a fraction of that. And when we need to add a new variant or adjust spacing, it’s a simple change that propagates everywhere. SwiftUI allows us to scale our design system without bloating our code base.

We also noticed something unexpected happened when we adopted SwiftUI. It started influencing our architecture. View models got smaller, often by a third, because we stopped writing glue code just to keep the UI and the state in sync. We also wrote a lot less explicit plumbing, fewer publishers, fewer operators, and far less lifecycle management because SwiftUI handled state propagation for us. And because UI, state and behavior lived together, changes touched fewer lines. Our UI. Pull request were 30 to 40% smaller and code reviews got noticeably faster.

That’s when SwiftUI stopped feeling like a UI experiment and started feeling like the right way to build systems. We never forced engineers to use SwiftUI. They chose it for their new work because it had less friction. It lowered the cognitive overhead. When the choice is right, 40% less code adoption becomes self-sustaining. Here’s the takeaway for us. SwiftUI didn’t spread because we told people to use it. It spread because it was the fastest path forward. When a framework lowers cognitive overhead and removes complexity, engineers don’t need convincing. We just reach for it.

But the code base, it didn’t flip overnight. It tilted. UIKit remains deeply embedded. SwiftUI grows around it. We measure success metrics like reduction in lines of code per feature, faster iteration cycles, and fewer regressions in those isolated SwiftUI features. We realized that interoperability is infrastructure. We invested in hosting wrappers, shared animation, bridges, and unified theming. When the bridge is solid, SwiftUI stops feeling new and starts feeling foundational.

A perfect example of this is our Apple Watch app. Both our Compass and Map surfaces are built with SwiftUI, and our map uses Mapkit. This demonstrates how we can ship critical performance sensitive functionality using modern architecture. We chose SwiftUI here not because it was cool, but because it allowed us to iterate faster on a complex surface without touching the legacy navigation logic.

So where are we today? All trails isn’t migrated to SwiftUI. You don’t need to rewrite your whole app to get the benefits. We’re a hybrid system with direction. UIKit provides stability. It’s still fantastic for deep UI customization like complex collection views or intricate navigation bars. SwiftUI defines our growth. It’s catching up fast, and our plant identification feature seen here is 100% SwiftUI.

So everything I’ve described so far is specific to all trails our scale, our members, our constraints. And that’s intentional. The common mistake teams make is treating adoption like a binary decision. Should we adopt SwiftUI? The better framing is. Under what conditions does adoption create momentum instead of risk?

When we looked at what actually worked for us fewer bugs, faster delivery, better cross team scaling. It wasn’t a single technical choice. It was a set of decisions made deliberately. One question I hear a lot is can UIKit and SwiftUI coexist safely? And from what I’ve shown today, the answer is most certainly yes.

So instead of telling you what to adopt, I’ll leave you with three questions you can use to evaluate adoption in your own context. One is interoperability treated as infrastructure. If interoperable, interoperability is fragile or ad hoc. Adoption will stall the moment real product pressure hits two. Does the new tool reduce cognitive overhead? When a tool genuinely simplifies mental load, adoption doesn’t need enforcement. Engineers will choose it voluntarily.

And three. Are you measuring momentum and not conversion? Momentum shows up in smaller pairs, faster reviews, and fewer regressions. Not in how much of your code base you’ve converted. So if there’s a takeaway here, it’s that you don’t need a rewrite to make progress. You need direction. So thanks so much for your time today and for letting me share the All Trails story. We’ll see you on the trail.