I want to declare a function which returns nothing.
So I declared an interface in Kotlin side which includes the following function signature:
var errorCallback: (errMessage: String) -> Unit
when generating iOS framework, it will be converted to:
NativeLibsStdlibUnit*(^errorCallback)(NSString*);
@interface NativeLibsStdlibUnit : KotlinBase
+ (instancetype)unit NS_SWIFT_NAME(init());
@end;
Shouldn't it be Void in Swift?
I expected its counterpart in swift to be:
var successCallback: (_ result: String) -> Void
Is it a bug or I did something wrong?
This behaviour is intentional and related to some details of Kotlin generics such as covariance and type erasure.
It doesn't seem to impose any limitations on usage, does it?
When Unit is used as a return type of function (not a function type), it is translated to Swift Void (and Objective-C void). E.g.
fun foo(): Unit
is translated to
-(void)foo;
So the behaviour described above applies only to function types (i.e. types of lambdas).
@SvyatoslavScherbina Hi, thanks for the answer! I just got a little bit confused:
When Unit is used as a return type of function (not a function type), it is translated to Swift Void (and Objective-C void).
So, how to declare a lambda which returns nothing in kotlin native, and what's the counterpart in swift looks like?
If you need to implement your errorCallback in Swift, then you can assign a Swift lambda to it, and return NativeLibsStdlibUnit() from the lambda.
Which means I indeed need to follow the generated ObjC code which has the following signature:
var successCallback: (_ result: String) -> NativeLibsStdlibUnit()
A little bit weird but thanks!
Hi :)
I just had the same issue. The following Kotlin function:
fun benchmark(callback: (String) -> Unit)
In Swift looks like:
func benchmark(callback: @escaping (String) -> KotlinUnit)
When the right thing would be:
func benchmark(callback: @escaping (String) -> Void)
Shouldn't this issue stay open?
Thanks!
Same behavior as Java. Seems like it's working as intended.
@JakeWharton For swift, the proper behavior would be to substitute Unit for Void, as it is already happening with functions and explained in https://kotlinlang.org/docs/reference/native/objc_interop.html
The same is true for Java and yet as already stated there are reasons why that is not the case.
Then seems to me that in both platforms the right thing to do is to substitute Unit for Void. As I understand, this is not happening due to implementation details related with "Kotlin generics such as covariance and type erasure".
That is why I am wondering if the issue should be reopened, so could be that a developer wants to look into it and fix it.
() -> Unit is subtype of () -> Any in Kotlin, so Swift counterparts should follow the same rule.
And () -> KotlinUnit is subtype of () -> AnyObject in Swift, while () -> Void is not.
This consideration is one of the reasons for mapping () -> Unit to () -> KotlinUnit.
And why is Unit converted to Void in functions then? (According to https://kotlinlang.org/docs/reference/native/objc_interop.html)
Because this case is much more important, and consequences of translating Unit to Void are not that serious here.
Tradeoffs are everywhere.
Improved with #2968. Will likely be shipped within 1.3.40.
Most helpful comment
Improved with #2968. Will likely be shipped within 1.3.40.