Getting to Know Jetpack Compose by Comparing Concepts with SwiftUI (Markus Müller's CocoaHeads Leipzig Presentation)

Markus Müller (@m_mlr) recently did a presentation at CocoaHeads Leipzig about Jetpack Compose. – Thanks for the inspiring presentation, Markus! I learned a lot.

Update 2023-01-24: Check out the sample project on GitHub!

The similarities are interesting, but the differences in the build tools was also striking. The JetBrains IDE did a great job at hiding noise and presenting useful information, especially when it comes to locating and then auto-importing the various modules that comprise a modern Android app written in Kotlin and using Jetpack Compose.

I took the chance to try a Boox Nova Air 2 (affiliate link) for monochrome, multi-layered “sketchnotes” (more on that in a future post):

Four concatenated "pages" worth of notes, scaled down to 10% (!) of the original size.

My personal highlights follow – please bear in mind this is all hearsay and based on a presentation + looking at some sample code a couple months ago, so I’m by no means an expert! The purpose of this list is to mention important concepts and provide links for further reading.

  • To make UI components remember their state, you need to look into “state hoisting” for view serialization
    • Scary term for a simple concept:

      In Jetpack Compose, state hoisting means moving the state of a composable to its parent (caller). It helps us to reuse the composables. (Source)

      This is actually not dis-similar to how a SwiftUI view has a @State, and its children only get the derived observables.

    • On (very) old Android API versions, text fields by default used to forget their contents when you rotated the devices vigorously. Ouch.
    • Hoisting via a “remember Saveable” is similar to Codable in Swift. But applied to UI components.
    • Unidirectional flow approaches (think: Redux, ReSwift, The Composable Architecture/TCA) can help remove the problem for you. If the component isn’t the state’s source of truth, you can’t lose its state, right? (This could actually be an over-simplification, I don’t know from experience.)
  • Where SwiftUI implements view modifiers as chained method calls, figuring out which modifier works where through the power of protocols, Jetpack Compose’s components provide run-of-the-mill injectable Modifier objects.
    • Modifiers in SwiftUI chain on the view object;
    • Modifiers in Jetpack Compose chain the view input.
    • Potential upside: no surprises when you change the modifier order. You can lose the type information of SwiftUI.Text-specific modifiers when you use general-purpose SwiftUI.View modifiers in some circumstances. That’s what I’m thinking of.
  • StateFlow and MutableStateFlow unify the concepts and benefits that SwiftUI gets from Combine publishers and structured concurrency (async/await). – Don’t ask me how, that’s just what I took note of :)

  • Theming is interesting! The App object is initialized inside the Theme:

     Theme {
         App { ... }
     }
    

    That sounds odd at first, but it makes sense that an app lives in the context of a global app theme. Theme changes affect all of the app. We don’t have that sort of theming in iOS and instead tweak the default look and feel of components with e.g. (shared) view modifiers. As an upside, SwiftUI view modifiers can affect a whole view sub-tree; it looked like you need to tell some Compose view components how to theme themselves a bit more when you want to deviate from the theme’s defaults.

  • The SwiftUI documentation pales in comparison. Apple docs imporoved in recent years, but it’s not fair how much better the Jetpack docs are.
    • The Jetpack Compose documentation is live-filterable,
    • a lot of pages come with sample project links,
    • the sample projects reside on GitHub,
    • and a huge focus is to get the idea across in a way that a new developer would understand it.
    • High-level overviews and samples take precedence over (rather boring) API method listings.
  • Jetpack Compose is an external library with a dedicated life-cycle, not tied to Android OS version upgrades. I wish SwiftUI were that flexible.