https://kotlinlang.org logo
Title
s

Sam

03/29/2018, 4:54 AM
I have a model class called
Account
defined in Swift as a subclass of NSObject. I'm trying to pass it into a method of a class in a framework. However to pass that object to some of the foundation classes it needs to be of type NSObject or the compiler will complain. Any cast I've tried has failed at runtime with an exception for me. If I take out the if check it will give a class cast exception
override fun put(obj: T): UUID {
        if (obj !is ObjCObject) { //Tried NSObject, NSObjectProtocol
            throw IllegalArgumentException("Object: $obj must be a subclass of NSObject.")
        }
        val objc = obj as ObjCObject
        val data = NSKeyedArchiver.archivedDataWithRootObject(objc) // Won't take obj so need to cast.
        ...
    }

    //Swift class
    class Account: NSObject {
    let name: String

    init(name: String) {
        self.name = name
    }
}
s

svyatoslav.scherbina

03/29/2018, 7:40 AM
This case is not supported yet. Objects, passed by Swift code to Kotlin through generated framework API, can’t be passed then by Kotlin code to imported Objective-C API.
s

Sam

03/29/2018, 11:15 AM
My Swift object is inheriting from NSObject and should be behaving like an objective c object. I’m able to use this same object in other places. https://github.com/samus/pistachio/blob/master/native-example-ios/Pistachio-iOS%20Example/ViewController.swift
o

olonho

03/29/2018, 11:47 AM
As Svyatoslav (author of our Objective-C interop) mentioned, this use case, when you pass objects from Swift to Kotlin and then try to use it in Objective-C is not yet supported, however we plan to improve that.
s

Sam

03/29/2018, 1:44 PM
Is it possible to pass an objc object to Kotlin that then will pass that object to foundation classes?
s

svyatoslav.scherbina

03/29/2018, 2:37 PM
Yes, but in a bit hacky way. You can declare Kotlin method as taking
Long
instead of object. This
Long
value would represent raw pointer to object. Then wrap this value manually before passing to foundation classes. Kotlin:
import kotlinx.cinterop.*
...

override fun put(objPtr: Long) {
    val obj = interpretObjCPointer<ObjCObject>(nativeNullPtr + objPtr)
    val data = NSKeyedArchiver.archivedDataWithRootObject(obj)
}
Swift:
put(unsafeBitCast(obj, to: Int64.self))
(where
obj
is of Objective-C type)
s

Sam

03/29/2018, 3:04 PM
Cool. Thanks for the info.
I'm trying to puzzle out what is happening under the hood when an objc object is passed to a k/n method. An objc object passed into kotlin is wrapped in a wrapper and normal method calls defined in the objc object can be invoked on the object. The objc object can't currently be passed to a foundation class like NSKeyArchiver because it is actually a wrapped object. Is this close? I've been looking through the code trying to locate whatever it is that is representing the objc object inside of the k/n runtime to see if I can hack together something a little more user friendly than passing in an object pointer.
s

svyatoslav.scherbina

03/30/2018, 7:21 AM
It is close enough. Currently all Objective-C objects are represented on runtime as Kotlin objects wrapping raw pointers to Objective-C objects. The problem is that currently there two different kinds of wrappers: 1) For objects that get into Kotlin by calling generated framework API from Objective-C/Swift code. 2) For objects that get into Kotlin by calling
platform.*.*
APIs from Kotlin code. (the same for passing objects back to Objective-C world). That’s why Kotlin objects formed by 1) can’t be passed directly to 2). We are going to improve the situation soon.
We have added some preliminary support for the case discussed above. It is available starting from 0.7-dev-1493 Only some early steps are done, so this support can be a bit buggy.
s

Sam

04/04/2018, 11:30 AM
Thank you for the heads up. I’ll see if I can make some time to try it out today.