diff --git a/README.md b/README.md
index 8ac73825..13361f4c 100644
--- a/README.md
+++ b/README.md
@@ -121,15 +121,18 @@ As part of your evaluation, you'll need to decide if you're going to integrate K
### Android
-The Android side is somewhat more straightforward. Kotlin is the preferred language for Android, and the library can be integrated as just another module library. We'll be updating soon with a general Android integration doc. In the meantime, the simplest method would be to copy the shared module into your standard Android build, and use the `app` module as a reference for dependency resolution.
+The Android side is somewhat more straightforward. Kotlin is the preferred language for Android, and the library can be integrated as just another module library.
+The main benefit of the :shared module is that it holds all the functionality needed to download, persist and display application's UI.
+This is accomplished by it using
+- Ktor - https://ktor.io/ as a Kotlin only library for handling downloads
+- SQLDelight - https://cashapp.github.io/sqldelight/ as a Kotlin only library for persisting data
+- Compose Multiplatform https://blog.jetbrains.com/kotlin/2023/05/compose-multiplatform-for-ios-is-in-alpha/ which is powered by Skia to allow displaying the same UI written in Jetpack Compose on multiple platforms.
### iOS
-The iOS integration process is relatively new and has been iterating fast. Be prepared to spend more time with config related issues when integrating with a production build.
+The iOS side is very similar to the Android one by leveraging the same technologies through :shared showcasing the major advantage of a Compose Multiplatform project - run once and run everywhere.
-You can integrate with Cocoapods, or by directly including the Xcode framework. If you are an Android developer without extensive iOS build experience, be aware that this is a risky option. Production build systems, for any ecosystem, tend to be complex. You'll almost certainly need to recruit somebody with experience maintaining your iOS build.
-
-See [IOS_PROJ_INTEGRATION.md](docs/IOS_PROJ_INTEGRATION.md) for iOS integration information.
+See [IOS_PROJ_INTEGRATION.md](docs/IOS_PROJ_INTEGRATION.md) for how iOS is consuming :shared through a Podfile.
If you are attempting to integrate your KMP project with a production iOS application, please let us know what issues you run into and reach out with questions if stuck. This is an ongoing area of improvement for the KMP platform and we'd like to help make this as smooth as possible.
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index ef663cea..27cf45c3 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,5 +1,6 @@
plugins {
id("com.android.application")
+ id("org.jetbrains.compose")
kotlin("android")
}
@@ -39,6 +40,13 @@ android {
}
}
+// Using Compose gradle plugin v 1.4.1 which supports at most Kotlin 1.8.1
+// And SQLDelight 2.0+ which supports at least Kotlin 1.8.2
+// Quick workaround until we get some easier to match versions.
+compose {
+ kotlinCompilerPlugin.set("org.jetbrains.compose.compiler:compiler:1.4.8")
+}
+
dependencies {
implementation(project(":shared"))
implementation(libs.bundles.app.ui)
diff --git a/app/src/main/kotlin/co/touchlab/kampkit/android/MainActivity.kt b/app/src/main/kotlin/co/touchlab/kampkit/android/MainActivity.kt
index 08ae0954..91a9d0fd 100644
--- a/app/src/main/kotlin/co/touchlab/kampkit/android/MainActivity.kt
+++ b/app/src/main/kotlin/co/touchlab/kampkit/android/MainActivity.kt
@@ -3,8 +3,9 @@ package co.touchlab.kampkit.android
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
-import co.touchlab.kampkit.android.ui.MainScreen
-import co.touchlab.kampkit.android.ui.theme.KaMPKitTheme
+import androidx.compose.foundation.isSystemInDarkTheme
+import co.touchlab.kampkit.ui.MainScreen
+import co.touchlab.kampkit.ui.theme.KaMPKitTheme
import co.touchlab.kampkit.injectLogger
import co.touchlab.kampkit.models.BreedViewModel
import co.touchlab.kermit.Logger
@@ -19,7 +20,7 @@ class MainActivity : ComponentActivity(), KoinComponent {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
- KaMPKitTheme {
+ KaMPKitTheme(isSystemInDarkTheme(), true) {
MainScreen(viewModel, log)
}
}
diff --git a/app/src/main/kotlin/co/touchlab/kampkit/android/ui/theme/Theme.kt b/app/src/main/kotlin/co/touchlab/kampkit/android/ui/theme/Theme.kt
deleted file mode 100644
index d17201c4..00000000
--- a/app/src/main/kotlin/co/touchlab/kampkit/android/ui/theme/Theme.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package co.touchlab.kampkit.android.ui.theme
-
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.darkColors
-import androidx.compose.material.lightColors
-import androidx.compose.runtime.Composable
-
-private val DarkColorPalette = darkColors(
- primary = Purple200,
- primaryVariant = Purple700,
- secondary = Teal200
-)
-
-private val LightColorPalette = lightColors(
- primary = Purple500,
- primaryVariant = Purple700,
- secondary = Teal200
-
- // Other default colors to override
- //
- // background = Color.White,
- // surface = Color.White,
- // onPrimary = Color.White,
- // onSecondary = Color.Black,
- // onBackground = Color.Black,
- // onSurface = Color.Black,
-)
-
-@Composable
-fun KaMPKitTheme(
- darkTheme: Boolean = isSystemInDarkTheme(),
- content: @Composable () -> Unit
-) {
- val colors = if (darkTheme) {
- DarkColorPalette
- } else {
- LightColorPalette
- }
-
- MaterialTheme(
- colors = colors,
- typography = Typography,
- shapes = Shapes,
- content = content
- )
-}
diff --git a/app/src/main/res/drawable/ic_favorite_24px.xml b/app/src/main/res/drawable/ic_favorite_24px.xml
deleted file mode 100644
index ce351f43..00000000
--- a/app/src/main/res/drawable/ic_favorite_24px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_favorite_border_24px.xml b/app/src/main/res/drawable/ic_favorite_border_24px.xml
deleted file mode 100644
index e6646709..00000000
--- a/app/src/main/res/drawable/ic_favorite_border_24px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/build.gradle.kts b/build.gradle.kts
index 47e20f28..b71bca0a 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -4,11 +4,13 @@
plugins {
alias(libs.plugins.gradleVersions)
alias(libs.plugins.ktlint) apply false
+ alias(libs.plugins.moko.resources) apply false
kotlin("multiplatform") version libs.versions.kotlin.get() apply false
kotlin("plugin.serialization") version libs.versions.kotlin.get() apply false
id("app.cash.sqldelight") version libs.versions.sqlDelight.get() apply false
id("com.android.library") version libs.versions.android.gradle.plugin.get() apply false
+ id("org.jetbrains.compose") version libs.versions.compose.gradle.plugin.get() apply(false)
}
allprojects {
@@ -17,6 +19,7 @@ allprojects {
mavenCentral()
maven("https://androidx.dev/storage/compose-compiler/repository/")
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev/")
+ gradlePluginPortal() // for moko
}
}
@@ -24,6 +27,7 @@ subprojects {
// TODO libs doesn't resolve if we do this
// apply(plugin = libs.plugins.ktlint.get().pluginId)
apply(plugin = "org.jlleitschuh.gradle.ktlint")
+ apply(plugin = "dev.icerock.mobile.multiplatform-resources")
configure {
enableExperimentalRules.set(true)
diff --git a/gradle.properties b/gradle.properties
index cb13eb29..ac3a2858 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -17,3 +17,7 @@ kotlin.code.style=official
xcodeproj=./ios
# New Android source-set layout
kotlin.mpp.androidSourceSetLayoutVersion=2
+
+# Compose Multiplatform
+org.jetbrains.compose.experimental.uikit.enabled=true
+kotlin.native.cacheKind=none
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index affd5f00..ba5dac19 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-kotlin = "1.8.21"
+kotlin = "1.8.22"
## SDK Versions
minSdk = "21"
@@ -8,11 +8,12 @@ compileSdk = "33"
# Dependencies
android-gradle-plugin = "7.4.2"
+compose-gradle-plugin = "1.4.1"
ktlint-gradle = "11.4.2"
gradle-versions = "0.47.0"
compose = "1.4.3"
-composeCompiler = "1.4.7"
+composeCompiler = "1.4.8"
android-desugaring = "2.0.3"
androidx-core = "1.10.1"
@@ -22,9 +23,9 @@ androidx-lifecycle = "2.6.1"
junit = "4.13.2"
-coroutines = "1.7.0"
+coroutines = "1.7.2"
kotlinx-datetime = "0.4.0"
-ktor = "2.3.1"
+ktor = "2.3.2"
robolectric = "4.10.3"
@@ -35,6 +36,9 @@ multiplatformSettings = "1.0.0"
turbine = "1.0.0"
sqlDelight = "2.0.0-rc01"
+moko-resources = "0.23.0"
+moko-graphics = "0.9.0"
+
[libraries]
android-desugaring = { module = "com.android.tools:desugar_jdk_libs", version.ref = "android-desugaring" }
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
@@ -86,9 +90,14 @@ touchlab-kermit-simple = { module = "co.touchlab:kermit-simple", version.ref = "
turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
+moko-resources = { module = "dev.icerock.moko:resources", version.ref = "moko-resources" }
+moko-resources-compose = { module = "dev.icerock.moko:resources-compose", version.ref = "moko-resources" }
+moko-graphics = { module = "dev.icerock.moko:graphics", version.ref = "moko-graphics" }
+
[plugins]
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint-gradle" }
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradle-versions" }
+moko-resources = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko-resources" }
[bundles]
app-ui = [
diff --git a/ios/KaMPKitiOS.xcodeproj/project.pbxproj b/ios/KaMPKitiOS.xcodeproj/project.pbxproj
index 765a0360..84b08187 100644
--- a/ios/KaMPKitiOS.xcodeproj/project.pbxproj
+++ b/ios/KaMPKitiOS.xcodeproj/project.pbxproj
@@ -7,12 +7,11 @@
objects = {
/* Begin PBXBuildFile section */
+ 0AE285002A6AD6B7000B6B60 /* ComposeContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE284FF2A6AD6B7000B6B60 /* ComposeContentView.swift */; };
+ 0AE285022A6AD7BD000B6B60 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE285012A6AD7BD000B6B60 /* ContentView.swift */; };
+ 0AE285042A6AD7D0000B6B60 /* IOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE285032A6AD7D0000B6B60 /* IOSApp.swift */; };
3DFF917C64A18A83DA010EE1 /* Pods_KaMPKitiOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B859F3FB23133D22AB9DD835 /* Pods_KaMPKitiOS.framework */; };
- 461C74AA2788F5F3004B1FFC /* CombineAdapters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 461C74A92788F5F3004B1FFC /* CombineAdapters.swift */; };
- 46A5B5EF26AF54F7002EFEAA /* BreedListScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46A5B5EE26AF54F7002EFEAA /* BreedListScreen.swift */; };
- 46A5B60826B04921002EFEAA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 46A5B60626B04920002EFEAA /* Main.storyboard */; };
46B5284D249C5CF400A7725D /* Koin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46B5284C249C5CF400A7725D /* Koin.swift */; };
- F1465F0123AA94BF0055F7C3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1465F0023AA94BF0055F7C3 /* AppDelegate.swift */; };
F1465F0A23AA94BF0055F7C3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F1465F0923AA94BF0055F7C3 /* Assets.xcassets */; };
F1465F0D23AA94BF0055F7C3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F1465F0B23AA94BF0055F7C3 /* LaunchScreen.storyboard */; };
F1465F1823AA94C00055F7C3 /* KaMPKitiOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1465F1723AA94C00055F7C3 /* KaMPKitiOSTests.swift */; };
@@ -37,16 +36,15 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
+ 0AE284FF2A6AD6B7000B6B60 /* ComposeContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeContentView.swift; sourceTree = ""; };
+ 0AE285012A6AD7BD000B6B60 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
+ 0AE285032A6AD7D0000B6B60 /* IOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOSApp.swift; sourceTree = ""; };
1DFCC00C8DAA719770A18D1A /* Pods-KaMPKitiOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KaMPKitiOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-KaMPKitiOS/Pods-KaMPKitiOS.release.xcconfig"; sourceTree = ""; };
2A1ED6A4A2A53F5F75C58E5F /* Pods-KaMPKitiOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KaMPKitiOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-KaMPKitiOS/Pods-KaMPKitiOS.release.xcconfig"; sourceTree = ""; };
- 461C74A92788F5F3004B1FFC /* CombineAdapters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombineAdapters.swift; sourceTree = ""; };
- 46A5B5EE26AF54F7002EFEAA /* BreedListScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreedListScreen.swift; sourceTree = ""; };
- 46A5B60726B04920002EFEAA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
46B5284C249C5CF400A7725D /* Koin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Koin.swift; sourceTree = ""; };
B859F3FB23133D22AB9DD835 /* Pods_KaMPKitiOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_KaMPKitiOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
ED1F782AF3705197012D0C33 /* Pods-KaMPKitiOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KaMPKitiOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-KaMPKitiOS/Pods-KaMPKitiOS.debug.xcconfig"; sourceTree = ""; };
F1465EFD23AA94BF0055F7C3 /* KaMPKitiOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KaMPKitiOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
- F1465F0023AA94BF0055F7C3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
F1465F0923AA94BF0055F7C3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
F1465F0C23AA94BF0055F7C3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
F1465F0E23AA94BF0055F7C3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
@@ -129,14 +127,13 @@
F1465EFF23AA94BF0055F7C3 /* KaMPKitiOS */ = {
isa = PBXGroup;
children = (
- F1465F0023AA94BF0055F7C3 /* AppDelegate.swift */,
- 46A5B60626B04920002EFEAA /* Main.storyboard */,
46B5284C249C5CF400A7725D /* Koin.swift */,
F1465F0923AA94BF0055F7C3 /* Assets.xcassets */,
F1465F0B23AA94BF0055F7C3 /* LaunchScreen.storyboard */,
F1465F0E23AA94BF0055F7C3 /* Info.plist */,
- 46A5B5EE26AF54F7002EFEAA /* BreedListScreen.swift */,
- 461C74A92788F5F3004B1FFC /* CombineAdapters.swift */,
+ 0AE284FF2A6AD6B7000B6B60 /* ComposeContentView.swift */,
+ 0AE285012A6AD7BD000B6B60 /* ContentView.swift */,
+ 0AE285032A6AD7D0000B6B60 /* IOSApp.swift */,
);
path = KaMPKitiOS;
sourceTree = "";
@@ -266,7 +263,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 46A5B60826B04921002EFEAA /* Main.storyboard in Resources */,
F1465F0D23AA94BF0055F7C3 /* LaunchScreen.storyboard in Resources */,
F1465F0A23AA94BF0055F7C3 /* Assets.xcassets in Resources */,
);
@@ -354,9 +350,9 @@
buildActionMask = 2147483647;
files = (
46B5284D249C5CF400A7725D /* Koin.swift in Sources */,
- 461C74AA2788F5F3004B1FFC /* CombineAdapters.swift in Sources */,
- 46A5B5EF26AF54F7002EFEAA /* BreedListScreen.swift in Sources */,
- F1465F0123AA94BF0055F7C3 /* AppDelegate.swift in Sources */,
+ 0AE285002A6AD6B7000B6B60 /* ComposeContentView.swift in Sources */,
+ 0AE285022A6AD7BD000B6B60 /* ContentView.swift in Sources */,
+ 0AE285042A6AD7D0000B6B60 /* IOSApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -392,14 +388,6 @@
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
- 46A5B60626B04920002EFEAA /* Main.storyboard */ = {
- isa = PBXVariantGroup;
- children = (
- 46A5B60726B04920002EFEAA /* Base */,
- );
- name = Main.storyboard;
- sourceTree = "";
- };
F1465F0B23AA94BF0055F7C3 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
@@ -464,7 +452,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.2;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@@ -522,7 +510,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.2;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@@ -596,7 +584,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = KaMPKitiOSTests/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 13.2;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -617,7 +605,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = KaMPKitiOSTests/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 13.2;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
diff --git a/ios/KaMPKitiOS/AppDelegate.swift b/ios/KaMPKitiOS/AppDelegate.swift
deleted file mode 100644
index 25924195..00000000
--- a/ios/KaMPKitiOS/AppDelegate.swift
+++ /dev/null
@@ -1,34 +0,0 @@
-//
-// AppDelegate.swift
-// KaMPKitiOS
-//
-// Created by Kevin Schildhorn on 12/18/19.
-// Copyright © 2019 Touchlab. All rights reserved.
-//
-
-import SwiftUI
-import shared
-
-@UIApplicationMain
-class AppDelegate: UIResponder, UIApplicationDelegate {
-
- var window: UIWindow?
-
- // Lazy so it doesn't try to initialize before startKoin() is called
- lazy var log = koin.loggerWithTag(tag: "AppDelegate")
-
- func application(_ application: UIApplication, didFinishLaunchingWithOptions
- launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
-
- startKoin()
-
- let viewController = UIHostingController(rootView: BreedListScreen())
-
- self.window = UIWindow(frame: UIScreen.main.bounds)
- self.window?.rootViewController = viewController
- self.window?.makeKeyAndVisible()
-
- log.v(message: {"App Started"})
- return true
- }
-}
diff --git a/ios/KaMPKitiOS/ComposeContentView.swift b/ios/KaMPKitiOS/ComposeContentView.swift
new file mode 100644
index 00000000..733655b7
--- /dev/null
+++ b/ios/KaMPKitiOS/ComposeContentView.swift
@@ -0,0 +1,24 @@
+//
+// ComposeContentView.swift
+// KaMPKitiOS
+//
+// Created by Petru on 7/21/23.
+// Copyright © 2023 Touchlab. All rights reserved.
+//
+
+import Foundation
+import shared
+import SwiftUI
+
+struct ComposeContentView: UIViewControllerRepresentable {
+ func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
+ // no-op
+ }
+
+ func makeUIViewController(context: Context) -> some UIViewController {
+ MainViewControllerKt.MainViewController(
+ logger: koin.loggerWithTag(tag: "ViewController"),
+ viewModel: KotlinDependencies.shared.getBreedViewModel().viewModel
+ )
+ }
+}
diff --git a/ios/KaMPKitiOS/ContentView.swift b/ios/KaMPKitiOS/ContentView.swift
new file mode 100644
index 00000000..05074fbe
--- /dev/null
+++ b/ios/KaMPKitiOS/ContentView.swift
@@ -0,0 +1,16 @@
+//
+// ContentView.swift
+// KaMPKitiOS
+//
+// Created by Petru on 7/21/23.
+// Copyright © 2023 Touchlab. All rights reserved.
+//
+
+import SwiftUI
+import shared
+
+struct ContentView: View {
+ var body: some View {
+ ComposeContentView()
+ }
+}
diff --git a/ios/KaMPKitiOS/IOSApp.swift b/ios/KaMPKitiOS/IOSApp.swift
new file mode 100644
index 00000000..8fd64b39
--- /dev/null
+++ b/ios/KaMPKitiOS/IOSApp.swift
@@ -0,0 +1,23 @@
+//
+// iOSApp.swift
+// KaMPKitiOS
+//
+// Created by Petru on 7/21/23.
+// Copyright © 2023 Touchlab. All rights reserved.
+//
+
+import SwiftUI
+
+@main
+struct IOSApp: App {
+
+ init() {
+ startKoin()
+ }
+
+ var body: some Scene {
+ WindowGroup {
+ ContentView()
+ }
+ }
+}
diff --git a/ios/KaMPKitiOS/Info.plist b/ios/KaMPKitiOS/Info.plist
index af8868b2..b79441c7 100644
--- a/ios/KaMPKitiOS/Info.plist
+++ b/ios/KaMPKitiOS/Info.plist
@@ -21,13 +21,13 @@
LSRequiresIPhoneOS
UILaunchStoryboardName
- LaunchScreen
- UIMainStoryboardFile
- Main
+ LaunchScreen.storyboard
UIRequiredDeviceCapabilities
armv7
+ UIStatusBarStyle
+
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 0d481a6a..9863cf83 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -15,9 +15,9 @@ EXTERNAL SOURCES:
:path: "../shared/"
SPEC CHECKSUMS:
- shared: 153082a09d0db819a966647739a6809fed9eff56
+ shared: 2f802391954274f6b79e59e6941e2cc7b18b56b1
SwiftLint: c585ebd615d9520d7fbdbe151f527977b0534f1e
PODFILE CHECKSUM: d5a73f50a47bad1893e4fbf8978f1bef946ebdf6
-COCOAPODS: 1.11.3
+COCOAPODS: 1.12.1
diff --git a/ios/Pods/Local Podspecs/shared.podspec.json b/ios/Pods/Local Podspecs/shared.podspec.json
index 9f2f93a2..3cb2c35d 100644
--- a/ios/Pods/Local Podspecs/shared.podspec.json
+++ b/ios/Pods/Local Podspecs/shared.podspec.json
@@ -11,7 +11,7 @@
"vendored_frameworks": "build/cocoapods/framework/shared.framework",
"libraries": "c++",
"platforms": {
- "ios": "12.4"
+ "ios": "14.0"
},
"pod_target_xcconfig": {
"KOTLIN_PROJECT_PATH": ":shared",
diff --git a/ios/Pods/Manifest.lock b/ios/Pods/Manifest.lock
index 0d481a6a..9863cf83 100644
--- a/ios/Pods/Manifest.lock
+++ b/ios/Pods/Manifest.lock
@@ -15,9 +15,9 @@ EXTERNAL SOURCES:
:path: "../shared/"
SPEC CHECKSUMS:
- shared: 153082a09d0db819a966647739a6809fed9eff56
+ shared: 2f802391954274f6b79e59e6941e2cc7b18b56b1
SwiftLint: c585ebd615d9520d7fbdbe151f527977b0534f1e
PODFILE CHECKSUM: d5a73f50a47bad1893e4fbf8978f1bef946ebdf6
-COCOAPODS: 1.11.3
+COCOAPODS: 1.12.1
diff --git a/ios/Pods/Pods.xcodeproj/project.pbxproj b/ios/Pods/Pods.xcodeproj/project.pbxproj
index 96c12b0b..04900b6c 100644
--- a/ios/Pods/Pods.xcodeproj/project.pbxproj
+++ b/ios/Pods/Pods.xcodeproj/project.pbxproj
@@ -257,8 +257,8 @@
46EB2E00000000 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastSwiftUpdateCheck = 1240;
- LastUpgradeCheck = 1240;
+ LastSwiftUpdateCheck = 1300;
+ LastUpgradeCheck = 1300;
};
buildConfigurationList = 46EB2E00000030 /* Build configuration list for PBXProject "Pods" */;
compatibilityVersion = "Xcode 10.0";
@@ -383,7 +383,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.4;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@@ -446,7 +446,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.4;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -464,7 +464,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -481,7 +481,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -498,7 +498,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_OBJC_WEAK = NO;
- IPHONEOS_DEPLOYMENT_TARGET = 12.4;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -516,7 +516,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_OBJC_WEAK = NO;
- IPHONEOS_DEPLOYMENT_TARGET = 12.4;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -542,7 +542,7 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = "Target Support Files/Pods-KaMPKitiOS/Pods-KaMPKitiOS-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 12.4;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -580,7 +580,7 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = "Target Support Files/Pods-KaMPKitiOS/Pods-KaMPKitiOS-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 12.4;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
diff --git a/ios/Pods/Target Support Files/Pods-KaMPKitiOS/Pods-KaMPKitiOS-Info.plist b/ios/Pods/Target Support Files/Pods-KaMPKitiOS/Pods-KaMPKitiOS-Info.plist
index 2243fe6e..19cf209d 100644
--- a/ios/Pods/Target Support Files/Pods-KaMPKitiOS/Pods-KaMPKitiOS-Info.plist
+++ b/ios/Pods/Target Support Files/Pods-KaMPKitiOS/Pods-KaMPKitiOS-Info.plist
@@ -3,7 +3,7 @@
CFBundleDevelopmentRegion
- en
+ ${PODS_DEVELOPMENT_LANGUAGE}
CFBundleExecutable
${EXECUTABLE_NAME}
CFBundleIdentifier
diff --git a/ios/Pods/Target Support Files/Pods-KaMPKitiOS/Pods-KaMPKitiOS-frameworks.sh b/ios/Pods/Target Support Files/Pods-KaMPKitiOS/Pods-KaMPKitiOS-frameworks.sh
index 333d767c..1e57ae2b 100755
--- a/ios/Pods/Target Support Files/Pods-KaMPKitiOS/Pods-KaMPKitiOS-frameworks.sh
+++ b/ios/Pods/Target Support Files/Pods-KaMPKitiOS/Pods-KaMPKitiOS-frameworks.sh
@@ -41,7 +41,7 @@ install_framework()
if [ -L "${source}" ]; then
echo "Symlinked..."
- source="$(readlink "${source}")"
+ source="$(readlink -f "${source}")"
fi
if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then
diff --git a/ios/Pods/Target Support Files/SwiftLint/SwiftLint.debug.xcconfig b/ios/Pods/Target Support Files/SwiftLint/SwiftLint.debug.xcconfig
index 003a1f43..5238df58 100644
--- a/ios/Pods/Target Support Files/SwiftLint/SwiftLint.debug.xcconfig
+++ b/ios/Pods/Target Support Files/SwiftLint/SwiftLint.debug.xcconfig
@@ -3,6 +3,7 @@ CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftLint
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftLint
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
diff --git a/ios/Pods/Target Support Files/SwiftLint/SwiftLint.release.xcconfig b/ios/Pods/Target Support Files/SwiftLint/SwiftLint.release.xcconfig
index 003a1f43..5238df58 100644
--- a/ios/Pods/Target Support Files/SwiftLint/SwiftLint.release.xcconfig
+++ b/ios/Pods/Target Support Files/SwiftLint/SwiftLint.release.xcconfig
@@ -3,6 +3,7 @@ CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftLint
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftLint
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
diff --git a/ios/Pods/Target Support Files/shared/shared.debug.xcconfig b/ios/Pods/Target Support Files/shared/shared.debug.xcconfig
index 49484481..ee6cdedd 100644
--- a/ios/Pods/Target Support Files/shared/shared.debug.xcconfig
+++ b/ios/Pods/Target Support Files/shared/shared.debug.xcconfig
@@ -6,6 +6,7 @@ KOTLIN_PROJECT_PATH = :shared
OTHER_LDFLAGS = $(inherited) -l"c++"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/../../shared
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
diff --git a/ios/Pods/Target Support Files/shared/shared.release.xcconfig b/ios/Pods/Target Support Files/shared/shared.release.xcconfig
index 49484481..ee6cdedd 100644
--- a/ios/Pods/Target Support Files/shared/shared.release.xcconfig
+++ b/ios/Pods/Target Support Files/shared/shared.release.xcconfig
@@ -6,6 +6,7 @@ KOTLIN_PROJECT_PATH = :shared
OTHER_LDFLAGS = $(inherited) -l"c++"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/../../shared
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts
index 75b64d60..cad6e133 100644
--- a/shared/build.gradle.kts
+++ b/shared/build.gradle.kts
@@ -5,6 +5,7 @@ plugins {
kotlin("native.cocoapods")
kotlin("plugin.serialization")
id("com.android.library")
+ id("org.jetbrains.compose")
id("app.cash.sqldelight")
}
@@ -46,6 +47,11 @@ kotlin {
val commonMain by getting {
dependencies {
+ implementation(compose.runtime)
+ implementation(compose.foundation)
+ implementation(compose.material) // for PullRefreshIndicator
+ implementation(compose.material3)
+ implementation(compose.materialIconsExtended)
implementation(libs.koin.core)
implementation(libs.coroutines.core)
implementation(libs.sqlDelight.coroutinesExt)
@@ -53,6 +59,8 @@ kotlin {
implementation(libs.multiplatformSettings.common)
implementation(libs.kotlinx.dateTime)
api(libs.touchlab.kermit)
+ api(libs.moko.resources)
+ api(libs.moko.resources.compose)
}
}
val commonTest by getting {
@@ -61,7 +69,10 @@ kotlin {
}
}
val androidMain by getting {
+ // Below line adds a temporary workaround for https://github.com/icerockdev/moko-resources/issues/531
+ kotlin.srcDirs("build/generated/moko/androidMain/src")
dependencies {
+ implementation(libs.compose.activity)
implementation(libs.androidx.lifecycle.viewmodel)
implementation(libs.sqlDelight.android)
implementation(libs.ktor.client.okHttp)
@@ -73,6 +84,7 @@ kotlin {
}
}
val iosMain by getting {
+ resources.srcDirs("build/generated/moko/iosMain/src")
dependencies {
implementation(libs.sqlDelight.native)
implementation(libs.ktor.client.ios)
@@ -81,6 +93,7 @@ kotlin {
}
val iosTest by getting
val iosSimulatorArm64Main by getting {
+ resources.srcDirs("build/generated/moko/iosSimulatorArm64Main/src")
dependsOn(iosMain)
}
val iosSimulatorArm64Test by getting {
@@ -97,17 +110,33 @@ kotlin {
summary = "Common library for the KaMP starter kit"
homepage = "https://github.com/touchlab/KaMPKit"
framework {
- isStatic = false // SwiftUI preview requires dynamic framework
+ // Below line is needed for Compose Multiplatform, see https://github.com/JetBrains/compose-multiplatform/issues/3178
+ isStatic = true
linkerOpts("-lsqlite3")
export(libs.touchlab.kermit.simple)
+ export(libs.moko.resources)
+ export(libs.moko.graphics)
}
- ios.deploymentTarget = "12.4"
+ ios.deploymentTarget = "14.0"
podfile = project.file("../ios/Podfile")
}
}
+// Using Compose gradle plugin v 1.4.1 which supports at most Kotlin 1.8.1
+// And SQLDelight 2.0+ which supports at least Kotlin 1.8.2
+// Quick workaround until we get some easier to match versions.
+compose {
+ kotlinCompilerPlugin.set("org.jetbrains.compose.compiler:compiler:1.4.8")
+}
+
sqldelight {
databases.create("KaMPKitDb") {
packageName.set("co.touchlab.kampkit.db")
}
}
+
+multiplatformResources {
+ multiplatformResourcesPackage = "co.touchlab.kampkit" // required
+ // multiplatformResourcesSourceSet = "iosSimulatorArm64Main"
+ multiplatformResourcesClassName = "MR" // optional, default MR
+}
diff --git a/shared/shared.podspec b/shared/shared.podspec
index 7c41fceb..853464bb 100644
--- a/shared/shared.podspec
+++ b/shared/shared.podspec
@@ -8,7 +8,7 @@ Pod::Spec.new do |spec|
spec.summary = 'Common library for the KaMP starter kit'
spec.vendored_frameworks = 'build/cocoapods/framework/shared.framework'
spec.libraries = 'c++'
- spec.ios.deployment_target = '12.4'
+ spec.ios.deployment_target = '14.0'
spec.pod_target_xcconfig = {
diff --git a/shared/src/androidMain/kotlin/co/touchlab/kampkit/ui/theme/KaMPKitTheme.kt b/shared/src/androidMain/kotlin/co/touchlab/kampkit/ui/theme/KaMPKitTheme.kt
new file mode 100644
index 00000000..9fab32ae
--- /dev/null
+++ b/shared/src/androidMain/kotlin/co/touchlab/kampkit/ui/theme/KaMPKitTheme.kt
@@ -0,0 +1,52 @@
+package co.touchlab.kampkit.ui.theme
+
+import android.app.Activity
+import android.os.Build
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
+import androidx.core.view.WindowCompat
+
+@Composable
+actual fun KaMPKitTheme(
+ darkTheme: Boolean,
+ dynamicColor: Boolean,
+ content: @Composable () -> Unit
+) {
+ val colorScheme = when {
+ // Dynamic color is only supported on Android 12+
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+ darkTheme -> DarkColorPalette
+ else -> LightColorPalette
+ }
+
+ // If not in Android Studio's preview then update also the system bars
+ val view = LocalView.current
+ if (!view.isInEditMode) {
+ SideEffect {
+ (view.context as Activity).window.apply {
+ statusBarColor = colorScheme.primary.toArgb()
+ WindowCompat
+ .getInsetsController(this, view).apply {
+ isAppearanceLightStatusBars = darkTheme
+ isAppearanceLightNavigationBars = darkTheme
+ }
+ }
+ }
+ }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = Typography,
+ shapes = Shapes,
+ content = content
+ )
+}
diff --git a/shared/src/androidMain/kotlin/co/touchlab/kampkit/utils/Strings.kt b/shared/src/androidMain/kotlin/co/touchlab/kampkit/utils/Strings.kt
new file mode 100644
index 00000000..8d69d97d
--- /dev/null
+++ b/shared/src/androidMain/kotlin/co/touchlab/kampkit/utils/Strings.kt
@@ -0,0 +1,24 @@
+package co.touchlab.kampkit.utils
+
+import android.content.Context
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+import dev.icerock.moko.resources.StringResource
+import dev.icerock.moko.resources.desc.Resource
+import dev.icerock.moko.resources.desc.StringDesc
+import dev.icerock.moko.resources.format
+
+actual class Strings(private val context: Context) {
+ actual fun get(id: StringResource, args: List): String {
+ return when (args.isEmpty()) {
+ true -> StringDesc.Resource(id).toString(context)
+ false -> id.format(*args.toTypedArray()).toString(context)
+ }
+ }
+}
+
+/**
+ * Get a string existing in the shared module.
+ */
+@Composable
+fun getString(id: StringResource, vararg args: Any) = Strings(LocalContext.current).get(id, args.toList())
diff --git a/shared/src/commonMain/kotlin/co/touchlab/kampkit/Koin.kt b/shared/src/commonMain/kotlin/co/touchlab/kampkit/Koin.kt
index db7c2df1..0c002b27 100644
--- a/shared/src/commonMain/kotlin/co/touchlab/kampkit/Koin.kt
+++ b/shared/src/commonMain/kotlin/co/touchlab/kampkit/Koin.kt
@@ -83,4 +83,18 @@ internal inline fun Scope.getWith(vararg params: Any?): T {
// Simple function to clean up the syntax a bit
fun KoinComponent.injectLogger(tag: String): Lazy = inject { parametersOf(tag) }
+/**
+ * Get any instance of `T` if available in the dependencies graph.
+ *
+ * @param tag Optional name of allowing to get a specific
+ */
+inline fun injectInstance(tag: String? = null): T {
+ return object : KoinComponent {
+ val value: T by when (tag) {
+ null -> inject()
+ else -> inject { parametersOf(tag) }
+ }
+ }.value
+}
+
expect val platformModule: Module
diff --git a/app/src/main/kotlin/co/touchlab/kampkit/android/ui/Composables.kt b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/Composables.kt
similarity index 83%
rename from app/src/main/kotlin/co/touchlab/kampkit/android/ui/Composables.kt
rename to shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/Composables.kt
index 002c2624..7f03c87b 100644
--- a/app/src/main/kotlin/co/touchlab/kampkit/android/ui/Composables.kt
+++ b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/Composables.kt
@@ -1,4 +1,4 @@
-package co.touchlab.kampkit.android.ui
+package co.touchlab.kampkit.ui
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.FastOutSlowInEasing
@@ -20,25 +20,26 @@ import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import co.touchlab.kampkit.android.R
+import co.touchlab.kampkit.MR
import co.touchlab.kampkit.db.Breed
import co.touchlab.kampkit.models.BreedViewModel
import co.touchlab.kampkit.models.BreedViewState
import co.touchlab.kermit.Logger
+import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.launch
@Composable
@@ -46,7 +47,7 @@ fun MainScreen(
viewModel: BreedViewModel,
log: Logger
) {
- val dogsState by viewModel.breedState.collectAsStateWithLifecycle()
+ val dogsState by viewModel.breedState.collectAsState()
val scope = rememberCoroutineScope()
MainScreenContent(
@@ -106,7 +107,7 @@ fun Empty() {
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
- Text(stringResource(R.string.empty_breeds))
+ Text(text = stringResource(MR.strings.empty_breeds))
}
}
@@ -166,27 +167,14 @@ fun FavoriteIcon(breed: Breed) {
) { fav ->
if (fav) {
Image(
- painter = painterResource(id = R.drawable.ic_favorite_border_24px),
- contentDescription = stringResource(R.string.favorite_breed, breed.name)
+ imageVector = Icons.Default.FavoriteBorder,
+ contentDescription = stringResource(MR.strings.favorite_breed, breed.name)
)
} else {
Image(
- painter = painterResource(id = R.drawable.ic_favorite_24px),
- contentDescription = stringResource(R.string.unfavorite_breed, breed.name)
+ imageVector = Icons.Default.Favorite,
+ contentDescription = stringResource(MR.strings.unfavorite_breed, breed.name)
)
}
}
}
-
-@Preview
-@Composable
-fun MainScreenContentPreview_Success() {
- MainScreenContent(
- dogsState = BreedViewState(
- breeds = listOf(
- Breed(0, "appenzeller", false),
- Breed(1, "australian", true)
- )
- )
- )
-}
diff --git a/app/src/main/kotlin/co/touchlab/kampkit/android/ui/theme/Color.kt b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/Color.kt
similarity index 79%
rename from app/src/main/kotlin/co/touchlab/kampkit/android/ui/theme/Color.kt
rename to shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/Color.kt
index 287f01c9..11f754dd 100644
--- a/app/src/main/kotlin/co/touchlab/kampkit/android/ui/theme/Color.kt
+++ b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/Color.kt
@@ -1,4 +1,4 @@
-package co.touchlab.kampkit.android.ui.theme
+package co.touchlab.kampkit.ui.theme
import androidx.compose.ui.graphics.Color
diff --git a/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/KaMPKitTheme.kt b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/KaMPKitTheme.kt
new file mode 100644
index 00000000..d5b06f60
--- /dev/null
+++ b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/KaMPKitTheme.kt
@@ -0,0 +1,10 @@
+package co.touchlab.kampkit.ui.theme
+
+import androidx.compose.runtime.Composable
+
+@Composable
+expect fun KaMPKitTheme(
+ darkTheme: Boolean,
+ dynamicColor: Boolean,
+ content: @Composable () -> Unit
+)
diff --git a/app/src/main/kotlin/co/touchlab/kampkit/android/ui/theme/Shapes.kt b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/Shapes.kt
similarity index 73%
rename from app/src/main/kotlin/co/touchlab/kampkit/android/ui/theme/Shapes.kt
rename to shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/Shapes.kt
index 0260e337..f464b2b5 100644
--- a/app/src/main/kotlin/co/touchlab/kampkit/android/ui/theme/Shapes.kt
+++ b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/Shapes.kt
@@ -1,7 +1,7 @@
-package co.touchlab.kampkit.android.ui.theme
+package co.touchlab.kampkit.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Shapes
+import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp
val Shapes = Shapes(
diff --git a/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/Theme.kt b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/Theme.kt
new file mode 100644
index 00000000..9f76f7a4
--- /dev/null
+++ b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/Theme.kt
@@ -0,0 +1,25 @@
+package co.touchlab.kampkit.ui.theme
+
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.lightColorScheme
+
+val DarkColorPalette = darkColorScheme(
+ primary = Purple200,
+ inversePrimary = Purple700,
+ secondary = Teal200
+)
+
+val LightColorPalette = lightColorScheme(
+ primary = Purple500,
+ inversePrimary = Purple700,
+ secondary = Teal200
+
+ // Other default colors to override
+ //
+ // background = Color.White,
+ // surface = Color.White,
+ // onPrimary = Color.White,
+ // onSecondary = Color.Black,
+ // onBackground = Color.Black,
+ // onSurface = Color.Black,
+)
diff --git a/app/src/main/kotlin/co/touchlab/kampkit/android/ui/theme/Typography.kt b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/Typography.kt
similarity index 86%
rename from app/src/main/kotlin/co/touchlab/kampkit/android/ui/theme/Typography.kt
rename to shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/Typography.kt
index 2c6faca5..92c7665f 100644
--- a/app/src/main/kotlin/co/touchlab/kampkit/android/ui/theme/Typography.kt
+++ b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/theme/Typography.kt
@@ -1,6 +1,6 @@
-package co.touchlab.kampkit.android.ui.theme
+package co.touchlab.kampkit.ui.theme
-import androidx.compose.material.Typography
+import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
@@ -8,7 +8,7 @@ import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
- body1 = TextStyle(
+ bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
diff --git a/shared/src/commonMain/kotlin/co/touchlab/kampkit/utils/Strings.kt b/shared/src/commonMain/kotlin/co/touchlab/kampkit/utils/Strings.kt
new file mode 100644
index 00000000..ed868d2f
--- /dev/null
+++ b/shared/src/commonMain/kotlin/co/touchlab/kampkit/utils/Strings.kt
@@ -0,0 +1,7 @@
+package co.touchlab.kampkit.utils
+
+import dev.icerock.moko.resources.StringResource
+
+expect class Strings {
+ fun get(id: StringResource, args: List): String
+}
diff --git a/shared/src/commonMain/resources/MR/base/strings.xml b/shared/src/commonMain/resources/MR/base/strings.xml
new file mode 100644
index 00000000..5ac1e5a9
--- /dev/null
+++ b/shared/src/commonMain/resources/MR/base/strings.xml
@@ -0,0 +1,7 @@
+
+
+ KaMP Kit
+ Favorite %1$s
+ Unfavorite %1$s
+ Sorry, no doggos found
+
diff --git a/shared/src/iosMain/kotlin/co/touchlab/kampkit/ui.theme/KaMPKitTheme.kt b/shared/src/iosMain/kotlin/co/touchlab/kampkit/ui.theme/KaMPKitTheme.kt
new file mode 100644
index 00000000..253d2ea2
--- /dev/null
+++ b/shared/src/iosMain/kotlin/co/touchlab/kampkit/ui.theme/KaMPKitTheme.kt
@@ -0,0 +1,18 @@
+package co.touchlab.kampkit.ui.theme
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+
+@Composable
+actual fun KaMPKitTheme(
+ darkTheme: Boolean,
+ dynamicColor: Boolean,
+ content: @Composable () -> Unit
+) {
+ MaterialTheme(
+ colorScheme = if (darkTheme) DarkColorPalette else LightColorPalette,
+ typography = Typography,
+ shapes = Shapes,
+ content = content
+ )
+}
diff --git a/shared/src/iosMain/kotlin/co/touchlab/kampkit/ui/MainViewController.kt b/shared/src/iosMain/kotlin/co/touchlab/kampkit/ui/MainViewController.kt
new file mode 100644
index 00000000..186145e9
--- /dev/null
+++ b/shared/src/iosMain/kotlin/co/touchlab/kampkit/ui/MainViewController.kt
@@ -0,0 +1,20 @@
+package co.touchlab.kampkit.ui
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.ui.window.ComposeUIViewController
+import co.touchlab.kampkit.injectInstance
+import co.touchlab.kampkit.models.BreedViewModel
+import co.touchlab.kampkit.ui.theme.KaMPKitTheme
+import co.touchlab.kermit.Logger
+import platform.UIKit.UIApplication
+import platform.UIKit.UIScreen
+import platform.UIKit.UIUserInterfaceStyle
+
+fun MainViewController(logger: Logger, viewModel: BreedViewModel) = ComposeUIViewController {
+ val isDarkThemeEnabled =
+ UIScreen.mainScreen.traitCollection.userInterfaceStyle == UIUserInterfaceStyle.UIUserInterfaceStyleDark
+
+ KaMPKitTheme(isDarkThemeEnabled, false) {
+ MainScreen(viewModel, logger)
+ }
+}
diff --git a/shared/src/iosMain/kotlin/co/touchlab/kampkit/utils/Strings.kt b/shared/src/iosMain/kotlin/co/touchlab/kampkit/utils/Strings.kt
new file mode 100644
index 00000000..1502f5cd
--- /dev/null
+++ b/shared/src/iosMain/kotlin/co/touchlab/kampkit/utils/Strings.kt
@@ -0,0 +1,13 @@
+package co.touchlab.kampkit.utils
+
+import dev.icerock.moko.resources.StringResource
+import dev.icerock.moko.resources.desc.Resource
+import dev.icerock.moko.resources.desc.StringDesc
+import dev.icerock.moko.resources.format
+
+actual class Strings {
+ actual fun get(id: StringResource, args: List) = when (args.isEmpty()) {
+ true -> StringDesc.Resource(id).localized()
+ false -> id.format(*args.toTypedArray()).localized()
+ }
+}