MacOS Kmp cannot install ktor dependency - xcode

Working with Kotlin KMP and targeting macos and I am having trouble installing a ktor dependency.
Here is my shared module gradle file
plugins {
kotlin("multiplatform")
id("kotlinx-serialization")
kotlin("native.cocoapods")
id("com.android.library")
}
version = "1.0"
kotlin {
android()
jvm()
val iosTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget =
if (System.getenv("SDK_NAME")?.startsWith("iphoneos") == true)
::iosArm64
else
::iosX64
iosTarget("ios") {}
macosX64("macos")
cocoapods {
summary = "Some description for the Shared Module"
homepage = "Link to the Shared Module homepage"
// ios.deploymentTarget = AppleSdk.iosDeploymentTarget
// osx.deploymentTarget = AppleSdk.osxDeploymentTarget
frameworkName = "shared"
}
sourceSets {
val commonMain by getting {
dependencies {
// ktor dependencies needs to use old coroutine version
// https://youtrack.jetbrains.com/issue/KT-46697
implementation(Kotlinx.coroutineCore)
// Koin
implementation(Koin.core)
implementation(Koin.test)
// Ktor
implementation(Ktor.clientCore)
implementation(Ktor.clientJson)
implementation(Ktor.clientLogging)
implementation(Ktor.clientSerialization)
// serialization
implementation(Kotlinx.serializationCore)
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
val androidMain by getting {
dependencies {
implementation(Ktor.clientAndroid)
}
}
val androidTest by getting {
dependencies {
implementation(kotlin("test-junit"))
implementation(Test.junit)
}
}
val iosMain by getting {
dependencies {
// "io.ktor:ktor-client-ios:1.6.0"
implementation(Ktor.clientIos)
}
}
val iosTest by getting
val jvmMain by getting {
dependencies {
implementation(Ktor.clientJvm)
}
}
val jvmTest by getting {
dependencies {
implementation(kotlin("test-junit"))
implementation(Test.junit)
}
}
val macosMain by getting {
dependencies {
// "io.ktor:ktor-client-cio:1.6.0"
implementation(Ktor.clientCio)
}
}
val macosTest by getting
}
}
My solution builds in android studio and I am able to run my solution in xcode for ios. However when I get to the macos target I get this error in xcode:
duplicate symbol '_kclass:io.ktor.network.sockets.SocketTimeoutException' in:
{redacted}/shared/build/cocoapods/framework/shared.framework/shared(result.o)
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
When I remove the macos ktor dependency it builds just fine. I don't have any extra build gradles that are modified right now, so I know that I am not installing ktor in any extra places currently. I do not get why ios works and macos fails. I The way that I read that xcode error it seems like there is a duplicate ktor method/class? Any help would be greatly appreciated! I am modeling a lot after this repo as well https://github.com/joreilly/PeopleInSpace and the only major difference I can see is that I am still using cocoa pods and the other dev migrated to swift package manager

According to the documentation, Ktor's CIO engine is JVM-only. Try using curl one:
val macosMain by getting {
dependencies {
implementation("io.ktor:ktor-client-curl:$ktor_version")
}
}

Related

no build output compiled for common module of Kotlin Multiplatform project

I'm trying to figure out why dependent projects for my Kotlin MPP library don't see any provided modules in their common modules even though the targets (jvm, android) can see them.
Published via maven-publish.
The /build directory for the library contains nothing I can identify as an intermediate representation of my common modules, leading me to think that I need to explicitly tell Gradle to produce the files to be included as common in the published package.
As it is, the .aar and .jar files produced in the android and desktop (jvm) modules each look normal, but the published common module is empty.
I need that common module to be populated before I can code against it inside the common module of dependent projects.
Here is the relevant section of my build.gradle.kts. I omit the repository config as it appears to work.
I basically followed the instructions from kotlinlang.org.
I've looked at the maven-publish plugin configuration, the settings for the kotlin-multiplatformm plugin, and the configured project structure.
kotlin version is 1.6.10, unable to update due to Jetbrains Compose dependency.
plugins {
kotlin("multiplatform")
id("com.android.library")
id("maven-publish")
}
kotlin {
android {
publishLibraryVariants = listOf("release", "debug")
}
jvm("desktop") {
compilations.all {
kotlinOptions {
jvmTarget = "11"
}
}
}
val publicationsFromMainHost = listOf(jvm("desktop").name, "kotlinMultiplatform")
publishing {
publications {
matching { it.name in publicationsFromMainHost }.all {
val targetPublication = this#all
tasks.withType<AbstractPublishToMaven>()
.matching { it.publication == targetPublication }
}
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.2")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val androidMain by getting {
dependencies {
implementation("androidx.startup:startup-runtime:1.1.1")
}
}
val androidTest by getting {
dependencies {
implementation("junit:junit:4.13.2")
implementation("androidx.test:core:1.4.0")
implementation("androidx.test:runner:1.4.0")
implementation("androidx.test:rules:1.4.0")
implementation("org.robolectric:robolectric:4.6.1")
}
}
val desktopMain by getting
val desktopTest by getting {
dependencies {
implementation("junit:junit:4.13.2")
}
}
}
}
The answer is to manually supply the Kotlin stdlib dependency, rather than relying on the gradle plugin to add it.
When there are only jvm-based builds present, commonMain will be built with platform type of jdk8 rather than common. By making an explicit dependency on stdlib-common, it will be coerced back to the common platform, and then the correct metadata will be created and published.
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
}
}
}
}

Why is this a receiver type mismatch when it seems to match what I see in the Gradle docs?

I'm converting a build from Buildr to Gradle.
So far I have the following build script:
repositories {
jcenter()
}
allprojects {
version = "0.8.1-SNAPSHOT"
group = "org.trypticon.hex"
}
subprojects {
apply(plugin = "java-library")
apply(plugin = "maven-publish")
apply(plugin = "signing")
configure<JavaPluginExtension> {
sourceCompatibility = JavaVersion.VERSION_11
withJavadocJar()
withSourcesJar()
}
dependencies {
"testImplementation"("junit:junit:4.13")
}
configure<PublishingExtension> {
repositories {
maven {
url = uri(if (version.toString().contains("SNAPSHOT")) {
"https://oss.sonatype.org/content/repositories/snapshots"
} else {
"https://oss.sonatype.org/service/local/staging/deploy/maven2"
})
credentials {
username = System.getenv("DEPLOY_USER")
password = System.getenv("DEPLOY_PASS")
}
}
}
publications {
register<MavenPublication>("mavenJava") {
pom {
// omitting a bunch of metadata unrelatted to the issue
}
}
}
}
configure<SigningExtension> {
sign(publishing.publications["mavenJava"])
}
}
When I try to build, I get this:
build.gradle.kts:124:14: Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public val PluginDependenciesSpec.publishing: PluginDependencySpec defined in org.gradle.kotlin.dsl
IntelliJ says that the types match and shows no error.
I started with code from the Gradle docs which said to use this:
signing {
sign(publishing.publications["mavenJava"])
}
But that fails when inside a subprojects block.
Using:
Gradle 6.8
Java 11.0.3
Further investigation:
I tried splitting the lines out to see exactly which bit the issue was on.
configure<SigningExtension> {
val e: PublishingExtension = publishing
val p: Publication = e.publications["mavenJava"]
sign(p)
}
The error points directly at publishing on the first line. Hovering over it in IDEA, it helpfully tells me that Project.publishing is a PublishingExtension, so the call shows no error. Now the question is, why is it fine when inspecting the file, but fails at runtime? I thought this was exactly the kind of thing switching to Kotlin was going to stop. :(
I had a similar problem, which I fixed by writing the following code:
configure<SigningExtension> {
val pubExt = checkNotNull(extensions.findByType(PublishingExtension::class.java))
val publication = pubExt.publications["your_publication"]
sign(publication)
}
In your case, your_publication should be replaced with mavenJava.

Kotlin Multi Platform framwork CodeSig problem on a Mac OS application

I created a module using kotlin multi-platform and is it is working perfectly on Android and iOS, but when I try to do the same for a macOS app I get the following error:
XYZ.app: code object is not signed at all
Command CodeSign failed with a nonzero exit code
I'm generating the framework as follow:
kotlin {
jvm("android")
macosX64("mac") {
binaries {
framework {
baseName = "XyzForMac"
}
}
}
def iosClosure = {
binaries {
framework {
baseName = "XyzForIos"
}
}
}
if (System.getenv("SDK_NAME")?.startsWith("iphoneos")) {
iosArm64("ios", iosClosure)
} else {
iosX64("ios", iosClosure)
}
sourceSets {…}
}
task packForXcode(type: Sync) {
def targetDir = new File(buildDir, "xcode-frameworks")
def mode = System.getenv("CONFIGURATION") ?: "DEBUG"
def frameworkMac = kotlin.targets.getByName("mac").binaries.getFramework(mode)
inputs.property("mode", mode)
dependsOn frameworkMac.linkTask
from frameworkMac.outputDirectory
into targetDir
def frameworkIos = kotlin.targets.getByName("ios").binaries.getFramework(mode)
inputs.property("mode", mode)
dependsOn frameworkIos.linkTask
from frameworkIos.outputDirectory
into targetDir
doLast {
def gradlew = new File(targetDir, "gradlew")
gradlew.text = """\
|#!/bin/bash
|export 'JAVA_HOME=${System.getProperty("java.home")}'
|cd '${rootProject.rootDir}'
|./gradlew \$#""".stripMargin().stripIndent()
gradlew.setExecutable(true)
}
}
tasks.getByName("build").dependsOn(packForXcode)
It looks I'm doing everything correctly, especially because the app is running as expected on Android and iOS. I'm wondering if I missing any mac os specific configuration to make it works…
I solve the problem changing the approach, instead of using the custom task packForXcode and importing manually the framework, I'm using now the CocoaPods integration, so what I did:
Using cocoapods, deleting the packForXcode task (and the custom steps on Xcode)
I'm moving the targets to a specific block on gradle build:
Because I'm using cocoapods there are no different packed binaries anymore
kotlin {
targets { // <-- this is new
jvm("android")
macosX64("mac") {
binaries {
framework("Xyz") // Removed the ForMac
}
}
def iosClosure = {
binaries {
framework("Xyz") // Removed the ForIos
}
}
if (System.getenv("SDK_NAME")?.startsWith("iphoneos")) {
iosArm64("ios", iosClosure)
} else {
iosX64("ios", iosClosure)
}
}
cocoapods {…} // cocoa pods block
sourceSets {…}
}

How to add a dependency to build.gradle.kts for kotlin-multiplatform (kotlin 1.3.50)?

I started a new project with kotlin-multiplatform to create a library usable on iOS and Android using this tutorial :
https://play.kotlinlang.org/hands-on/Targeting%20iOS%20and%20Android%20with%20Kotlin%20Multiplatform/01_Introduction
It seems to work fine but I wanted to add the Serialization library mentioned at the end of the tutorial (https://github.com/Kotlin/kotlinx.serialization) and I can't make it work.
The setup guide in the library is not in Kotlin DSL so I tried different things to adapt the code but without success. Here is my project gradle :
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
maven { url "https://kotlin.bintray.com/kotlinx" }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
And now my build.gradle.kts
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
}
kotlin {
//select iOS target platform depending on the Xcode environment variables
val iOSTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget =
if (System.getenv("SDK_NAME")?.startsWith("iphoneos") == true)
::iosArm64
else
::iosX64
iOSTarget("ios") {
binaries {
framework {
baseName = "SharedCode"
}
}
}
jvm("android")
sourceSets["commonMain"].dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-common")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.13.0")
}
sourceSets["androidMain"].dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.13.0")
}
sourceSets["iosMain"].dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:0.13.0")
}
}
val packForXcode by tasks.creating(Sync::class) {
val targetDir = File(buildDir, "xcode-frameworks")
/// selecting the right configuration for the iOS
/// framework depending on the environment
/// variables set by Xcode build
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
val framework = kotlin.targets
.getByName<KotlinNativeTarget>("ios")
.binaries.getFramework(mode)
inputs.property("mode", mode)
dependsOn(framework.linkTask)
from({ framework.outputDirectory })
into(targetDir)
/// generate a helpful ./gradlew wrapper with embedded Java path
doLast {
val gradlew = File(targetDir, "gradlew")
gradlew.writeText("#!/bin/bash\n"
+ "export 'JAVA_HOME=${System.getProperty("java.home")}'\n"
+ "cd '${rootProject.rootDir}'\n"
+ "./gradlew \$#\n")
gradlew.setExecutable(true)
}
}
tasks.getByName("build").dependsOn(packForXcode)
I have no errors but I cannot use the library in my code.
Can someone please explain how to integrate this dependency or any dependency with this setup ? What do I do wrong ?
Note : I'm using Android Studio 3.5.1, Gradle 5.4.1, Kotlin 1.3.50.
Ok, so I found the issue.. just the version of the library.. 0.13.0 not 0.14.0. No error is thrown when you sync a wrong library version. I hope this post helps someone anyway.

How to add a dependency in a multiplatform kotlin / native intellij project?

I have the following build.gradle configuration:
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.41'
}
repositories {
mavenCentral()
}
kotlin {
linuxX64("linux") {
binaries {
executable {
entryPoint = 'sample.main'
runTask?.args('')
}
}
}
sourceSets {
linuxMain {
dependencies {
api("org.http4k:http4k-core:3.183.0")
}
}
linuxTest {
}
}
}
And the following source file src/linuxMain/kotlin/sample/SampleLinux.kt :
package sample
fun hello(): String = "Hello, Kotlin/Native!"
fun main() {
println(hello())
}
How to add a external library in order to be able to use autocomplete in imports for the library org.http4k:http4k-core:3.183.0?
As you can see, I tried to add the line api("org.http4k:http4k-core:3.183.0") in linuxMain dependencies, but although intellij show the library in External Libraries section, I cannot work with the packages neither classes of http4k in SampleLinux.kt file: any org.http4k..... import attempt is not recognized and generates compilation error.
After a quick look, I am almost sure that http4k is JVM-only library, at least for now. According to this issue, they are still waiting for Native to grow. If you are interested, it would be nice if one can ask the library maintainers again. As far as K/N has grown a lot by the last year, maybe they change their mind.

Resources