Ktor and kotlinx dependencies not found when building iOS framework - ios

I am trying to include support for ktor and kotlinx (serialization and coroutines to be exact) to my Kotlin/Native project. All libraries work on the Android version of the project, but when I try to compile the Xcode project, I receive errors stating that the kotlinx.serialization and kotlinx.coroutines libraries could not be found:
src/commonMain/kotlin/.../.kt:4:16: error: unresolved reference: coroutines
import kotlinx.coroutines.CoroutineDispatcher
^
src/commonMain/kotlin/.../.kt:5:16: error: unresolved reference: serialization
import kotlinx.serialization.json.JSON
Here is my gradle file for my project:
plugins {
id 'kotlin-multiplatform' version '1.3.11'
id 'kotlinx-serialization' version '1.3.0'
}
ext {
ktor_version = '1.0.1'
kotlinx_version = '1.1.0'
}
repositories {
maven { url "https://kotlin.bintray.com/ktor" }
maven { url "https://kotlin.bintray.com/kotlinx" }
google()
jcenter()
mavenCentral()
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions'
configurations {
compilerPlugin
}
android {
compileSdkVersion 27
defaultConfig {
applicationId "org.jetbrains.kotlin.mpp_app_android"
minSdkVersion 15
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
}
kotlin {
targets {
fromPreset(presets.android, 'android')
// This preset is for iPhone emulator
// Switch here to presets.iosArm64 (or iosArm32) to build library for iPhone device
fromPreset(presets.iosX64, 'ios') {
compilations.main.outputKinds('FRAMEWORK')
}
}
sourceSets {
commonMain {
dependencies {
implementation 'commons-codec:commons-codec:1.10'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
implementation("io.ktor:ktor-client:$ktor_version")
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$kotlinx_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.9.1"
}
}
commonTest {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-test-common'
implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common'
}
}
androidMain {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib'
implementation("io.ktor:ktor-client-android:$ktor_version")
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinx_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.9.1"
}
}
androidTest {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-test'
implementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
}
iosMain {
dependencies {
implementation("io.ktor:ktor-client-ios:$ktor_version")
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:$kotlinx_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:0.9.1"
}
}
iosTest {
}
}
}
// This task attaches native framework built from ios module to Xcode project
// (see iosApp directory). Don't run this task directly,
// Xcode runs this task itself during its build process.
// Before opening the project from iosApp directory in Xcode,
// make sure all Gradle infrastructure exists (gradle.wrapper, gradlew).
task copyFramework {
def buildType = project.findProperty("kotlin.build.type") ?: "DEBUG"
def target = project.findProperty("kotlin.target") ?: "ios"
dependsOn "link${buildType.toLowerCase().capitalize()}Framework${target.capitalize()}"
doLast {
def srcFile = kotlin.targets."$target".compilations.main.getBinary("FRAMEWORK", buildType)
def targetDir = getProperty("configuration.build.dir")
copy {
from srcFile.parent
into targetDir
include 'app.framework/**'
include 'app.framework.dSYM'
}
}
}
Any help would be greatly appreciated, thanks!

Make sure enableFeaturePreview("GRADLE_METADATA") is in the settings.gradle file. Once I added this line, I was able to compile from Xcode.

I'm having this problem as well. And it suddenly appeared on a project that I've been working on for months. I do have enableFeaturePreview('GRADLE_METADATA') set in settings.gradle. What could be going on? This is really frustrating; my project is suddenly at a standstill for no apparent reason.

Related

Failing to generate libraryname.xcframework in kotlin multiplatform mobile (KMM)

I was trying to generate the XCFramework with Kotlin 1.5.31 containing the targets for iOSArm64 and iOSX64 .
With the below build.gradle.kt , it generates a FatFrameworks . I’m failing to generate XCFrameworks .
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework
kotlin {
val xcFramework = XCFramework(libName)
android()
ios {
binaries.framework(libName) {
xcFramework.add(this)
}
}
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")
}
}
val iosMain by getting
val iosTest by getting
}
}
And also I have included the tasks in build.gradle.kts :
tasks {
register(“buildDebugXCFramework”)
register(“buildReleaseXCFramework”)
register(“publishDevFramework”)
register(“publishFramework”)
}
This is the output I got : fatframeowrks generated but not the libraryname.xcframeworks
If Any suggestions to generate XCFrameworks with targets iOSArm64 and iOSX64 ? , it would be helpful , Thank you .
I think that following the documentation might help here.
Please set library name by the baseName option and build the final XCFramework by running assembleXCFramework Gradle task.
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework
kotlin {
val xcFramework = XCFramework()
android()
ios {
binaries.framework() {
baseName = "libName"
xcFramework.add(this)
}
}
...

Kotlin multiplatform project depending on another one with cocoapods

I have a kotlin multipltform library which has several cocoapods
cocoapods {
....
pod("gRPC/GRPCCore", grpcVersion)
pod("gRPC-ProtoRPC", grpcVersion)
pod("Protobuf", "3.15.8")
// etc
}
I published this library to artifactory (maven) and the ios target contains all the klib's for the cinterop'ed pods.
I have a second Kotlin Multiplatform Library where I wish to consume the previous "core" library.
iOSTarget("ios") {
binaries {
framework {
baseName = "SecondSharedLib"
export("first.shared.lib:1.0.0")
xcf.add(this)
}
}
}
sourceSets {
val commonMain by getting {
dependencies {
api("first.shared.lib:1.0.0")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val androidMain by getting {
dependencies {
}
}
val androidTest by getting {
dependencies {
implementation("junit:junit:4.13.2")
}
}
val iosMain by getting {
dependencies {
}
}
val iosTest by getting
}
When linking ios however the pod modules are not found.
ex: ld: framework not found gRPC_ProtoRPC
I also tried redeclaring the pods to no avail.
Is what I am trying to do even possible? does anyone have any suggestions?
Note: I am not an iOS developer so please be harsh if my understanding is off
For anyone dealing with a nested multiplatform library where the base library relies on the cocoapods plugin, I had to link all the frameworks via the linker opts as well as redefine all the cocoapods so that the frameworks would be available for linking.
have you tried below code instead when referencing another KMM project? Below code snippet is from Kotlin's documentation
kotlin {
sourceSets["commonMain"].dependencies {
implementation(project(":some-other-multiplatform-module"))
}
sourceSets["androidMain"].dependencies {
//platform part of :some-other-multiplatform-module will be added automatically
}
}

How to make dependency project visible for Ios app?

I have a kotlin multiplatform project MusicFeature with targets for ios, android, common with the following build.gradle
sourceSets {
commonMain {
dependencies {
implementation(project(":ProjectUtils"))
}
}
androidMain {
}
iosMain {
}
}
ProjectUtils is also a multiplatform project.
There's no troubles in using ProjectUtils code from MusicFeature. But when I export MusicFeature as a framework for iOS, I don't have access to classes from ProjectUtils.
I guess you are looking for transitiveExport = true for your framework.
binaries {
framework {
export project(':dependency')
// Export transitively.
transitiveExport = true
}
}
You can find more reference here in export dependencies to binaries.

Kotlin Multiplatform Library: Unable to generate .framework for iOS

I am new to Android/KotlinMultiplatform , I am trying to create a library for iOS/Android using Kotlin Multiplatform.
When I run the command on terminal
./gradlew :shared:packForXcode
It succeeds but could not find a /build/xcode-frameworks folder inside the root folder.
Could anyone help me to find where it is going wrong...?
IntelliJ CE Version : 2020.2.3
My Gradle file Content:
plugins {
id("org.jetbrains.kotlin.multiplatform") version "1.4.10"
id("com.android.library")
id("kotlin-android-extensions")
"maven-publish"
}
repositories {
mavenCentral()
}
group "me.myname"
version "0.0.1"
kotlin {
targets {
android()
ios {
binaries {
framework {
baseName = "MyLib"
}
}
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")
}
}
val androidMain by getting {
dependencies { }
}
val iosMain by getting {
dependencies { }
}
}
}
android {
compileSdkVersion(29)
defaultConfig {
minSdkVersion(24)
targetSdkVersion(29)
versionCode = 1
versionName = "1.0"
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
}
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 sdkName: String? = System.getenv("SDK_NAME")
val isiOSDevice = sdkName.orEmpty().startsWith("iphoneos")
val framework = kotlin.targets
.getByName<KotlinNativeTarget>(
if(isiOSDevice) {
"iosArm64"
} else {
"iosX64"
}
)
.binaries.getFramework(mode)
inputs.property("mode", mode)
dependsOn(framework.linkTask)
from({ framework.outputDirectory })
into(targetDir)
println("Build Folder => $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.build.dependsOn("packForXCode")
UPDATE
Project Created using IntelliJ IDEA, as below screenshot:
My project structure looks like below:
I've only been able to see the template of your screenshot by using
IntelliJ 2020.2.3 Ultimate
This template doesn't have the packForXcode task set by default, so you would have put it by hands I suppose.
Anyway, with a cleaned project, if you run it, you could have the debug framework in the build folder where you want to have it.
You should have, of course, at least one source (Greeting.kt) file like the one I've shown you in my pic.
I suggest you to look deep at the documentation starting from here and here.
If I remember correctly, this task is not designed to be executed manually. It should be triggered as a part of the Xcode project build, see in the documentation. Please try to follow the steps from the documentation, and see if the framework connects and works fine from Xcode.

Linking ktor and kotlinx.coroutines results in undefined symbols

I am trying to link my Kotlin/Native project as a framework for iOS. The Kotlin code compiles just fine, but when the linker is run, I get the following error:
Undefined symbols for architecture x86_64:
"_kfun:kotlinx.coroutines.AbstractContinuation.getResult$kotlinx-coroutines-core-native()kotlin.Any?", referenced from:
_kfun:io.ktor.client.engine.ios.IosClientEngine.execute(io.ktor.client.call.HttpClientCall;io.ktor.client.request.HttpRequestData)io.ktor.client.call.HttpEngineCall in combined.o
ld: symbol(s) not found for architecture x86_64
error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld invocation reported errors
I have tried changing the platform from iosX64 to iosArm64 but it results in the same error. Here is my gradle file for reference just in case I'm missing a dependency.
plugins {
id 'kotlin-multiplatform' version '1.3.11'
id 'kotlinx-serialization' version '1.3.0'
}
ext {
ktor_version = '1.0.1'
coroutines_version = '1.1.0'
serialization_version = '0.9.1'
}
repositories {
maven { url "https://kotlin.bintray.com/ktor" }
maven { url "https://kotlin.bintray.com/kotlinx" }
google()
jcenter()
mavenCentral()
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions'
configurations {
compilerPlugin
}
android {
compileSdkVersion 27
defaultConfig {
applicationId "org.jetbrains.kotlin.mpp_app_android"
minSdkVersion 15
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
}
kotlin {
targets {
fromPreset(presets.android, 'android')
// This preset is for iPhone emulator
// Switch here to presets.iosArm64 (or iosArm32) to build library for iPhone device
fromPreset(presets.iosX64, 'ios') {
compilations.main.outputKinds('FRAMEWORK')
}
}
sourceSets {
commonMain {
dependencies {
implementation 'commons-codec:commons-codec:1.10'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
implementation("io.ktor:ktor-client:$ktor_version")
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version"
}
}
commonTest {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-test-common'
implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common'
}
}
androidMain {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib'
implementation("io.ktor:ktor-client-android:$ktor_version")
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
}
}
androidTest {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-test'
implementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
}
iosMain {
dependencies {
implementation "io.ktor:ktor-client-ios_debug_ios_x64:$ktor_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version"
}
}
iosTest {
}
configure([ios_x86_64Main, ios_arm64Main]) {
dependsOn iosMain
}
}
}
// This task attaches native framework built from ios module to Xcode project
// (see iosApp directory). Don't run this task directly,
// Xcode runs this task itself during its build process.
// Before opening the project from iosApp directory in Xcode,
// make sure all Gradle infrastructure exists (gradle.wrapper, gradlew).
task copyFramework {
def buildType = project.findProperty("kotlin.build.type") ?: "DEBUG"
def target = project.findProperty("kotlin.target") ?: "ios"
dependsOn "link${buildType.toLowerCase().capitalize()}Framework${target.capitalize()}"
doLast {
def srcFile = kotlin.targets."$target".compilations.main.getBinary("FRAMEWORK", buildType)
def targetDir = getProperty("configuration.build.dir")
copy {
from srcFile.parent
into targetDir
include 'app.framework/**'
include 'app.framework.dSYM'
}
}
}
Any help would be greatly appreciated, thanks!
Your iosMain Ktor dependency seems wrong. Try setting it like this:
implementation "io.ktor:ktor-client-ios:$ktor_version"
implementation "io.ktor:ktor-client-core-ios:$ktor_version"
implementation "io.ktor:ktor-client-json-ios:$ktor_version"

Resources