Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Swift to Kotlin cases #42

Open
rickclephas opened this issue Feb 15, 2022 · 2 comments · May be fixed by #152
Open

Add support for Swift to Kotlin cases #42

rickclephas opened this issue Feb 15, 2022 · 2 comments · May be fixed by #152
Assignees
Milestone

Comments

@rickclephas
Copy link
Owner

Currently the whole library is focused on Kotlin to Swift cases but there are also valid cases for Swift to Kotlin support.

For example you should be able to call the following function from Swift:

suspend fun doSomething(something: suspend (String) -> String): String = TODO()

https://kotlinlang.slack.com/archives/C3PQML5NU/p1644904527044239

@rickclephas
Copy link
Owner Author

Some thoughts about Swift implementations of Kotlin classes/interfaces.

Kotlin code:

interface A {
    suspend fun foo(): String
    suspend fun bar(): String {
        return "Default"
    }
}

open class AKotlinImpl: A {
    suspend fun foo(): String {
        return "Kotlin"
    }
}

class AKotlinImplNoDefaults: AKotlinImpl {
    override suspend fun bar(): String {
        return "Kotlin"
    }
}

suspend fun main() {
    AKotlinImpl().run {
        println(foo()) // Kotlin
        println(bar()) // Default
    }
    AKotlinImplNoDefaults().run {
        println(foo()) // Kotlin
        println(bar()) // Kotlin
    }
}

Generated Kotlin code:

abstract class ANativeImpl: A {
    final override suspend fun foo(): String { /* ... */ }
    abstract fun fooNativeImpl(): NativeSuspend<String>

    final override suspend fun bar(): String { /* ... */ }
    open fun barNativeImpl(): NativeSuspend<String> { /* ... */ }
}

fun A.fooNative(): NativeSuspend<String> { /* ... */ }
fun A.barNative(): NativeSuspend<String> { /* ... */ }

Swift code:

class ASwiftImpl: ANativeImpl {
    override func fooNativeImpl() -> NativeSuspend<String> {
        return nativeSuspend {
            return "Swift"
        }
    }
}

class ASwiftImplNoDefaults: ASwiftImpl {
    override func barNativeImpl() -> NativeSuspend<String> {
        return nativeSuspend {
            return "Swift"
        }
    }
}

func main() async {
    let impl = ASwiftImpl()
    print(await asyncFunction(for: impl.fooNative())) // Swift
    print(await asyncFunction(for: impl.barNative())) // Default
    let implNoDefaults = ASwiftImplNoDefaults()
    print(await asyncFunction(for: implNoDefaults.fooNative())) // Swift
    print(await asyncFunction(for: implNoDefaults.barNative())) // Swift
}

Note: this won't work because A is an interface and doesn't expose the extension functions like this.
Support for interfaces is limited anyway because ObjC doesn't support default implementations.

Note: this way calling the Swift implementations from Swift will convert from async to NativeSuspend and back to async.
To fix that we would need an extension/wrapper function in Swift that can directly call the async version instead.
That would need some kind of Swift codegen (during Kotlin compile) to make this easy to use.

@chris-hatton
Copy link

Just dropping a note to say I too arrived here looking for a library to help with consuming a Swift ASyncStream from Kotlin/Native.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants