Skip to content
Articles / Development

My Mobile Stack

RS
Randall Sutton
4 min read

I’ve written about my stack for websites and web apps. This one covers mobile.

Why Native

Cross-platform frameworks like React Native and Flutter promise one codebase for both platforms. In practice, you get one codebase that feels native on neither. You spend time working around platform differences instead of embracing them.

Native development means each app gets the UI conventions, performance characteristics, and OS integration that users on that platform expect. It’s more code, but it’s better code — and with modern toolkits, it’s not as much more as it used to be.

Apple: SwiftUI + Swift

SwiftUI is Apple’s declarative UI framework, and it’s the only way I build for Apple platforms now.

The model is straightforward: you describe what the UI should look like for a given state, and SwiftUI handles the rendering. No manual view lifecycle management, no storyboards, no Interface Builder. Just structs that describe views.

struct ProjectCard: View {
    let project: Project

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text(project.name)
                .font(.headline)
            Text(project.status)
                .font(.subheadline)
                .foregroundStyle(.secondary)
        }
        .padding()
    }
}

What makes SwiftUI compelling beyond the declarative model:

  • Previews — Live previews in Xcode that update as you type. Faster iteration than building and running on a simulator.
  • Cross-device — The same SwiftUI code runs on iPhone, iPad, Apple Watch, and Mac. You adapt for screen size and input method, not for entirely different frameworks.
  • OS integration — Widgets, Live Activities, App Intents, and other system features are all SwiftUI-native. You’re not wrapping native APIs through a compatibility layer.

Swift as the language is a given at this point. It’s fast, it’s safe, and its value types with struct and enum make state management predictable. The concurrency model with async/await and actors handles the hard problems without callback hell.

Android: Jetpack Compose + Kotlin

Jetpack Compose is Google’s answer to the same question Apple answered with SwiftUI: how do you build UI declaratively?

The concepts map almost directly. You write composable functions that describe UI based on state. Compose handles the rest.

@Composable
fun ProjectCard(project: Project) {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        Text(
            text = project.name,
            style = MaterialTheme.typography.titleMedium
        )
        Text(
            text = project.status,
            style = MaterialTheme.typography.bodyMedium,
            color = MaterialTheme.colorScheme.onSurfaceVariant
        )
    }
}

Compose gives you:

  • Material Design 3 — Built-in support for Material You, including dynamic color theming from the user’s wallpaper. The design system is baked in, not bolted on.
  • Interop — Compose works alongside existing Android Views, so you can adopt it incrementally. No big rewrite required.
  • Tooling — Android Studio previews, layout inspector, and recomposition debugging. The tooling has caught up to the framework.

Kotlin is the language Google recommends and the one the ecosystem has standardized on. Coroutines handle async work cleanly, null safety catches bugs at compile time, and extension functions keep code readable without inheritance hierarchies.

The Parallel

It’s worth stepping back and noticing what happened. Apple and Google, independently, arrived at nearly the same architecture: a declarative UI framework powered by a modern, null-safe language with structured concurrency.

SwiftUI and Compose are more alike than different. Both use a tree of lightweight value descriptions. Both re-render based on state changes. Both eliminate the imperative UI code that made mobile development tedious for years. If you know one, picking up the other is a matter of learning syntax, not concepts.

Why This Stack

The argument against native has always been cost — two codebases instead of one. Modern declarative frameworks undercut that argument significantly. SwiftUI and Compose are fast to write, easy to maintain, and produce apps that feel right on their platform.

Users can tell when an app isn’t native. The scroll physics are slightly off, the navigation doesn’t match the OS conventions, the keyboard handling has rough edges. Native eliminates all of that.

I’d rather write two apps that each feel perfect than one app that feels like a compromise everywhere.