How to setup protobuf in Android/IOS Kotlin Multiplatform project - android

First time using protobu and could not find an example of how to connect it to Kotlin Multiplatform. I created Multiplatform project Android/IOS with "shared" module where I need to use protobuf. Project structure:
Code in build.gradle shared.module lvl
build.gradle shared.module lvl
plugins {
kotlin("multiplatform")
id("com.android.library")
id("com.google.protobuf")
}
kotlin {
android()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "shared"
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val androidMain by getting
val androidTest by getting
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}
val iosX64Test by getting
val iosArm64Test by getting
val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
}
}
android {
namespace = "com.example.someproject"
compileSdk = 33
defaultConfig {
minSdk = 21
targetSdk = 33
}
protobuf {
protoc {
artifact = ("com.google.protobuf:protoc:3.21.9")
}
generateProtoTasks {
all().forEach { task ->
task.builtins {
java {
// option 'lite' //Error - Unresolved reference: option
}
kotlin {
// option 'lite' //Error - Unresolved reference: option
}
}
}
}
}
dependencies {
implementation("com.google.protobuf:protobuf-javalite:3.21.9")
}
}
My build.gradle project lvl:
build gradle project lvl
buildscript {
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.9.1'
}
}
plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
id 'com.google.protobuf' version '0.9.1' apply false
}
I tried to add path to my .proto files with sourceSets, but I got errors:
android {
namespace = "com.example.someproject"
compileSdk = 33
defaultConfig {
minSdk = 21
targetSdk = 33
}
sourceSets {
main { //Error - Unresolved reference: main
proto { //Error - Unresolved reference: proto
srcDir 'src/main/protobuf' //Error - Unresolved reference: srcDir
}
java {
srcDirs 'build/generated/source/proto/main/java' //Error - Unresolved reference: srcDirs
srcDirs 'build/generated/source/proto/main/kotlin' //Error - Unresolved reference: srcDirs
}
}
}
protobuf {
protoc {
artifact = ("com.google.protobuf:protoc:3.21.9")
}
generateProtoTasks {
all().forEach { task ->
task.builtins {
java {
// option 'lite' //Error - Unresolved reference: option
}
kotlin {
// option 'lite' //Error - Unresolved reference: option
}
}
}
}
}
dependencies {
implementation("com.google.protobuf:protobuf-javalite:3.21.9")
}
}
When I try to build project without sourceSets, I didn't get generated java/kotlin files in build/generated/source - path
And my question is: "How setup protobuf in Android/IOS project Kotlin multiplatform?"

If you want to use ProtoBuf library in shared module in common code, then you have to use library that supports Kotlin Multiplatform. You can take a look at official kotlinx.serialization library, it supports ProtoBuf. And you can use it in commonMain module.
Also take a look at community library
Or learn more about platform-specific implementations and use expect/actual mechanism
Another option will be use your ProtoBuf library and plugin in androidApp only and for iOS part use another iOS specific ProtoBuf library.

Related

Setting up Protobuf + Kotlin in Android Studio 2023

I spend hours on just setting up Protobuf with Kotlin in Android Studio. The endgoal is just that my proto files are compiled in Kotlin and that I can use them in my project.
I have an example project here: https://github.com/Jasperav/ProtobufAndroid. It mimics my setup in the real application: an external dir containing the proto files and the android project. It contains all the code mentioned below. This is a combined effort of tutorials I found on the internet. It is probably terrible wrong. I tried https://github.com/google/protobuf-gradle-plugin, but it just looks so complicated for something simple I am doing:
Have a dir with protofiles somewhere on your filesystem
Create a new Android project on Kotlin
In the Project build.gradle, add id 'com.google.protobuf' version '0.9.2' apply false as plugin
In the Module build.gradle, add ->
This to the dependencies: implementation 'com.google.protobuf:protobuf-lite:3.21.12'
The sourceSets at the bottom inside the android bracket
The protobuf section at the bottom between the dependencies and android section.
sourceSets:
sourceSets {
main {
kotlin {
srcDirs += 'build/generated/source/proto/main/kotlin'
}
proto {
srcDir '/Users/me/androidkotlin/proto'
}
}
}
protobuf:
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.21.12'
}
plugins {
kotlinlite {
artifact = 'com.google.protobuf:protoc-gen-kotlin:3.21.12'
}
}
generateProtoTasks {
ofSourceSet("main").forEach { task ->
task.builtins {
getByName("kotlin") {
option("lite")
}
}
}
}
}
I get this error:
A problem occurred evaluating project ':app'.
> Could not find method proto() for arguments [build_cxwfo79b6zcc266x9rsqzou9f$_run_closure1$_closure8$_closure10$_closure12#203aac02] on source set main of type com.android.build.gradle.internal.api.DefaultAndroidSourceSet.
You are in a good way, but, there is some stuff missing:
The gradle code I'll share is written in Kotlin, just in case. If you can convert your grade files to Kotlin, nice, if not you have to convert them to groovy.
The first thing to check is if you have the proto folder in the right path, it should be in
root->app->src->main->proto
In the project gradle make sure to have the plugin applied
id("com.google.protobuf") version "0.8.15" apply false
In the app gradle, make sure to have the following.
import com.google.protobuf.gradle.*
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.protobuf")
}
The dependencies:
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2")
implementation("com.google.protobuf:protobuf-kotlin:3.21.2")
implementation("io.grpc:grpc-stub:1.52.0")
implementation("io.grpc:grpc-protobuf:1.52.0")
implementation("io.grpc:grpc-okhttp:1.52.0")
implementation("com.google.protobuf:protobuf-java-util:3.21.7")
implementation("com.google.protobuf:protobuf-kotlin:3.21.2")
implementation("io.grpc:grpc-kotlin-stub:1.3.0")
And the protobuf task:
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:${rootProject.ext["protobufVersion"]}"
}
plugins {
id("java") {
artifact = "io.grpc:protoc-gen-grpc-java:${rootProject.ext["grpcVersion"]}"
}
id("grpc") {
artifact = "io.grpc:protoc-gen-grpc-java:${rootProject.ext["grpcVersion"]}"
}
id("grpckt") {
artifact = "io.grpc:protoc-gen-grpc-kotlin:${rootProject.ext["grpcKotlinVersion"]}:jdk8#jar"
}
}
generateProtoTasks {
all().forEach {
it.plugins {
id("java") {
option("lite")
}
id("grpc") {
option("lite")
}
id("grpckt") {
option("lite")
}
}
it.builtins {
id("kotlin") {
option("lite")
}
}
}
}
}
These are the versions I'm using:
ext["grpcVersion"] = "1.47.0"
ext["grpcKotlinVersion"] = "1.3.0" // CURRENT_GRPC_KOTLIN_VERSION
ext["protobufVersion"] = "3.21.2"
ext["coroutinesVersion"] = "1.6.2"
Having that your project should generate the code based on your proto files.
For further reference, I recently build this Android App based on Kotlin + gRPC: https://github.com/wilsoncastiblanco/notes-grpc

KMM + Compose: Unresolved reference: drawable

I have a KMM application in which the android target uses Jetpack Compose. I am getting the following error when I try to use a drawable resource:
e: [...]/OnboardingScreen.kt: (33, 46): Unresolved reference: drawable
This is the result of trying to access a drawable via painterResource(id = R.drawable.ic_icon).
I have tried the following things to fix the issue:
Clean and build the project
Invalidate cache and restart
Fix all warnings when executing ./gradlew assembleDebug
The static R class is correctly imported
Nothing of the above solves the problem.
This is my build.gradle.kts of the android module:
plugins {
id("com.android.application")
kotlin("android")
}
val composeVersion = findProperty("version.compose") as String
val composeNavigationVersion = findProperty("version.composeNavigation") as String
val koinVersion = findProperty("version.koin") as String
android {
compileSdk = (findProperty("android.compileSdk") as String).toInt()
defaultConfig {
applicationId = "com.app.app.android"
minSdk = (findProperty("android.minSdk") as String).toInt()
targetSdk = (findProperty("android.targetSdk") as String).toInt()
versionCode = 1
versionName = "1.0"
}
buildFeatures {
compose = true
}
// Set both the Java and Kotlin compilers to target Java 8.
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
composeOptions {
kotlinCompilerExtensionVersion = composeVersion
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
}
dependencies {
implementation(project(":shared"))
// Android
implementation("com.google.android.material:material:1.4.0")
// Jetpack Compose
implementation("androidx.compose.ui:ui:$composeVersion")
implementation("androidx.compose.ui:ui-tooling:$composeVersion")
implementation("androidx.compose.foundation:foundation:$composeVersion")
implementation("androidx.compose.material:material:$composeVersion")
implementation("androidx.compose.material:material-icons-core:$composeVersion")
implementation("androidx.compose.material:material-icons-extended:$composeVersion")
implementation("androidx.activity:activity-compose:$composeVersion")
implementation("androidx.navigation:navigation-compose:$composeNavigationVersion")
// Koin
implementation("io.insert-koin:koin-android:$koinVersion")
}
configurations.all {
resolutionStrategy {
force("org.jetbrains.kotlin:kotlin-stdlib:1.5.31")
}
}
My build.gradle.kts of the shared module:
plugins {
kotlin("multiplatform")
kotlin("native.cocoapods")
id("com.android.library")
}
version = "1.0"
kotlin {
android()
iosX64()
iosArm64()
iosSimulatorArm64() // sure all ios dependencies support this target
cocoapods {
summary = "Some description for the Shared Module"
homepage = "Link to the Shared Module homepage"
ios.deploymentTarget = "14.1"
podfile = project.file("../ios/Podfile")
framework {
baseName = "shared"
}
}
val multiplatformVersion = findProperty("version.multiplatformSettings") as String
val koinVersion = findProperty("version.koin") as String
val coroutinesVersion = findProperty("version.coroutines") as String
sourceSets {
val commonMain by getting {
dependencies {
implementation("com.russhwolf:multiplatform-settings-no-arg:$multiplatformVersion")
implementation("io.insert-koin:koin-core:$koinVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
val androidMain by getting
val androidTest by getting {
dependencies {
implementation(kotlin("test-junit"))
implementation("junit:junit:4.13.2")
}
}
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}
val iosX64Test by getting
val iosArm64Test by getting
val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
}
}
android {
compileSdk = 31
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdk = 22
targetSdk = 31
}
}
And finally, my gradle.properties:
# Gradle
org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
# Kotlin
kotlin.code.style=official
# Android
android.useAndroidX=true
android.compileSdk=31
android.targetSdk=31
android.minSdk=22
# MPP
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.mpp.enableCInteropCommonization=true
kotlin.native.enableDependencyPropagation=false
kotlin.native.ignoreDisabledTargets=true
# Common versions
version.multiplatformSettings=0.8.1
version.koin=3.1.4
version.coroutines=1.5.2-native-mt
# Android versions
version.compose=1.0.5
version.composeNavigation=2.4.0-rc01
Thanks in advance.
That's how you reference image resources:
import com.your.package.R
val img = R.drawable.img_name
or
val img = com.your.package.R.drawable.img_name
Denny Kurniawan's comment above pointed me in the right direction. I had a similar problem after changing the name of my project. At the top of your 'OnboardingScreen.kt' file, check to see if the full 'package' name matches your actual project's name, e.g. "package com.android.application".
If the package name listed in 'OnboardingScreen.kt' is not the same as your actual project's name, then the static R class is probably not being imported correctly and, hence, this error.
In other words, the full package name in 'OnboardingScreen.kt' should match the name of the folder shown under 'app->src->main->java'. Hope this helps someone as it did me.
for me, this error happen because I drag my image to the resource manager, I solve it by importing the images manually with the "Import Drawables" button

Can only add Kotlin Multiplatform Mobile library from maven by using releaseImplementation and debugImplementation

Every time I publish a Kotlin Multiplatform Mobile library to maven central the only I can seem to add/use the Android dependency in an Android app is by adding both the releaseImplementation and debugImplementation
Example
releaseImplementation 'io.github.tyczj.lumberjack:Lumberjack-android:1.0.0#aar'
debugImplementation 'io.github.tyczj.lumberjack:Lumberjack-android-debug:1.0.0#aar'
Instead of the "normal" way where you just have a single implementation
implementation 'io.github.tyczj.lumberjack:Lumberjack-android:1.0.0'
Here is my build.gradle file
plugins {
kotlin("multiplatform") version "1.4.32"
id("com.android.library")
id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
id("maven-publish")
id("signing")
}
group = "io.github.tyczj.lumberjack"
version = "1.0.2"
ext["signing.keyId"] = ""
ext["signing.password"] = ""
ext["signing.secretKeyRingFile"] = ""
repositories {
google()
mavenCentral()
maven {
setUrl("https://plugins.gradle.org/m2/")
}
}
val javadocJar by tasks.registering(Jar::class) {
archiveClassifier.set("javadoc")
}
val emptyJar by tasks.registering(Jar::class) {
archiveAppendix.set("empty")
}
kotlin {
android{
publishLibraryVariants("release", "debug")
}
iosX64("ios") {
binaries {
framework {
baseName = "lumberjack"
}
}
}
sourceSets {
val commonMain by getting
val commonTest by getting
val androidMain by getting
val androidTest by getting
val iosMain by getting
val iosTest by getting
}
}
android {
compileSdkVersion(29)
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdkVersion(24)
targetSdkVersion(29)
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
}
afterEvaluate {
publishing {
repositories {
maven {
name = "sonatype"
url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = rootProject.ext["ossrhUsername"]?.toString()
password = rootProject.ext["ossrhPassword"]?.toString()
}
}
}
publications.withType<MavenPublication> {
artifact(javadocJar.get())
pom{
name.set("Lumberjack")
description.set("Logging library for Kotlin Multiplatform Mobile applications")
url.set("https://github.com/tyczj/Lumberjack")
licenses {
license {
name.set("MIT")
url.set("https://opensource.org/licenses/MIT")
}
}
developers {
developer {
id.set("tyczj")
name.set("Jeff Tycz")
email.set("tyczj359#gmail.com")
}
}
scm {
url.set("https://github.com/tyczj/Lumberjack")
}
}
}
}
}
ext["signing.keyId"] = rootProject.ext["signing.keyId"]?.toString()
ext["signing.password"] = rootProject.ext["signing.password"]?.toString()
ext["signing.secretKeyRingFile"] = rootProject.ext["signing.secretKeyRingFile"]?.toString()
signing {
sign(publishing.publications)
}
apply(from = "${rootDir}/scripts/publish-root.gradle")
The full source for this library can be found here
What is wrong with how I am building/publishing KMM libraries where I have to specify the release and debug implementations?
You should not specify -android postfix, just use implementation("io.github.tyczj.lumberjack:Lumberjack:1.0.0").
This works because dependency variant resolution is based on Gradle Module Metadata. This metadata is just another file published with your library (it has .module extension) and it contains description of all variants. As you are publishing the library as a whole, the top-level artifact io.github.tyczj.lumberjack:Lumberjack contains the metadata for the whole library, allowing gradle to choose the right variant.
Another option would be to make sure your -android artifact contains proper module metadata with both release and debug variants. I believe publishLibraryVariantsGroupedByFlavor is the way to tell the publisher plugin to make it this way, but I have not tried it.

Kotlin Multiplatform Configuration issue

I continue getting Gradle configuration error in my KMP + Jetpack Compose project
A problem occurred configuring project ':shared'.
Configuration with name 'testApi' not found.
My setup is:
Android Studio Arctic Fox 2020.3.1 Canary 3
Project level setup
dependencies {
classpath("com.android.tools.build:gradle:7.0.0-alpha03")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20")
}
'shared module'
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
kotlin("multiplatform")
id("com.android.library")
}
kotlin {
android()
ios {
binaries {
framework {
baseName = "shared"
}
}
}
sourceSets {
val commonMain by getting
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
val androidMain by getting {
dependencies {
implementation("com.google.android.material:material:1.2.1")
}
}
val androidTest by getting {
dependencies {
implementation(kotlin("test-junit"))
implementation("junit:junit:4.13.1")
}
}
val iosMain by getting
val iosTest by getting
}
}
android {
compileSdkVersion(30)
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdkVersion(21)
targetSdkVersion(30)
}
}
val packForXcode by tasks.creating(Sync::class) {
group = "build"
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator"
val targetName = "ios" + if (sdkName.startsWith("iphoneos")) "Arm64" else "X64"
val framework = kotlin.targets.getByName<KotlinNativeTarget>(targetName).binaries.getFramework(mode)
inputs.property("mode", mode)
dependsOn(framework.linkTask)
val targetDir = File(buildDir, "xcode-frameworks")
from({ framework.outputDirectory })
into(targetDir)
}
tasks.getByName("build").dependsOn(packForXcode)
Note:
By removing the configuration part by part, I seem to figure out w
that the problem seems to be around the android configuration itself,
so if I remove android() part from
kotlin {
android()
....
and just go with simple jvm() it goes well
You can use the below code as a workaround in your shared module Gradle file
android {
configurations {
create("androidTestApi")
create("androidTestDebugApi")
create("androidTestReleaseApi")
create("testApi")
create("testDebugApi")
create("testReleaseApi")
}
}
NOTE: This has to be put before the kotlin {} block
Fixed issue in Kotlin 1.5 M1 (pending)
The problem is in Canary or AGP 7.0.0:
IDE: Canary 11
distributionUrl: 6.8.2
7.0.0-alpha11
Workaround 1:
IMPORTANT: Make sure the file is groovy or dsl
PRECONDITION: These configurations have to be done in all modules / sub-modules of the project that are KMM and the android {} block has to be before the kotlin {} block
For Kotlin DSL:
build.gradle.kts (:kmm_shared)
android {
configurations {
create("androidTestApi")
create("androidTestDebugApi")
create("androidTestReleaseApi")
create("testApi")
create("testDebugApi")
create("testReleaseApi")
}
}
kotlin { }
For Groovy:
build.gradle (:kmm_shared)
android {
configurations {
androidTestApi {}
androidTestDebugApi {}
androidTestReleaseApi {}
testApi {}
testDebugApi {}
testReleaseApi {}
}
}
kotlin { }
Also, you should to use AGP 7.0 because previous versions of gradle generate problems.
build.gradle.kts (:project) && build.gradle.kts (:buildSrc)
dependencies {
implementation("com.android.tools.build:gradle:7.0.0-alpha11")
}
Workaround 2 (Deprecated)
Temporarily use a maximum of the beta versions:
IDE: Beta 6
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
classpath 'com.android.tools.build:gradle:4.2.0-beta06'
Good Luck
issue/KT-43944
commit in Mobius

Android Studio: "shared" module directories of KMM project keep getting unmarked as sources root

I created a new project in Android Studio using KMM wizard. I was following handson tutorial and I noticed that I don't have an option of creating a package inside of some directories. Specifically, inside of "shared" module only kotlin directory of androidMain folder is always marked as "sources root".
I manually marked kotlin directories in other folders (commonMain, iosMain) as "sources root". I also marked sqldelight directory inside of commonMain as "sources root".
But Android Studio keeps reverting that state back periodically. I don't know what is causing this issue. It also shows that kotlin directory of androidMain folder is set as "sources root" and also is not. Which is weird, directory can't be set and unset as "sources root" at the same time.
Is it a bug of Android Studio, KMM Plugin or some kind of setting in preferences?
Android Studio version: 4.1.1
KMM plugin version: 0.1.3-release-54-Studio4.1
EDIT:
build.gradle.kts for shared module:
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
id("com.android.library")
id("kotlin-android-extensions")
id("com.squareup.sqldelight")
}
group = "com.example.kmmapplication"
version = "1.0-SNAPSHOT"
repositories {
gradlePluginPortal()
google()
jcenter()
mavenCentral()
}
kotlin {
android()
ios {
binaries {
framework {
baseName = "shared"
}
}
}
// Block from https://github.com/cashapp/sqldelight/issues/2044#issuecomment-721299517.
val onPhone = System.getenv("SDK_NAME")?.startsWith("iphoneos") ?: false
if (onPhone) {
iosArm64("ios")
} else {
iosX64("ios")
}
val coroutinesVersion = "1.3.9-native-mt"
val serializationVersion = "1.0.1"
val ktorVersion = "1.4.2"
val sqlDelightVersion: String by project
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-serialization:$ktorVersion")
implementation("com.squareup.sqldelight:runtime:$sqlDelightVersion")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
val androidMain by getting {
dependencies {
implementation("io.ktor:ktor-client-android:$ktorVersion")
implementation("com.squareup.sqldelight:android-driver:$sqlDelightVersion")
}
}
val androidTest by getting {
dependencies {
implementation(kotlin("test-junit"))
implementation("junit:junit:4.13.1")
}
}
val iosMain by getting {
dependencies {
implementation("io.ktor:ktor-client-ios:$ktorVersion")
implementation("com.squareup.sqldelight:native-driver:$sqlDelightVersion")
}
}
val iosTest by getting
}
}
android {
compileSdkVersion(29)
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdkVersion(24)
targetSdkVersion(29)
versionCode = 1
versionName = "1.0"
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
}
sqldelight {
database("AppDatabase") {
packageName = "com.example.kmmapplication.shared.cache"
}
}
val packForXcode by tasks.creating(Sync::class) {
group = "build"
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator"
val targetName = "ios" + if (sdkName.startsWith("iphoneos")) "Arm64" else "X64"
val framework =
kotlin.targets.getByName<KotlinNativeTarget>(targetName).binaries.getFramework(mode)
inputs.property("mode", mode)
dependsOn(framework.linkTask)
val targetDir = File(buildDir, "xcode-frameworks")
from({ framework.outputDirectory })
into(targetDir)
}
tasks.getByName("build").dependsOn(packForXcode)
settings.gradle.kts:
pluginManagement {
repositories {
gradlePluginPortal()
google()
jcenter()
mavenCentral()
}
resolutionStrategy {
eachPlugin {
if (requested.id.namespace == "com.android" || requested.id.name == "kotlin-android-extensions") {
useModule("com.android.tools.build:gradle:4.0.1")
}
}
}
}
rootProject.name = "KMMApplication"
include(":androidApp")
include(":shared")
gradle-wrapper.properties:
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
These ios target definitions conflict:
ios { //Target 1
binaries {
framework {
baseName = "shared"
}
}
}
// Block from https://github.com/cashapp/sqldelight/issues/2044#issuecomment-721299517.
val onPhone = System.getenv("SDK_NAME")?.startsWith("iphoneos") ?: false
if (onPhone) {
iosArm64("ios") //Target 2
} else {
iosX64("ios") //Target 3
}
You have ios, which is a combined target for arm and x64, then the individual targets iosArm64 and iosX64`. I don't know if that's what's causing the IDE issue, but it's certainly confusing.
The 2nd two don't define frameworks as they were taken from a context that would use cocoapods. If you look at the sqldelight issue comment, it came from me, and ultimately came from KaMPKit: https://github.com/touchlab/KaMPKit/blob/master/shared/build.gradle.kts#L28.
To get the IDE working, I'd suggest removing the first ios target. However, again, the sample from the sqldelight issue assumes you have cocoapods configured. You'll either need to add cocoapods or update both the target config and packForXcode.
The targets would look something like the following.
val onPhone = System.getenv("SDK_NAME")?.startsWith("iphoneos") ?: false
if (onPhone) {
iosArm64("ios") {
binaries {
framework {
baseName = "shared"
}
}
}
} else {
iosX64("ios") {
binaries {
framework {
baseName = "shared"
}
}
}
}
As an alternative, I'd suggest just using KaMPKit as a base for your project until you're more familiar with the KMM plugin samples and config options. They don't quite work out of the box yet.

Categories

Resources