Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Instant Apps Eulogy

Instant Apps Eulogy

Last year we asked ourselves a question. One that most Android engineers avoid whispering aloud: Are Instant Apps still a thing? It turns out that Google officially said no! Instant Apps are fascinating because they offer possibilities outside of what Google originally engineered them for.

Benoît (Cash App) and Cyril (amo), both of us shipped an Instant App this year for different use cases and different implementations. The restrictions required to publish an Instant App encourage dynamic, lean and performant architectures. Savoir-faire which stays relevant even outside the context of Instant Apps.

After hovering briefly over the state of Instant Apps, we’ll share how we built and shipped our projects. From development quirks to publishing oddities, we’ll highlight what worked, what didn’t, and why Instant Apps might have deserved a second look.

Avatar for Cyril Mottier

Cyril Mottier

August 04, 2025
Tweet

More Decks by Cyril Mottier

Other Decks in Technology

Transcript

  1. ~2015 Web Native Push Noti fi O ff Device HW

    access Distribution* N/A N/A N/A 🚀 🚀 🚀 🚀 😬
  2. 2015~ PWA Native Push Noti fi O ff Device HW

    access Distribution* 🐌 🐌 🐌 🚀 🚀 🚀 🚀 😬
  3. 2016 PWA Native Push Noti fi O ff Device HW

    access Distribution* 🛫 🛫 🛫 🚀 🚀 🚀 🚀 😬
  4. AIA

  5. 2016~ PWA Android AIA Push Noti fi O ff Device

    HW access Distribution* 🛫 🛫 🛫 🚀 🚀 🚀 🚀 🐌
  6. 2020~ PWA Android AIA Push Noti fi O ff Device

    HW access Distribution* 🛫 🛫 🛫 🚀 🚀 🚀 🚀 🐌 iOS App clips 🚀 🚀 🚀 👀
  7. 2025 PWA Android AIA Push Noti fi O ff Device

    HW access Distribution* 🛫 🛫 🛫 🚀 🚀 🚀 🚀 🐌 iOS App clips 🚀 🚀 🚀 🛫
  8. monorepo ├── android │ ├── apps │ ├── build-logic │

    ├── components │ │ ├── focus │ │ │ ├── api │ │ │ ├── bindings │ │ │ └── lib │ │ ├── super-sends-hearts │ │ │ ├── api │ │ │ ├── bindings │ │ │ └── lib │ │ └── … │ ├── foundation │ ├── platform │ └── … └── …
  9. @Provides fun provideSettingsConfiguration(): SettingsConfiguration = SettingsConfiguration( socialMedia = listOf( SocialMedia.Instagram(

    “bumpbyamo", R.string.socialMedia_instagram), ), ), ) dependencies { implementation( projects.components.settings.bindings ) } android/apps/location/src/main/LocationAppModule.kt android/apps/location/build.gradle.kts and optionally…
  10. dependencies { happyImplementation( projects.components.superSecretNewFeature.bindings ) } android/apps/location/build.gradle.kts dependencies { implementation(

    projects.components.avatar.bindings ) } android/apps/location-instant/build.gradle.kts dependencies { implementation( projects.components.avatar.bindings ) } android/apps/location/build.gradle.kts
  11. dependencies { happyImplementation( projects.components.superSecretNewFeature.bindings ) } android/apps/location/build.gradle.kts dependencies { implementation(

    projects.components.avatar.bindings ) } android/apps/location-instant/build.gradle.kts dependencies { implementation( projects.components.avatar.bindings ) } android/apps/location/build.gradle.kts
  12. Android Kotlin • Compose iOS Swi ft • UIKit/Swi ft

    UI Core Rust • SQLite RxSwi ft streams Flows & suspend
  13. Android Kotlin • Compose iOS Swi ft • UIKit/Swi ft

    UI Core Rust • SQLite RxSwi ft streams Flows & suspend
  14. Android Kotlin • Compose iOS Swi ft • UIKit/Swi ft

    UI Core Rust • SQLite RxSwi ft streams Flows & suspend iOS app
  15. Android Kotlin • Compose iOS Swi ft Core Rust •

    SQLite RxSwi ft Flows & suspend
  16. Android Kotlin • Compose iOS Swi ft Core Rust •

    SQLite RxSwi ft Flows & suspend Android app
  17. Acts as a local backend from a UI PoV Deals

    with persistance and networking Gets and sends proto models via gRPC Exposes APIs to Kotlin/Swi ft through FFI Rust library function
  18. Acts as a local backend from a UI PoV Deals

    with persistance and networking Gets and sends proto models via gRPC Exposes APIs to Kotlin/Swi ft through FFI Rust library function
  19. Acts as a local backend from a UI PoV Deals

    with persistance and networking Gets and sends proto models via gRPC Exposes APIs to Kotlin/Swi ft through FFI Rust library function
  20. Acts as a local backend from a UI PoV Deals

    with persistance and networking Gets and sends proto models via gRPC Exposes APIs to Kotlin/Swi ft through FFI Rust library function
  21. Acts as a local backend from a UI PoV Deals

    with persistance and networking Gets and sends proto models via gRPC Exposes APIs to Kotlin/Swi ft through FFI Rust library function
  22. Acts as a local backend from a UI PoV Deals

    with persistance and networking Gets and sends proto models via gRPC Exposes APIs to Kotlin/Swi ft through FFI Rust library function
  23. Rust library function in an instant app Acts as a

    local backend from a UI PoV Deals with persistance and networking Gets and sends proto models via gRPC Exposes APIs to Kotlin/Swi ft through FFI
  24. plugins { id("co.amo.application.android.instant") } amoApplication { applicationDomain = "bumpmaps.com" applicationDeeplinkScheme

    = "location" } android { namespace = "co.amo.location.instant" defaultConfig { applicationId = "co.amo.android.location" base.archivesName = "location-instant-$versionName" } } dependencies { // ... }
  25. plugins { id("co.amo.application.android.instant") } amoApplication { applicationDomain = "bumpmaps.com" applicationDeeplinkScheme

    = "location" } android { namespace = "co.amo.location.instant" defaultConfig { applicationId = "co.amo.android.location" base.archivesName = "location-instant-$versionName" } } dependencies { // ... }
  26. plugins { id("co.amo.application.android.instant") } amoApplication { applicationDomain = "bumpmaps.com" applicationDeeplinkScheme

    = "location" } android { namespace = "co.amo.location.instant" defaultConfig { applicationId = "co.amo.android.location" base.archivesName = "location-instant-$versionName" } } dependencies { // ... }
  27. plugins { id("co.amo.application.android.instant") } amoApplication { applicationDomain = "bumpmaps.com" applicationDeeplinkScheme

    = "location" } android { namespace = "co.amo.location.instant" defaultConfig { applicationId = "co.amo.android.location" base.archivesName = "location-instant-$versionName" } } dependencies { // ... }
  28. internal fun Project.configureApplicationVersion( applicationExtension: ApplicationExtension, isInstantApp: Boolean = false, )

    { val applicationType = if (isInstantApp) 0 else 1 applicationExtension.apply { defaultConfig { with(gitVersion.semVer) { // 2_100_000_000 (Play Store maximum version code) // M_MMM_mmm_PPA } } } } versionCode = major * 1_000_000 + minor * 1000 + patch * 10 + applicationType versionName = "$major.$minor.$patch" if (isInstantApp) { versionNameSuffix = "-instant" }
  29. internal fun Project.configureApplicationVersion( applicationExtension: ApplicationExtension, isInstantApp: Boolean = false, )

    { val applicationType = if (isInstantApp) 0 else 1 applicationExtension.apply { defaultConfig { with(gitVersion.semVer) { // 2_100_000_000 (Play Store maximum version code) // M_MMM_mmm_PPA } } } } versionCode = major * 1_000_000 + minor * 1000 + patch * 10 + applicationType versionName = "$major.$minor.$patch" if (isInstantApp) { versionNameSuffix = "-instant" }
  30. versionCode = major * 1_000_000 + minor * 1000 +

    patch * 10 + applicationType versionName = "$major.$minor.$patch" if (isInstantApp) { versionNameSuffix = "-instant" } Main app Instant app Version code Version name 2000241 2.0.24 2000240 2.0.24-instant
  31. Dynamic Features - how? val manager = SplitInstallManagerFactory.create(context) val request

    = SplitInstallRequest.newBuilder() .addModule(“my_feature”) .build() manager.startInstall(request) manager.registerListener {}
  32. Instant app total download size ∞ Local environments 15MB Play

    Store tracks It’s actually more complex, but who cares now? (›°□°)› ︵ ᵲᴸᵲ
  33. Slim down your binary Dependencies reduction Code mini fi Vector

    graphics Resources de-duplication Resource shrinking Downloadable fonts Assets manual rendering Remote fetching Images optimisation WebP format Granular dependencies ABI removal Sparse translation elimination Avoid (Rust) native Dynamic features Treeshaking Languages removal