In my Kotlin Multiplatform project, I'm trying to access Kotlin types defined in kotlin-stdlib from Swift.
TL;DR: StdLib types/methods seem not to result in header definitions, I'd like a solution that doesn't involve writing lots of boilerplate code
My scenario
I have an interface defined in Kotlin ...
interface MyKotlinInterface {
fun run() : Sequence<String>
}
... and implemented this interface in Swift ...
class MySwiftClass : MyKotlinInterface {
func run() -> KotlinSequence {
// return sequenceOf("foo")
}
}
... there I'm trying to create a Sequence but there are no methods from the kotlin.sequences package available (e.g. generateSequence).
Is it actually possible to access Kotlin framework types or methods beyond what I define in my code -- if yes, how? Furthermore, how can this be achieved without writing boilerplate code?
Further details
Having a look into the generated Objective-C header file, I see definitions for my class (obviously) and basic Kotlin types. What's missing is basically everything from the standard library functionality (I care for everything Sequence-related).
My build.gradle.kts looks like:
plugins {
kotlin("multiplatform") version "1.3.0"
}
kotlin {
targets { /* ... */ }
sourceSets {
getByName("commonMain") {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib-common")
}
}
// ...
getByName("iosMain") {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib")
}
}
}
}
Having the kotlin-stdlib defined as a dependency for the iOS target, I would expect those to become actually available from Swift.
Minimal working example
https://github.com/panzerfahrer/so-mwe-kotlin-mpp-swift
Current solution approach
The only solution I came up with, is writing the desired function for the iOS target:
fun <T : kotlin.Any> generateSequence(nextFunction: () -> T?): kotlin.sequences.Sequence<T> = kotlin.sequences.generateSequence(nextFunction)
This works ok-ish but is highly unsatisfying as it requires lots of boilerplate code. Additionally, extension functions cannot be made available this way and would require more boilerplate code or even rewriting parts of the standard library.
Desired solution
I like to avoid writing boilerplate code as much as possible. What I actually only care about, is to have (in my case) Sequence fully accessible from Swift. My feeling is, it would be sufficient to make the compiler generate selected or all header definitions for the standard library functionality.
Do you really need lazy computation (aka Sequence) in your Kotlin code?
If no, I would recommend using List<T> instead (and it maps to Swift directly).
For Sequence implementation, a workaround could be to export a factory function from your Kotlin library, e.g. you may declare a function like
fun <T : kotlin.Any> generateSequence(nextFunction: () -> T?)
= kotlin.sequences.generateSequence(nextFunction)
You may select any other factory function for Sequence, that matches your use-case.
In general, there are too many functions in the Kotlin standard library. Exporting them all to Swift will create too many useless symbols in the binary and increase the compilation time.
Related
We are a company with 10+ apps all using 10+ internal SDKs, all coded back at the glorious time of the kotlin-android-extensions gradle plugin. We heavily rely on both #Parcelize and synthetic binding (all our views are coded with it). With the newer versions of Kotlin, this plugin is now deprecated in favor of kotlin-parcelize for #Parcelize and View Binding as a replacement for synthetic binding.
We tried to upgrade our Kotlin version from 1.4.10 to 1.6.0 while still using the kotlin-android-extensions plugin. We suddenly had a #Parcelize error reported here, fixed in Kotlin 1.5.0. Except that the fix is not in Kotlin itself, it is in the kotlin-parcelize plugin. And of course as they deprecated kotlin-android-extensions, the later doesn't contain the fix. So in short, in order to fix the issue we have to use kotlin-parcelize.
Knowing that kotlin-parcelize can't be used along kotlin-android-extensions (build error), and that switching all our views from synthetic to view binding will be a hell of a work that will take a tremendous amount of time, what can be done here ? We really want to keep using synthetic binding while being able to upgrade Kotlin to its latest versions.
What looks like the obvious choice is that you need to move from synthetic views as soon as possible although it might not be possible due to lack of resources.
Old-school approach
A solution to get around the Parcelable problem is to figure out the classes that are not properly parcelized and serialize them in an old-school fashion.
Serializing to JSON
I suggest you take a look at the kotlinx-serialization package it's quite handy to read from and write to JSON.
For those that don't work you can provide the following parcelable implementation which would be generic and work all around the codebase:
Ensure that your classes are annotated with #Serializable important that such annotation comes from kotlinx.serialization package and not the java one.
With this generic function you can write any #Serializable object into a parcel.
inline fun <reified T> writeToParcel(out: Parcel, data: T) {
val jsonString = Json.encodeToString(data)
out.writeInt(jsonString.length)
out.writeByteArray(jsonString.toByteArray())
}
And with this other one you should be able to deserialize such object
inline fun <reified T> readFromParcel(input: Parcel): T {
val size = input.readInt()
val bytes = ByteArray(size) { input.readByte() }
val jsonString = bytes.toString()
return Json.decodeFromString<T>(jsonString)
}
You can make them extension function from Parcel to write even less code.
This should get you around the classes that are problematic for the Parcelize extension; it's not as fast as a proper Parcelable implementation but hey, it works.
Side notes
Note that the code has written directly as an answer of this post and hence untested and some parts might need adaptation. And of course, check out the kotlinx.serialization documentation to see how to create the Json encoder and decoder (it's fairly simple)
In researching answers for my question here I found (after several days of very frustrating work) references to the kotlin billing library "billing.ktx" which a couple of Developer pages claim
contains Kotlin extensions and coroutines support that enable you to
write idiomatic Kotlin when using Google Play's billing system
but gives neither details nor links for more information. The Play billing examples, "Classy Taxi" and "TrivialDrive" have been rewritten to use Kotlin but hardly "idiomatic Kotlin", and certainly not using coroutines, nor do they use this library. They are now two years old and showing their age in this fast moving arena.
My question is specifically what does this library offer in terms of idiomatic Kotlin or even coroutine support? I am making some headway with some billing client functions (as can be seen in the referenced question) before using this library, but I can't see what difference using it makes. To be even more specific, "launchBillingFlow" looks impossible to convert, but is it?
Just links to somewhere to find information would be enough. Why is it so hard to find more than class definitions for the billing client?
I can find no documentation. Everything in this answer comes from looking through the billing-ktx aar that appears when I add a dependency on this library to my project.
This library looks pretty minimal. It provides three new "result" classes as well as four extension funtions on BillingClient to replace callback-based code with suspend funs.
package com.android.billingclient.api
public suspend fun BillingClient.acknowledgePurchase(params: AcknowledgePurchaseParams): BillingResult { /* compiled code */ }
public suspend fun BillingClient.consumePurchase(params: ConsumeParams): ConsumeResult { /* compiled code */ }
public suspend fun BillingClient.queryPurchaseHistory(skuType: String): PurchaseHistoryResult { /* compiled code */ }
public suspend fun BillingClient.querySkuDetails(params: SkuDetailsParams): SkuDetailsResult { /* compiled code */ }
With these in place, inside a coroutine you can write:
val result = billingClient.querySkuDetails(params.build())
// you can now access result.billingResult or result.skuDetailsList
Rather than something like what appears in the documentation:
billingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList ->
// Process the result.
}
I'm making an Android app using Kotlin for the first time using MVP pattern. My questions is, why do I need interfaces for View and Presenter as Kotlin provides higher order functions? Can't we just communicate using those higher order functions? Is the use of pattern without interfaces bad?
I have looked and read lots of article and tutorials but non answered my question. Is what I am doing in the code below a wrong practice? Can someone explain it to me?
In my Activity
override fun init() {
btn_login.setOnClickListener {
LoginPresenter.userLogin(et_emailAddress.text.toString(),et_password.text.toString()){
if (it){
//do something
}else{
//do something
}
}
}
}
My Presenter
object LoginPresenter {
fun userLogin(emailId: String, password: String, completion: (Boolean) -> Unit) {
//do something
completion(true)
}
}
Higher-order function costs
Kotlin official documentation on the cost of higher order functions
Using higher-order functions imposes certain runtime penalties: each
function is an object, and it captures a closure, i.e. those variables
that are accessed in the body of the function. Memory allocations
(both for function objects and classes) and virtual calls introduce
runtime overhead.
and if you're replacing all your interfaces with higher-order functions, you may end up with a bad performance.
2.
Interfaces can hold multiple functions, for which you'll need individual function params when using higher-order functions.
Consider the following case,
interface UserLoginInterface {
fun onLoginSuccess(loggedInUser: User)
fun onLoginFailure(error: ErrorResponse)
fun onRedirect(someOtherObjectWithDirectives: SomeDataClass)
}
To translate this to higher-order functions usage, You'll have to use three Function params
why do I need interfaces for View and Presenter as Kotlin provides higher order functions?
This is rather a common practice in software development. And while you may not use interfaces, there is a number of key points why interfaces are preferable. Off the top of my head:
with interface you can have multiple implementations of it without actually caring about the concrete type of the implementation. This is what you're missing with the higher order functions - you're restricted with the only type, LoginPresenter, when using the LoginPresenter.userLogin() method.
most of the design patterns is based on the separation of interfaces from their implementations. So programming into implementation rather than abstraction won't let you make use of those.
you won't be able to properly unit test classes that depend on other implementations as no mocking is possible in this case.
code maintenance and extension becomes much harder with concrete implementation.
As part of working on the development of a new API, I am learning to use Kotlin. Initially I want the Kotlin API to be used within a Java (Android) project, but in the long term I hope to adopt Kotlin entirely.
As part of improving the implementation of a long-running process, I want to use coroutines. Specifically, a channel producer from the kotlinx.coroutines package.
For example:
fun exampleProducer() = produce {
send("Hello")
delay(1000)
send("World")
}
What is the best way to consume this in Java? I am okay with adding temporary 'helper' functions to Kotlin and/or Java.
The easiest way to interop channels with Java is via Reactive Streams. Both Rx and Project Reactor are supported out-of-the-box. For example, add kotlinx-coroutines-rx2 to your dependicies and you'll be able to use rxFlowable builder:
fun exampleFlowable() = rxFlowable<String> {
send("Hello")
delay(1000)
send("World")
}
This function returns an instance of Flowable, which is specifically designed for ease-of-use from Java, for example, you can do in Java:
exampleFlowable().subscribe(t -> System.out.print(t));
Currently, assuming Java 8 is used and lambdas are available, I rely on a helper function defined in Kotlin which allows passing a callback to consume incoming results.
The helper method in Kotlin:
fun exampleProducerCallback( callback: (String) -> Unit ) = runBlocking {
exampleProducer().consumeEach { callback( it ) }
}
This is then consumed in Java as:
ApiKt.exampleProducerCallback( text -> {
System.out.print( text );
return Unit.INSTANCE; // Needed since there is no void in Kotlin.
} );
Explanation on why return Unit.INSTANCE is needed can be found in this answer.
It´s a good idea to use kotlin extensions all over the code?
I miss a lot the extensions from iOS, but this is a good way to use those kind of things in android?
Refering to http://antonioleiva.com/kotlin-android-extension-functions/
Is there a better solution for this?
To expand a little bit more on Andrey Breslav's answer a bit, Kotlin extension functions do compile down to static java methods, so most general purpose extension functions carry no overhead. But there is one edge case you need to look out for that Jake Wharton calls out in the first few min of this talk at Google IO.
That is when you pass in higher order functions (lambdas), as a parameter to the extension function like so:
fun View.doSomething(block: () -> Unit) {
//do something
}
This code would take a performance hit because lambda's under the hood have to create an anonymous class under the hood which can eat up methods and cause class loading. This is a very simple fix by adding the inline keyword to the function which will essentially inline your code into all of this call sites functions so you will not take a performance hit each time the extension function is called.
inline fun View.doSomething(block: () -> Unit) {
//do something
}
Extension functions in Kotlin are compiled to normal Java methods. For example, when you define a function in your package it turns into a static method in a Java class. There's no overhead compared to simply calling a static utility