Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert STAR sample to KMP #1103

Merged
merged 61 commits into from
Jan 11, 2024
Merged

Convert STAR sample to KMP #1103

merged 61 commits into from
Jan 11, 2024

Conversation

ZacSweers
Copy link
Collaborator

@ZacSweers ZacSweers commented Jan 6, 2024

This PR converts the STAR sample into a multiplatform project!

The commits are a little winding, so I'd suggest looking at the files changed view for code and commits just for titles/story telling. The line diff is almost entirely just the updated baseline profile as I fixed the missing startup prof along the way.

Highlights

  • The STAR app now runs on Android and Desktop. This is just a starting point, support for others to continue later.
  • Update to Coil 3.0 alphas (with multiplatform support)
  • Revamped CoilRule project (which is now also multiplatform)
  • Using commonized annotations for both parcelize and dagger worked remarkably well, allowing almost all the impls to live in common sources.
  • Focused just on JVM for now for scoping reasons. Generified or expect/actual'd things where it made sense.
  • Expect/actual for overlay functions actually feels like a really slick indirection pattern, as it can let us use a bottom sheet on mobile and something more appropriate like a dialog for desktop.
  • The Platform.kt pattern works well here.
  • Multiplatform windowSizeClass works nicely: https://github.com/chrisbanes/material3-windowsizeclass-multiplatform
  • Supports dark mode via cmd+U. Quits with cmd+W.
  • Add STAR instrumentation tests to CI.
  • Switch to kotlinx-datetime for time APIs

Rough edges

  • Shared resources don't really have a story in compose multiplatform currently. There's some libraries and tools out there, but out of scope for this PR. Now we just have a Strings.kt file with our three strings.
  • Kapt cannot run on a KMP project with both Android and JVM targets, so we have to do annoying tedium to conditionally enable the JVM target only when requested. Ultimately the better solution would be to move to KSP or split them into separate subprojects.
  • I tried out kotlin-inject as a KMP solution and... there's a lot of missing features that I felt made it a little untenable. Might still be the only KMP solution available for the foreseeable future though.
    • No modules support, everything is a component
    • Obviously no anvil/contributing support
    • Multibindings must be provided in the host component directly. Unclear how to provide lazy multibindings like Map<Class, Provider<Activity>>.
  • IDE completions often didn't know about common sources that could be imported, namely annotations.
  • KSP cannot see through typealias'd actual annotations 🤔. This resulted in my needing to support a lenient mode that allows for short name matching on the annotation rather than the fully qualified one. Filed KSP does not read actualized typealias declaration google/ksp#1676.
  • Desktop-specific - unclear where to store persistent data like a DB or token storage. Currently uses an in-memory DB and FakeFileSystem for token storage 🤷.
  • The IDE seems to use significantly more memory when doing a lot of KMP work. I was maxing out all 4gb of memory assigned to Android Studio in this branch.

TODO

  • Filter overlay on Desktop
  • Touch up info screen padding
  • Touch up landscape detail page
  • Escape key as a back nav event on desktop seems flaky at best, probably worth putting back the nav icon.
  • Finish getting this working in CI. It's annoying because check invokes the double kapt jvm/android issue. Probably need to just disable the desktop target by default and make it opt-in for building locally.
output.mp4

@@ -31,11 +31,17 @@ org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.caching=true

# Disable noisy DAGP stability warning
dependency.analysis.compatibility=NONE
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opportunistic noise cleanup

kct = "0.4.0"
kotlin = "1.9.22"
kotlinpoet = "1.15.3"
kotlinx-coroutines = "1.7.3"
ksp = "1.9.22-1.0.16"
ktfmt = "0.46"
ktor = "2.3.7"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required for Coil 3, backed by OkHttp on JVM

leakcanary-android = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" }
leakcanary-android-instrumentation = { module = "com.squareup.leakcanary:leakcanary-android-instrumentation", version.ref = "leakcanary" }

lints-compose = "com.slack.lint.compose:compose-lint-checks:1.2.0"

material = "com.google.android.material:material:1.11.0"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required for M3 themes in themes.xml

@@ -1,12 +1,25 @@
// Copyright (C) 2022 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
/*
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed this header while I was at it

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These theme files are just moved from the star library, unchanged


@ContributesBinding(AppScope::class)
@SingleIn(AppScope::class)
class InjectedTokenStorage @Inject constructor(storage: Storage<Preferences>) :
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately it's not enough to just have one contributed binding in commonJvm and instead needs to be duplicated in each target

fun ListBenchmarksItemPreview() {
Box { ListBenchmarksItem(ListBenchmarksItemScreen.State(0)) }
}
// @Preview
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previews are annoyingly not multiplatform compatible. Need to figure out a good solution for these

@CircuitInject(screen = AboutScreen::class, scope = AppScope::class)
@Composable
fun About(modifier: Modifier = Modifier) {
Scaffold(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scaffold was redundant, removed opportunistically

*/
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun HorizontalPagerIndicator(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copied as accompanist isn't multiplatform

iconButtonContent: @Composable () -> Unit,
) {
// We do nothing on desktop
// TODO maybe we do escaping?
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd need to do something that sends an escape event to the window, as that's sorta how I've implemented backstack popping here

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think we'll have to create a back press local so that it can get triggered correctly on the window 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I think it's out of scope for this PR as a start anyway

@ZacSweers

This comment was marked as resolved.

samples/star/README.md Outdated Show resolved Hide resolved
} ?: Modifier
} ?: Modifier
} else {
Modifier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure whats needed but it'd be nice to swipe on the pager via mouse event, or have the little side arrows on desktop.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fun fact that's already implemented, works on android too :P. You can see it in action in the video when the images stop scrolling and start snapping

iconButtonContent: @Composable () -> Unit,
) {
// We do nothing on desktop
// TODO maybe we do escaping?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think we'll have to create a back press local so that it can get triggered correctly on the window 🤔

@ZacSweers ZacSweers added this pull request to the merge queue Jan 11, 2024
Merged via the queue into main with commit ed96619 Jan 11, 2024
4 checks passed
@ZacSweers ZacSweers deleted the z/kmpStar branch January 11, 2024 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants