-
Notifications
You must be signed in to change notification settings - Fork 564
Add very basic documentation for interop with Swift/Objective-C #1482
Conversation
OBJC_INTEROP.md
Outdated
|
||
Kotlin/Native provides bidirectional interoperability with Objective-C. | ||
Objective-C frameworks and libraries can be used in Kotlin code if | ||
properly attached to the build (system frameworks are attached by default). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"attached to" -> "integrated with", attachment assumes somewhat asymmetrical relation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why this relation should be described as being symmetrical?
I mean, when importing a library to Kotlin code through cinterop
, we don't import Kotlin code to the library.
We may use replace "attach" by "include" or "import".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Imported or used is fine with me.
OBJC_INTEROP.md
Outdated
See e.g. "Interop libraries" in | ||
[Gradle plugin documentation](GRADLE_PLUGIN.md#building-artifacts). | ||
Swift library can be used in Kotlin code if its API is exported to Objective-C | ||
with `@objc`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe explicitly mention "Pure Swift modules are not yet supported"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of the last sentence or after it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After
|
||
The table below shows how Kotlin concepts are mapped to Swift/Objective-C and vice versa. | ||
|
||
| Kotlin | Swift | Objective-C | Notes | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+100!
OBJC_INTEROP.md
Outdated
Names of Kotlin classes and interfaces are prefixed when imported to Swift/Objective-C. | ||
The prefix is derived from the framework name. | ||
|
||
### Top-level functions and properties |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unit
conversion paragraph?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it makes sense. What do you think, should it be related to function types mapping or to Unit
type mapping?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe function types.
OBJC_INTEROP.md
Outdated
|
||
### Top-level functions and properties | ||
|
||
Top-level Kotlin functions and properties are accessible as members of a special class. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example?
OBJC_INTEROP.md
Outdated
### Number | ||
|
||
`NSNumber` is not automatically translated to Kotlin primitive types | ||
when used as Swift/Objective-C parameter type or return value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe some rationale, or not yet implemented motivation?
OBJC_INTEROP.md
Outdated
|
||
### Mutable collections | ||
|
||
Every Kotlin `MutableSet` is `NSMutableSet`, however the opposite is not true. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please mention explicitly that all other collections, such as Map and List are properly mapped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually while writing these paragraphs I was considering them to be read just as notes to the table, after following the corresponding links.
Now I see that they look very silly when being read as self-contained descriptions. So I suppose some of the paragraphs should be expanded.
OBJC_INTEROP.md
Outdated
Kotlin classes and interfaces can be subclassed by Swift/Objective-C classes | ||
and protocols. | ||
Currently a class that adopts Kotlin protocol should inherit `NSObject` | ||
(either directly or indirectly). Note that all Kotlin classes do inherit `NSObject`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All Kotlin classes are visible to Swift/Objective-C as inheriting NSObject
, so it is OK to adopt Kotlin protocol by a subclass of a Kotlin class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably say that somehow more clear.
OBJC_INTEROP.md
Outdated
Currently a class that adopts Kotlin protocol should inherit `NSObject` | ||
(either directly or indirectly). Note that all Kotlin classes do inherit `NSObject`. | ||
|
||
Swift/Objective-C classes can be subclassed with Kotlin `final` class. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please mention, that non-final inheritance and complex object hierarchies are not yet supported.
# _Kotlin/Native_ interoperability with Swift/Objective-C | ||
|
||
This documents covers some details of Kotlin/Native interoperability with | ||
Swift/Objective-C. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we use some common name for Swift and Objective-C?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, although I've no idea what could it be.
OBJC_INTEROP.md
Outdated
| ------ | ----- |------------ | ----- | | ||
| `class` | `class` | `@interface` | [note](#name-translation) | | ||
| `interface` | `protocol` | `@protocol` | | | ||
| `constructor` | Initializer | Initializer | | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if constructors map to initializers in a natural way for Kotlin developers, so there probably should be some note on initBy(...)
and invocation ordering.
Background:
I'm playing around with Kotlin/Native based iOS app and finding initializers a bit confusing (having no background in Obj-C / Swift).
I'm trying out code-only approach, so I've removed storyboards and invoking this in AppDelegate
:
val mainViewController = ViewController()
println("mainViewController ready [${mainViewController.hashCode()}]")
In my ViewController
I've added logging to constructors and initWith...
methods as following:
@ExportObjCClass
class ViewController: UIViewController {
init {
println("ViewController [${hashCode()}] ctor: common init")
}
constructor(): super() {
println("ViewController [${hashCode()}] ctor: no-arg")
}
constructor(aDecoder: NSCoder): super(aDecoder) {
println("ViewController [${hashCode()}] ctor: NSCoder")
}
constructor(nibNameOrNil: String?, bundle: NSBundle?): super(nibNameOrNil, bundle) {
println("ViewController [${hashCode()}] ctor: nibNameOrNil")
}
override fun init(): UIViewController? {
println("ViewController [${hashCode()}]: init")
return initBy(ViewController())
}
override fun initWithCoder(aDecoder: NSCoder): UIViewController? {
println("ViewController [${hashCode()}]: initWithCoder")
return initBy(ViewController(aDecoder))
}
override fun initWithNibName(nibNameOrNil: String?, bundle: NSBundle?): UIViewController {
println("ViewController [${hashCode()}]: initWithNibName")
return initBy(ViewController(nibNameOrNil, bundle))
}
// ...
}
Here's the resulting output:
ViewController [1807837959]: initWithNibName
ViewController [1807837959] ctor: common init
ViewController [1807837959] ctor: nibNameOrNil
ViewController [1807837959] ctor: common init
ViewController [1807837959] ctor: no-arg
mainViewController ready [1807837959]
So there are several points I'm finding odd, which ask for being covered by interop documentation:
- No-arg constructor leads to
initWithNibName
. This is probably due toinit(nibName:bundle:)
being designated initializer. initWithNibName
is being called before any constructor code. Looks like that's caused by the nature of initializers in Objective-C, so they are treated a bit differently from other methods. I see thatinitBy
is intrinsic and has a special handling when invoked, but it's not documented in any way, so a note here might be helpful. And actualinitWith...
methods appear as usual methods in generated.kt
stubs, so nothing reveals their real nature.- Two different constructors being called on one object, with two calls to
init { ... }
block. I guess Objective-C base class is able to invoke another constructor of derived class, and I'm not sure if such behavior can be achieved with Kotlin/JVM, so this also is a surprise point.
Please let me know if I'm doing something wrong or if something should be filled as an issue -- I'll try to provide as much details as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@r4zzz4k overriding of initializers is tricky and considered to be redesigned.
The problem with your example is that you call super
-constructor that is based on non-designated initializer. The compiler should prohibit this, however it doesn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, so this is temporary state of thins. And so even if I want to use no-arg constructor without working with nibs it should still invoke super(null, null)
to reference UIViewController(nibNameOrNil: String?, bundle: NSBundle?)
. Is that correct? If so, something like this should be great to have for newcomers like me not to fail this trap:
Note: When implementing constructors of Kotlin classes which are extending Objective-C ones make sure you are calling superclass constructor corresponding to it's designated initializer (i.e.
initWithNibName:bundle:
forUIViewController
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if I want to use no-arg constructor without working with nibs it should still invoke
super(null, null)
to referenceUIViewController(nibNameOrNil: String?, bundle: NSBundle?)
.
Yes. Swift also has this requirement.
We will either add such a warning to this doc or forbid this case in the compiler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe some high level description of initBy
and current state of things around initializers makes sense indeed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@olonho btw, I'm working on some improvements to Objective-C initializers support, which would make initBy
(and also calling/overriding init*
methods) obsolete. So I suggest waiting until this work is done and then completing the documentation about initializers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And here it is: #1512
``` | ||
foo { | ||
bar($0 as! Int32) | ||
return KotlinUnit() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not too pretty, but we could live with it.
OBJC_INTEROP.md
Outdated
and protocols. | ||
Currently a class that adopts Kotlin protocol should inherit `NSObject` | ||
(either directly or indirectly). Note that all Kotlin classes do inherit `NSObject`, | ||
so a subclass of Kotlin class can adopt Kotlin protocol. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not entirely clear to me. Maybe "subclass of Kotlin class" -> "Objective-C subclass of Kotlin class"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense.
No description provided.