Kotlin-native: Kotlin Generics to Swift Generics.

Created on 29 Nov 2018  路  17Comments  路  Source: JetBrains/kotlin-native

Is it possible to convert Kotlin generics to Swift Generics ?
At this time, it is converted to Any? (a.k.a Optional\

Most helpful comment

@olonho
I am currently evaluating whether or not we could use this project to build Android and iOS applications for our customers. The fact that we are basically unable to use generics in shared APIs or generics when calling into platform APIs (eg. UIViewController.subViews becoming a List<*> instead of List<UIView>) is a huge problem for us. I am sure this is a really, really hard problem and most likely there are no plans for this yet, but we really, really need this to build a solid framework for our applications (without a lot of boilerplate and type wrapping)

All 17 comments

K/N does not interop with Swift directly yet, only via Objective-C, thus this feature request is not something we could help with at the moment.

yet and at the moment means it will be supported in the future?

It means I describe the current situation and cannot make any promises here.

I would also love to have this functionality. This would open a lot of cool possibilities for our team.

@olonho
I am currently evaluating whether or not we could use this project to build Android and iOS applications for our customers. The fact that we are basically unable to use generics in shared APIs or generics when calling into platform APIs (eg. UIViewController.subViews becoming a List<*> instead of List<UIView>) is a huge problem for us. I am sure this is a really, really hard problem and most likely there are no plans for this yet, but we really, really need this to build a solid framework for our applications (without a lot of boilerplate and type wrapping)

I must agree that generics visibility is an important part of interoperability. I believe that it is necessary if making common parts in Kotlin for Android and iOS is to become popular in mainstream programming.

Draft pull request generating generics in objc interop. Please comment if interested: https://github.com/JetBrains/kotlin-native/pull/2850

amazing

We need generics.

Kotlin Generics can be converted to ObjC/Swift Generics as of Kotlin 1.3.40
See https://blog.jetbrains.com/kotlin/2019/06/kotlin-1-3-40-released/

Yeah!

Kotlin Generics can be converted to ObjC/Swift Generics as of Kotlin 1.3.40
See https://blog.jetbrains.com/kotlin/2019/06/kotlin-1-3-40-released/

I haven't been able to make this work yet :( Currently I have this:

kotlin {
targets{
        final def iOSTarget = System.getenv('SDK_NAME')?.startsWith("iphoneos") \
                              ? presets.iosArm64 : presets.iosX64

        fromPreset(iOSTarget, 'iOS') {
            binaries {
                framework('common') {
                    freeCompilerArgs.add("-Xobjc-generics")

But I still see Any? on my generic calls on the Xcode project, trying with iosX64 returns me the Cannot create binary debugFramework: binary with such a name already exists error, any tips, or any example on how to implement this correctly?

@desgraci this snippet is not enough to find the cause. Please post the entire Gradle script.

@SvyatoslavScherbina sure no problem:

//this is what makes this module a cool kotlin/native
apply plugin: 'kotlin-multiplatform'
//this is for using sqldelight
apply plugin: 'com.squareup.sqldelight'
//this is for sync with xcode
apply plugin: 'co.touchlab.kotlinxcodesync'
//this to work with pods
//https://github.com/JetBrains/kotlin-native/blob/master/COCOAPODS.md
apply plugin: 'org.jetbrains.kotlin.native.cocoapods'
version = "1.0"

apply plugin: 'kotlinx-serialization'

//region WIP
//WIP please consider using a delegate!
apply plugin: 'com.android.library'

android {

    compileSdkVersion AndroidConfig.compileSdkVersion

    defaultConfig {
        minSdkVersion AndroidConfig.minSdkVersion
        targetSdkVersion AndroidConfig.targetSdkVersion
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        //This is for MultiplatformSettings
        debug {
            // MPP libraries don't currently get this resolution automatically
            matchingFallbacks = ['release']
        }
    }

    packagingOptions {
        exclude 'META-INF/*.kotlin_module'
    }

}
//endregion

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

sqldelight {
    GymTrack {
        packageName = "com.vonderful.gymtrack"
        sourceFolders = ["sqldelight"]
        //not needed now, but meh
        schemaOutputDirectory = file("src/main/sqldelight/migrations")
    }
}


kotlin {
    targets {
        targetFromPreset(presets.android, 'android')

        final def iOSTarget = System.getenv('SDK_NAME')?.startsWith("iphoneos") \
                              ? presets.iosArm64 : presets.iosX64

        fromPreset(iOSTarget, 'iOS'){
            compilations.main{
                binaries {
                    framework("common") {
                        freeCompilerArgs.add("-Xobjc-generics")
                    }
                }
            }
        }

    }

    sourceSets {

        commonMain.dependencies {

            implementation kotlin('stdlib-common')

            //region Kotlin
            implementation "org.jetbrains.kotlin:kotlin-stdlib-common:${Versions.kotlin}"
            //endregion

            //region Coroutines
            implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:${Versions.kotlinCoroutines}"
            //endregion

            //region SQL Delight
            implementation "com.squareup.sqldelight:runtime:${Versions.sqlDelight}"
//            implementation "com.squareup.sqldelight:coroutines-extensions:${Versions.sqlDelight}"
            //endregion

            //region di
            implementation "org.kodein.di:kodein-di-erased:${Versions.kodein}"
            //endregion

            //https://github.com/Kotlin/kotlinx.serialization
            implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.13.0"

            implementation "io.ktor:ktor-client-core:${Versions.ktor}"
            implementation "io.ktor:ktor-client-json:${Versions.ktor}"
            implementation "io.ktor:ktor-client-serialization:${Versions.ktor}"
        }

        commonTest.dependencies {
            implementation 'org.jetbrains.kotlin:kotlin-test-common'
            implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common'
        }

        //If you want to don't share the libs, don't use this and do your own imports on the dependencies...
        configure([androidMain]) {
            dependsOn commonMain
        }

        androidMain.dependencies {

            //region Coroutines
            implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.kotlinCoroutines}"
            //implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.kotlinCoroutines}"
            //endregion

            //region  SQL Delight
            implementation "com.squareup.sqldelight:android-driver:${Versions.sqlDelight}"
            //implementation "com.squareup.sqldelight:coroutines-extensions-jvm:${Versions.sqlDelight}"
            //endregion

            // The base will compile ok, but when ran, it will fail with an unresolveded reference
            // so we add this
            implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:${Versions.serializationRuntime}"
            //same we ktor, we are just sending those scary messages away!
            implementation "io.ktor:ktor-client-android:${Versions.ktor}"
            implementation "io.ktor:ktor-client-json-jvm:${Versions.ktor}"
            implementation "io.ktor:ktor-client-serialization-jvm:${Versions.ktor}"
        }

        androidTest.dependencies{
            implementation 'org.jetbrains.kotlin:kotlin-test'
            implementation 'org.jetbrains.kotlin:kotlin-test-junit'
            implementation "com.squareup.sqldelight:sqlite-driver:${Versions.sqlDelight}"
        }

        configure([iOSMain]) {
            dependsOn commonMain
        }

        iOSMain.dependencies {

            //region Coroutines
            implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:${Versions.kotlinCoroutines}"
            //endregion

            //region  SQL Delight
            implementation "com.squareup.sqldelight:ios-driver:${Versions.sqlDelight}"
            //endregion

            //same as in Android
            implementation "io.ktor:ktor-client-ios:${Versions.ktor}"
            implementation "io.ktor:ktor-client-json-native:${Versions.ktor}"
            implementation"org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:${Versions.serializationRuntime}"
            implementation "io.ktor:ktor-client-serialization-native:${Versions.ktor}"
        }

    }

//    //run the podscpec if you don't see the podspec :p !
    cocoapods {
        // Configure fields required by CocoaPods.
        summary = "Library for GymTrack app"
        homepage = "https://po.ta.to"
    }
}

xcode {
    projectPath = "../ios/Gym Track/Gym Track.xcodeproj"
    target = "Gym Track"
}

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

task copyFramework {
    def buildType = project.findProperty('kotlin.build.type') ?: 'DEBUG'
    def target = project.findProperty('kotlin.target') ?: 'iOS'
    //looking for commonDebugFramework
    dependsOn kotlin.targets."$target".binaries.getFramework("common", 'DEBUG').linkTask

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

I've tried different combinations so far, but no luck, if there is anything else that I can bring to the table to help ouy let me know, btw this is my test generic class:

data class GenericTestData<T : Any>(val t: T)

but I still get the @interface SharecodeGenericTestData without the <T> at Swift

CocoaPods plugin creates its own framework internally.
Your snippet

framework("common") {
    freeCompilerArgs.add("-Xobjc-generics")
}

creates the second one. It should configure already created framework instead. Please refer to the documentation (see "Accessing binaries").

Indeed, wasn't overwriting in the correct way the cocoapod's framework, worked like a charm, a good case of RTFabolousM, thank you for guiding me on this one @SvyatoslavScherbina

Hi, maybe you can help me. I followed this tutorial: https://medium.com/@fandygotama/kotlin-multiplatform-reactive-a45263e1fd7a and got tuck right in the beginning. The sync fails:

Build file '/myPath/kmmmovies/core/build.gradle' line: 16

A problem occurred evaluating project ':core'.
> Operation is not supported for read-only collection

line 16 is the freeCompilerArgs.add("-Xobjc-generics"):
fromPreset(iOSTarget, 'iOS') { binaries { framework("core") { freeCompilerArgs.add("-Xobjc-generics") } }
Can anyone tells me what am I doing wrong?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

AregevDev picture AregevDev  路  3Comments

msink picture msink  路  4Comments

nvlizlo picture nvlizlo  路  4Comments

barsan-md picture barsan-md  路  4Comments

benasher44 picture benasher44  路  4Comments