ktor 依赖项未在多平台项目的 ios 模块中解析

ktor dependencies not resolved in ios module of multiplatform project

我有一个针对 iOS 和 Android 的 kotlin 多平台项目。 公共模块使用Ktor http客户端

Android 应用一切正常。 但是当使用 iOS lib 构建项目时,我收到以下异常:

> Task :app:compileKotlinIos FAILED
src/commonMain/kotlin/com/ottamotta/mozoli/api/MozoliApiKtor.kt:4:8: error: unresolved reference: io
import io.ktor.client.HttpClient
       ^
src/commonMain/kotlin/com/ottamotta/mozoli/api/MozoliApiKtor.kt:5:8: error: unresolved reference: io
import io.ktor.client.features.feature

...还有其他的,说 none 的 ktor 依赖关系已经解决。

这里是build.gradle:

plugins {
    id 'kotlin-multiplatform' version '1.3.10'
}
repositories {
    google()
    jcenter()
    mavenCentral()
    maven { url "https://kotlin.bintray.com/kotlinx" }
}

ext {
    support_lib_version = '28.0.0'
    ktor_version = '1.0.0'
}

def keystorePropertiesFile = rootProject.file("./app/secret.properties");
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 28
    buildToolsVersion '28.0.3'
    defaultConfig {
        applicationId "com.ottamotta.mozoli"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        manifestPlaceholders = [auth0Domain: "@string/com_auth0_domain", auth0Scheme: "https"]
    }
    buildTypes {

        debug {
            resValue "string", "com_auth0_client_id", keystoreProperties['com_auth0_client_id']
        }
        release {
            resValue "string", "com_auth0_client_id", keystoreProperties['com_auth0_client_id']
            minifyEnabled false
        }
    }

    lintOptions {
        abortOnError false
    }


}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "com.android.support:recyclerview-v7:${support_lib_version}"
    implementation "com.android.support:appcompat-v7:${support_lib_version}"
    implementation 'com.squareup.picasso:picasso:2.71828'

    implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.7"

    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.1")
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'

    implementation "com.auth0.android:auth0:1.14.1"

    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 'org.jetbrains.kotlin:kotlin-stdlib-common'
                implementation "io.ktor:ktor-client:$ktor_version"
                implementation "io.ktor:ktor-client-json:$ktor_version"
                implementation "io.ktor:ktor-client-jackson:$ktor_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"
            }
        }
        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")
            }
        }
        iosTest {
        }
    }
}

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'
        }
    }
}

这是生成错误的通用模块的文件代码:

package com.ottamotta.mozoli.api

import com.ottamotta.mozoli.*
import io.ktor.client.HttpClient
import io.ktor.client.features.feature
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.JsonSerializer
import io.ktor.client.features.json.defaultSerializer
import io.ktor.client.request.header
import io.ktor.client.request.request
import io.ktor.client.request.url
import io.ktor.http.HttpMethod


class MozoliApiKtor(
    private val serverUrl: String,
    private var jsonSerializer: JsonSerializer? = null,
    private val tokenProvider: suspend () -> String?
) : MozoliApi {

    private val client: HttpClient

    private val AUTH_HEADER = "Authorization";
    private val TOKEN_PREFIX = "Bearer "

    init {
        client = HttpClient {
            install(JsonFeature) {
                serializer = jsonSerializer ?: defaultSerializer()
            }
        }
        jsonSerializer = client.feature(JsonFeature)?.serializer
    }

    override suspend fun getUserProfile(): User {
        return client.request {
            url("${serverUrl}/user/")
            method = HttpMethod.Get
            header(AUTH_HEADER, TOKEN_PREFIX + tokenProvider())
        }
    }

    override suspend infix fun getEventsByCity(cityId: String): List<Event> {
        return client.request {
                url("${serverUrl}/event/city/${cityId}")
                method = HttpMethod.Get
                header(AUTH_HEADER, TOKEN_PREFIX + tokenProvider())
            }

    }

}

我遇到了和你一样的问题。为 Android 应用构建并完美运行,但 iOS 模块找不到任何 Ktor、协程或 kotlinx 序列化 class.

跟随 https://github.com/adrianbukros/github-multiplatform-example,并尝试复制他们的设置,我终于通过复制他们的一些 gradle 设置来完成 packForXCode 任务:

他们的公共模块build.gradle,应该是这样的:

apply plugin: 'kotlin-multiplatform'
apply plugin: 'kotlinx-serialization'

kotlin {
    targets {
        fromPreset(presets.jvm, "android")

        fromPreset(presets.iosX64, "ios_x86_64")
        fromPreset(presets.iosArm64, "ios_arm64")
        configure([ios_x86_64, ios_arm64]) {
            compilations.main.outputKinds("FRAMEWORK")
        }
    }

    sourceSets {
        commonMain.dependencies {
            implementation "org.jetbrains.kotlin:kotlin-stdlib"
            implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutines_version"
            implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version"

            implementation "io.ktor:ktor-client-core:$ktor_version"
            implementation "io.ktor:ktor-client-json:$ktor_version"
        }

        androidMain.dependencies {
            implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
            implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"

            implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"

            implementation "io.ktor:ktor-client-core-jvm:$ktor_version"
            implementation "io.ktor:ktor-client-json-jvm:$ktor_version"
        }

        iosMain.dependencies {
            implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:$coroutines_version"
            implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version"

            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"
        }
        configure([ios_x86_64Main, ios_arm64Main]) {
            dependsOn iosMain
        }
    }
}

// workaround for https://youtrack.jetbrains.com/issue/KT-27170
configurations {
    compileClasspath
}

task packForXCode(type: Sync) {
    final File frameworkDir = new File(buildDir, "xcode-frameworks")

    final String configuration = project.findProperty("CONFIGURATION")?.toUpperCase() ?: "DEBUG"
    final String arch = project.findProperty("ARCHS") ?: "x86_64"

    dependsOn kotlin.targets."ios_${arch}".compilations.main.linkTaskName("FRAMEWORK", configuration)

    from { kotlin.targets."ios_${arch}".compilations.main.getBinary("FRAMEWORK", configuration).parentFile }
    into frameworkDir
}

tasks.build.dependsOn packForXCode

他们的 settings.gradle 文件应如下所示:

enableFeaturePreview("GRADLE_METADATA") // IMPORTANT!

include 'commoncode'
include 'app'

如果仍然无法正常工作,请查看他们所有的 *.gradlegradle.* 文件,看看您的文件有何不同。

这终于让我完成了任务,我可以在 XCode 中使用我的 Kotlin Ktor 代码。但是,现在 Android Studio 对共享 iOS 模块说 "Kotlin is not configured"。如果我弄清楚原因,我会回来报告,如果你找到了什么,请分享!

编辑 将公共模块 build.gradle 更改为如下所示,似乎一切正常!

apply plugin: 'kotlin-multiplatform'
apply plugin: 'kotlinx-serialization'

kotlin {
    targets {
        fromPreset(presets.jvm, 'android')

        // Change to `presets.iosArm64` to deploy the app to iPhone
        fromPreset(presets.iosX64, 'ios') {
            compilations.main.outputKinds('FRAMEWORK')
        }
    }

    sourceSets {
        commonMain.dependencies {
            implementation "org.jetbrains.kotlin:kotlin-stdlib"
            implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutines_version"
            implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version"

            implementation "io.ktor:ktor-client-core:$ktor_version"
            implementation "io.ktor:ktor-client-json:$ktor_version"
        }

        androidMain.dependencies {
            implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
            implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"

            implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"

            implementation "io.ktor:ktor-client-core-jvm:$ktor_version"
            implementation "io.ktor:ktor-client-json-jvm:$ktor_version"
        }

        iosMain.dependencies {
            implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:$coroutines_version"
            implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version"

            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"
        }
    }
}

// workaround for https://youtrack.jetbrains.com/issue/KT-27170
configurations {
    compileClasspath
}

task packForXCode(type: Sync) {
    final File frameworkDir = new File(buildDir, "xcode-frameworks")
    final String mode = project.findProperty("XCODE_CONFIGURATION")?.toUpperCase() ?: 'DEBUG'

    inputs.property "mode", mode
    dependsOn kotlin.targets.ios.compilations.main.linkTaskName("FRAMEWORK", mode)

    from { kotlin.targets.ios.compilations.main.getBinary("FRAMEWORK", mode).parentFile }
    into frameworkDir

    doLast {
        new File(frameworkDir, 'gradlew').with {
            text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew $@\n"
            setExecutable(true)
        }
    }
}

tasks.build.dependsOn packForXCode

截至目前,iOS 的 kotlin 多平台在 windows 上没有 运行。这可以解释为什么只有 iosmain 代码中的依赖项无法解析。