Skip to content
This repository has been archived by the owner on Aug 10, 2021. It is now read-only.

Add very basic documentation for interop with Swift/Objective-C #1482

Merged
merged 4 commits into from
Apr 18, 2018

Conversation

SvyatoslavScherbina
Copy link
Collaborator

No description provided.

@SvyatoslavScherbina
Copy link
Collaborator Author

SvyatoslavScherbina commented Apr 10, 2018

To be merged after #1481, #1490 and #1512

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).
Copy link
Contributor

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.

Copy link
Collaborator Author

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".

Copy link
Contributor

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`.
Copy link
Contributor

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"?

Copy link
Collaborator Author

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?

Copy link
Contributor

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 |
Copy link
Contributor

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
Copy link
Contributor

@olonho olonho Apr 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit conversion paragraph?

Copy link
Collaborator Author

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?

Copy link
Contributor

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.
Copy link
Contributor

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.
Copy link
Contributor

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.
Copy link
Contributor

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.

Copy link
Collaborator Author

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`.
Copy link
Contributor

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?

Copy link
Collaborator Author

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.

Copy link
Contributor

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.
Copy link
Contributor

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.
Copy link
Collaborator Author

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?

Copy link
Contributor

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 | |
Copy link

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 to init(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 that initBy 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 actual initWith... 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.

Copy link
Collaborator Author

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.

Copy link

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: for UIViewController).

Copy link
Collaborator Author

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 reference UIViewController(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.

Copy link
Contributor

@olonho olonho Apr 17, 2018

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.

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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()
Copy link
Contributor

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.
Copy link
Contributor

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"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense.

@SvyatoslavScherbina SvyatoslavScherbina merged commit f27d445 into master Apr 18, 2018
@SvyatoslavScherbina SvyatoslavScherbina deleted the objc-interop-docs branch April 18, 2018 17:46
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants