Skip to content

Commit

Permalink
Initial iOS support (slackhq#583)
Browse files Browse the repository at this point in the history
This does an initial integration of support for iOS in our core
artifacts. This is very much intended just to be a toe-hold and not
anything meaningful or commitment, but rather a step similar to when we
first moved into multiplatform where it can help keep the door open for
later work by keeping us honest in our implementation of circuit
internals cross-platform friendly now.

This does a bunch of that ground work, namely around preparing the
`counter` sample for this and making some needed infra changes in the
core artifacts to support it.

- Switch to using a multiplatform UUID library in backstack. Using the
sort of canonical one in the community until Kotlin adds one to the
stdlib (https://youtrack.jetbrains.com/issue/KT-31880).
- Don't use java reflection in common code
- Set up `ios` (which includes `iosX64` and `iosArm64`) and
`iosSimulatorArm64` (needed for simulator runs) in projects + `actual`
declarations where needed
- I don't know what `Screen` will look like in iOS. There's `NSCoder`
maybe? Saving for future work.
  - Promote `ContentWithOverlays` to `commonMain`.
  - iOS `rememberRetained` is also inert like on JVM.
- Started an iOS `Counter` app and dir. This is super basic and uses a
KMM circuit presenter + SwiftUI. It does not try to do a full
`CircuitContent` or anything related. Most relevant bits are in
`ContentView.swift` and `SwiftSupport.kt`.
- The multiplatform counter presenter now produces a swift framework for
use in the above app. Not sure if we wanna make that sample use compose
iOS's UI or try it with swift UI or do both.



https://user-images.githubusercontent.com/1361086/235322804-7adbf0a5-57af-4f0d-84a9-5ddee6240d28.mp4
  • Loading branch information
ZacSweers authored May 8, 2023
1 parent fc60d93 commit 0fafc40
Show file tree
Hide file tree
Showing 60 changed files with 1,075 additions and 121 deletions.
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,12 @@ docs/contributing.md
docs/changelog.md
docs/api/
site/
temp-clone/
temp-clone/

.externalNativeBuild
Podfile.lock
**/Pods/*
**/*.xcworkspace/*
**/*.xcodeproj/*
!samples/counter/apps/Counter.xcodeproj/project.pbxproj
*.podspec
6 changes: 6 additions & 0 deletions backstack/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
plugins {
id("com.android.library")
kotlin("multiplatform")
alias(libs.plugins.compose)
id("com.vanniktech.maven.publish")
}

kotlin {
// region KMP Targets
android { publishLibraryVariants("release") }
jvm()
ios()
iosSimulatorArm64()
// endregion

sourceSets {
Expand All @@ -18,6 +21,7 @@ kotlin {
api(libs.compose.runtime)
api(libs.compose.ui)
api(libs.coroutines)
implementation(libs.uuid)
implementation(libs.compose.runtime.saveable)
}
}
Expand All @@ -37,6 +41,8 @@ kotlin {
}
}
maybeCreate("jvmTest").apply { dependsOn(commonJvmTest) }
val iosMain by getting
val iosSimulatorArm64Main by getting { dependsOn(iosMain) }
}
}

Expand Down
2 changes: 2 additions & 0 deletions backstack/dependencies/androidReleaseRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ androidx.savedstate:savedstate
androidx.startup:startup-runtime
androidx.tracing:tracing
androidx.versionedparcelable:versionedparcelable
com.benasher44:uuid-jvm
com.benasher44:uuid
com.google.guava:listenablefuture
org.jetbrains.compose.runtime:runtime-saveable
org.jetbrains.compose.runtime:runtime
Expand Down
2 changes: 2 additions & 0 deletions backstack/dependencies/jvmRuntimeClasspath.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
com.benasher44:uuid-jvm
com.benasher44:uuid
org.jetbrains.compose.runtime:runtime-desktop
org.jetbrains.compose.runtime:runtime-saveable-desktop
org.jetbrains.compose.runtime:runtime-saveable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import java.util.UUID
import com.benasher44.uuid.uuid4

@Composable
public fun rememberSaveableBackStack(init: SaveableBackStack.() -> Unit): SaveableBackStack =
Expand Down Expand Up @@ -62,7 +62,7 @@ public class SaveableBackStack : BackStack<SaveableBackStack.Record> {
public data class Record(
override val route: String,
val args: Map<String, Any?> = emptyMap(),
override val key: String = UUID.randomUUID().toString()
override val key: String = uuid4().toString(),
) : BackStack.Record {
internal companion object {
val Saver: Saver<Record, List<Any>> =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.slack.circuit.backstack

import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.compositionLocalOf

internal actual val LocalBackStackRecordLocalProviders:
ProvidableCompositionLocal<List<BackStackRecordLocalProvider<BackStack.Record>>>
get() = compositionLocalOf { emptyList() }
78 changes: 42 additions & 36 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import org.jetbrains.compose.ComposeExtension
import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.internal.KaptGenerateStubsTask
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin
import org.jetbrains.kotlin.gradle.plugin.NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME
import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile

plugins {
alias(libs.plugins.kotlin.jvm) apply false
Expand Down Expand Up @@ -136,51 +139,46 @@ subprojects {

val hasCompose = !project.hasProperty("circuit.noCompose")
plugins.withType<KotlinBasePlugin> {
tasks.withType<KotlinCompile>().configureEach {
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions {
allWarningsAsErrors.set(true)
jvmTarget.set(JVM_11)

// Stub gen copies args from the parent compilation
if (this@configureEach !is KaptGenerateStubsTask) {
freeCompilerArgs.addAll(
"-progressive",
"-Xinline-classes",
"-Xjsr305=strict",
"-opt-in=kotlin.contracts.ExperimentalContracts",
"-opt-in=kotlin.experimental.ExperimentalTypeInference",
"-opt-in=kotlin.ExperimentalStdlibApi",
"-opt-in=kotlin.time.ExperimentalTime",
// We should be able to remove this in Kotlin 1.7, yet for some reason it still warns
// about its use
// https://youtrack.jetbrains.com/issue/KT-52720
"-opt-in=kotlin.RequiresOptIn",
// Match JVM assertion behavior:
// https://publicobject.com/2019/11/18/kotlins-assert-is-not-like-javas-assert/
"-Xassertions=jvm",
// Potentially useful for static analysis tools or annotation processors.
"-Xemit-jvm-type-annotations",
"-Xproper-ieee754-comparisons",
// Enable new jvm-default behavior
// https://blog.jetbrains.com/kotlin/2020/07/kotlin-1-4-m3-generating-default-methods-in-interfaces/
"-Xjvm-default=all",
// https://kotlinlang.org/docs/whatsnew1520.html#support-for-jspecify-nullness-annotations
"-Xtype-enhancement-improvements-strict-mode",
"-Xjspecify-annotations=strict",
)

if (hasCompose && suppressComposeKotlinVersion) {
if (this is KotlinJvmCompilerOptions) {
jvmTarget.set(JVM_11)
// Stub gen copies args from the parent compilation
if (this@configureEach !is KaptGenerateStubsTask) {
freeCompilerArgs.addAll(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:suppressKotlinVersionCompatibilityCheck=$kotlinVersion"
"-Xjsr305=strict",
// Match JVM assertion behavior:
// https://publicobject.com/2019/11/18/kotlins-assert-is-not-like-javas-assert/
"-Xassertions=jvm",
// Potentially useful for static analysis tools or annotation processors.
"-Xemit-jvm-type-annotations",
// Enable new jvm-default behavior
// https://blog.jetbrains.com/kotlin/2020/07/kotlin-1-4-m3-generating-default-methods-in-interfaces/
"-Xjvm-default=all",
// https://kotlinlang.org/docs/whatsnew1520.html#support-for-jspecify-nullness-annotations
"-Xtype-enhancement-improvements-strict-mode",
"-Xjspecify-annotations=strict",
)
}
}

freeCompilerArgs.add("-progressive")

if (hasCompose && suppressComposeKotlinVersion) {
freeCompilerArgs.addAll(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:suppressKotlinVersionCompatibilityCheck=$kotlinVersion"
)
}
}
}

if (hasCompose) {
dependencies { add(PLUGIN_CLASSPATH_CONFIGURATION_NAME, libs.androidx.compose.compiler) }
dependencies {
add(PLUGIN_CLASSPATH_CONFIGURATION_NAME, libs.androidx.compose.compiler)
add(NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME, libs.androidx.compose.compiler)
}
}

if (!project.path.startsWith(":samples")) {
Expand Down Expand Up @@ -344,6 +342,14 @@ subprojects {
}
}
}
tasks.withType<KotlinNativeCompile>().configureEach {
notCompatibleWithConfigurationCache("https://youtrack.jetbrains.com/issue/KT-49933")
}
@Suppress("INVISIBLE_REFERENCE")
tasks.withType<org.jetbrains.kotlin.gradle.plugin.mpp.apple.FrameworkCopy>().configureEach {
@Suppress("INVISIBLE_MEMBER")
notCompatibleWithConfigurationCache("https://youtrack.jetbrains.com/issue/KT-49933")
}
}
}

Expand Down
1 change: 1 addition & 0 deletions circuit-codegen-annotations/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ kotlin {
// region KMP Targets
android { publishLibraryVariants("release") }
jvm()
// Anvil/Dagger does not support iOS targets
// endregion

sourceSets {
Expand Down
6 changes: 4 additions & 2 deletions circuit-foundation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,23 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("com.android.library")
kotlin("multiplatform")
alias(libs.plugins.compose)
id("com.vanniktech.maven.publish")
}

kotlin {
// region KMP Targets
android { publishLibraryVariants("release") }
jvm()
ios()
iosSimulatorArm64()
// endregion

sourceSets {
commonMain {
dependencies {
api(libs.compose.runtime)
api(libs.compose.foundation)
api(libs.coroutines)
api(projects.backstack)
api(projects.circuitRuntime)
Expand All @@ -26,12 +30,10 @@ kotlin {
api(libs.compose.ui)
}
}
maybeCreate("jvmMain").apply { dependencies { api(libs.compose.foundation) } }
maybeCreate("androidMain").apply {
dependencies {
api(libs.androidx.compose.runtime)
api(libs.androidx.compose.animation)
api(libs.androidx.compose.foundation)
implementation(libs.androidx.compose.integration.activity)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ androidx.savedstate:savedstate
androidx.startup:startup-runtime
androidx.tracing:tracing
androidx.versionedparcelable:versionedparcelable
com.benasher44:uuid-jvm
com.benasher44:uuid
com.google.guava:listenablefuture
org.jetbrains.compose.foundation:foundation
org.jetbrains.compose.runtime:runtime-saveable
org.jetbrains.compose.runtime:runtime
org.jetbrains.compose.ui:ui
Expand Down
2 changes: 2 additions & 0 deletions circuit-foundation/dependencies/jvmRuntimeClasspath.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
com.benasher44:uuid-jvm
com.benasher44:uuid
org.jetbrains.compose.animation:animation-core-desktop
org.jetbrains.compose.animation:animation-core
org.jetbrains.compose.animation:animation-desktop
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public class CircuitConfig private constructor(builder: Builder) {
private val UnavailableContent: @Composable (screen: Screen, modifier: Modifier) -> Unit =
{ screen, modifier ->
BasicText(
"Route not available: ${screen.javaClass.name}",
"Route not available: ${screen::class.qualifiedName}",
modifier.background(Color.Red),
style = TextStyle(color = Color.Yellow)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ internal class NavigatorImpl(

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
if (other == null || this::class != other::class) return false

other as NavigatorImpl

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import com.slack.circuit.runtime.Screen
public fun SaveableBackStack.push(screen: Screen) {
push(
SaveableBackStack.Record(
route = screen.javaClass.simpleName,
route = checkNotNull(screen::class.simpleName),
args = mapOf("screen" to screen),
key = screen.hashCode().toString()
)
Expand Down
11 changes: 9 additions & 2 deletions circuit-overlay/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@
plugins {
id("com.android.library")
kotlin("multiplatform")
alias(libs.plugins.compose)
id("com.vanniktech.maven.publish")
}

kotlin {
// region KMP Targets
android { publishLibraryVariants("release") }
jvm()
ios()
iosSimulatorArm64()
// endregion

sourceSets {
commonMain { dependencies { api(libs.compose.runtime) } }
maybeCreate("androidMain").apply { dependencies { api(libs.androidx.compose.foundation) } }
commonMain {
dependencies {
api(libs.compose.runtime)
api(libs.compose.foundation)
}
}
maybeCreate("commonTest").apply {
dependencies {
implementation(libs.kotlin.test)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ androidx.startup:startup-runtime
androidx.tracing:tracing
androidx.versionedparcelable:versionedparcelable
com.google.guava:listenablefuture
org.jetbrains.compose.foundation:foundation
org.jetbrains.compose.runtime:runtime
org.jetbrains.kotlin:kotlin-bom
org.jetbrains.kotlin:kotlin-stdlib-jdk7
Expand Down
24 changes: 24 additions & 0 deletions circuit-overlay/dependencies/jvmRuntimeClasspath.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
org.jetbrains.compose.animation:animation-core-desktop
org.jetbrains.compose.animation:animation-core
org.jetbrains.compose.animation:animation-desktop
org.jetbrains.compose.animation:animation
org.jetbrains.compose.foundation:foundation-desktop
org.jetbrains.compose.foundation:foundation-layout-desktop
org.jetbrains.compose.foundation:foundation-layout
org.jetbrains.compose.foundation:foundation
org.jetbrains.compose.runtime:runtime-desktop
org.jetbrains.compose.runtime:runtime-saveable-desktop
org.jetbrains.compose.runtime:runtime-saveable
org.jetbrains.compose.runtime:runtime
org.jetbrains.compose.ui:ui-desktop
org.jetbrains.compose.ui:ui-geometry-desktop
org.jetbrains.compose.ui:ui-geometry
org.jetbrains.compose.ui:ui-graphics-desktop
org.jetbrains.compose.ui:ui-graphics
org.jetbrains.compose.ui:ui-text-desktop
org.jetbrains.compose.ui:ui-text
org.jetbrains.compose.ui:ui-unit-desktop
org.jetbrains.compose.ui:ui-unit
org.jetbrains.compose.ui:ui-util-desktop
org.jetbrains.compose.ui:ui-util
org.jetbrains.compose.ui:ui
org.jetbrains.kotlin:kotlin-bom
org.jetbrains.kotlin:kotlin-stdlib-jdk7
org.jetbrains.kotlin:kotlin-stdlib-jdk8
Expand All @@ -9,4 +31,6 @@ org.jetbrains.kotlinx:atomicfu
org.jetbrains.kotlinx:kotlinx-coroutines-bom
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm
org.jetbrains.kotlinx:kotlinx-coroutines-core
org.jetbrains.skiko:skiko-awt
org.jetbrains.skiko:skiko
org.jetbrains:annotations
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ private class OverlayHostDataImpl<T : Any>(

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
if (other == null || this::class != other::class) return false

other as OverlayHostDataImpl<*>

Expand Down
Loading

0 comments on commit 0fafc40

Please sign in to comment.