Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kotlin/Native tutorial. Writing a framework and dynamic library #1091

Merged
merged 20 commits into from
Aug 29, 2018

Conversation

jonnyzzz
Copy link
Member

@jonnyzzz jonnyzzz commented Aug 8, 2018

https://youtrack.jetbrains.com/issue/EVAN-5132

Ready for the factual check. English will be improved later

@jonnyzzz jonnyzzz self-assigned this Aug 8, 2018
@jonnyzzz jonnyzzz requested a review from olonho August 8, 2018 08:38
## Generated Framework Headers

Each of created frameworks contains the header file in `<Framework>/Headers/Demo.h`.
The file does not depend on the target platform (at least with Kotlin/Native v.0.8.1).
Copy link

Choose a reason for hiding this comment

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

Not sure if it is correct wrt some primitive type mappings.

Copy link
Contributor

Choose a reason for hiding this comment

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

It should definitely be correct for the code above. But it is not correct when Kotlin code has e.g. NSInteger in public signatures.

@property (readonly) NSString *field;
@end;

__attribute__((objc_subclassing_restricted))
Copy link

Choose a reason for hiding this comment

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

Exact generated bindings content may change without a warning, and we shalln't put too much emphasis on how what is inside, but on hw to use it.

</div>

The Kotlin code is turned into nearly the similar looking
code in Swift. There are some small differences, so far. We create the Kotlin `object DemoObject`
Copy link

Choose a reason for hiding this comment

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

Maybe we shall cover passing nullable primitives as well (see https://kotlinlang.slack.com/archives/C3SGXARS6/p1533824954000609).

Copy link
Member Author

Choose a reason for hiding this comment

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

added nullable types and lambdas


It may turn out tricky to configure the Objective-C or Swift project in XCode or
JetBrains AppCode. You should include the Framework into the *General* tab of Target settings
of XCode project. Also, you may need to fix the `@rpath` to include our Framework folder in the
Copy link

Choose a reason for hiding this comment

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

Better document how "to fix" rpath.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

Copy link
Member Author

Choose a reason for hiding this comment

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

done. added iOS explanation as well

where you run the compiler:
- macOS: `demo_api.h` and `libdemo.dylib`
- Linux: `demo_api.h` and `libdemo.so`
- Windows: `demo_api.h` and `demo.dll`
Copy link

Choose a reason for hiding this comment

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

On Windows .def file is produced as well. Seems due to small bug it was produced inside Windows temporary files, but it will be fixed with JetBrains/kotlin-native#1859.

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed in the dynamic libraries branch and #1084

You may want to check [the thread](https://stackoverflow.com/questions/1675351/typedef-struct-vs-struct-definitions)
for an explanation of that pattern.

We see from that definitions that Kotlin object `DemoObject` is mapped into
Copy link

Choose a reason for hiding this comment

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

As with Objective-C manual not sure if diving into details of generated C headers is a good idea.

Now we call the following commands to compile the code into Frameworks
for macOS, iOS and iOS emulator respectively:
```bash
kotlinc lib.kt -produce dynamic -output macOS/Demo
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not produce framework for macOS too?

Copy link
Member Author

Choose a reason for hiding this comment

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

it is on the list of 3 commands, what do I miss?

Copy link
Contributor

Choose a reason for hiding this comment

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

Your first command produces dynamic library (macOS/Demo.dylib), while other commands produce frameworks (iOS*/Demo.framework). Is it OK?

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed. It was a typoe


[Demo forIntegersB:1 s:1 i:3 l:4];

NSString* ret = [Demo stringsStr:(@"That is string")];
Copy link
Contributor

Choose a reason for hiding this comment

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

(@"That is string")

Are brackets required here?

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

function `demoObject`, which allows us to get the only instance of the object and to call
`DemoObject` methods on it.
The standard pattern is used to create an instance of the `DemoClazz` class. We call
the `[[ DemoDemoClazz alloc] init]` on 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.

Another standard pattern can be used here: [DemoDemoClazz new]. It is available for calling constructor without parameters.

Copy link
Member Author

Choose a reason for hiding this comment

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

thanks!

The Kotlin code is turned into nearly the similar looking
code in Swift. There are some small differences, so far. We create the Kotlin `object DemoObject`
as a class in Swift to access it's methods. We may see from the Objective-C attributes
in `<Framework>/Headers/Demo.h` that it is the way to access the object.
Copy link
Contributor

Choose a reason for hiding this comment

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

It is not exactly clear from this sentence that DemoObject() doesn't create new instance of DemoObject but returns the existing single one. I would prefer to avoid the word "create".

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed


Kotlin/Native supports C interop too. Checkout the [Kotlin/Native as a Dynamic Library](dynamic-libraries.html)
tutorial for that, or have a look at the
[C Interop](https://github.com/JetBrains/kotlin-native/blob/master/OBJC_INTEROP.md) documentation article
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this link correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

yep

Copy link
Contributor

Choose a reason for hiding this comment

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

OBJC_INTEROP.md doesn't seem to cover anything related to C interop or -produce dynamic. What do I miss?

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

with Kotlin objects from C language. Kotlin/Native has an interop with Objective-C and
Swift, and integrates with their reference counters. You may want to find more details
in the [documentation](https://github.com/JetBrains/kotlin-native/blob/master/OBJC_INTEROP.md)
or to check the related [TUTORIAL_KOTLIN_macOS_FRAMEWORK_LINK].
Copy link
Contributor

@SvyatoslavScherbina SvyatoslavScherbina Aug 13, 2018

Choose a reason for hiding this comment

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

Does anything prevent this link from being [apple-framework.html]?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll just remove that. The next sentence is just the about the same thing


Kotlin/Native object references does not support multi-threaded access. It
might be necessary to host the returned `demo_ExportedSymbols*` pointer
per thread.
Copy link
Contributor

Choose a reason for hiding this comment

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

@olonho Isn't demo_ExportedSymbols* the same for all threads?

Copy link
Member Author

Choose a reason for hiding this comment

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

+1 to the question


## Using Generated Headers from C

The usage from C not complicated and strait forward. We create a `main.c` file with the following
Copy link
Contributor

Choose a reason for hiding this comment

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

strait forward

Is it correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

nope, fixed

//create Kotlin object instance
demo_kref_demo_DemoClazz newInstance
= lib->kotlin.root.demo.DemoClazz.DemoClazz();
long result = lib->kotlin.root.demo.DemoClazz.foo(newInstance, 42);
Copy link
Contributor

Choose a reason for hiding this comment

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

long != demo_KLong. It is better to use either demo_KLong or long long here.

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

On macOS 10.13 with Xcode, we compile the C code and link it with the dynamic library
with the following command:
```bash
gcc main.c libdemo.dylib
Copy link
Contributor

Choose a reason for hiding this comment

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

On macOS there is no gcc anymore, it is wrapper for clang. It makes some sense to use the latter directly here.

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

@jonnyzzz jonnyzzz changed the title [Kotlin/Native Tutorials] Writing a framework in Kotlin and using it in Objective-C Kotlin/Native tutorial. Writing a framework and dynamic library Aug 15, 2018
are mapped directly.
are mapped directly. Non-nullable primitive types are mapped transparently.
Nullable primitive types are mapped into `NSNumber*`.
You may see both higher order functions `acceptFunF` and `supplyFun` are included,
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see the second one.

Copy link
Member Author

Choose a reason for hiding this comment

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

returned that function back in the samples above, thanks for noticing that

are mapped directly. Non-nullable primitive types are mapped transparently.
Nullable primitive types are mapped into `NSNumber*`.
You may see both higher order functions `acceptFunF` and `supplyFun` are included,
and accept Objective-C lambdas.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure people often name Objective-C blocks this way.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll use blocks too here, thanks!

We add the Framework in the `Embedded Binaries` section of the `General` section of
the *target* configuration. We need to fix the `Framework Search Paths` in the
`Build Settings` of the *target* configuration. We shall use the `iOS_emu` folder
or the `ios_arm64` target for the run in the iOS emulator.
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't get the meaning of this sentence. Is it entirely correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

rewrote it entirely, thanks!


## XCode for iOS Targets

We add the Framework in the `Embedded Binaries` section of the `General` section of
Copy link
Contributor

Choose a reason for hiding this comment

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

Is Embedded Binaries feature available for macOS target?

Copy link
Member Author

Choose a reason for hiding this comment

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

Nope, for bugger Mac you normally include frameworks with the application and configure @rpath appropriately

```bash
kotlinc lib.kt -produce framework -target macos_x64 -output macOS/Demo
kotlinc lib.kt -produce framework -target ios_arm64 -output iOS/Demo
kotlinc lib.kt -produce framework -target ios_x64 -output iOS_emu/Demo
```

The `kotlinc` generates three frameworks for us, named `Demo.framework` under
`macOS`, `iOS` and `iOS_emu` folders respectively.
The `kotlinc` generates three Frameworks for us, named `Demo.framework` under
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the purpose of using capital "F" in "Framework"?

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed


Kotlin/Native has garbage collection, but it does not help to deal
with Kotlin objects from C language. Kotlin/Native has an interop with Objective-C and
Swift, and integrates with their reference counters. You may want to find more details
in the [documentation](https://github.com/JetBrains/kotlin-native/blob/master/OBJC_INTEROP.md)
or to check the related [TUTORIAL_KOTLIN_macOS_FRAMEWORK_LINK].

NOTE. It is possible to have several Kotlin/Native dynamic libraries in one application,
Copy link
Contributor

Choose a reason for hiding this comment

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

Does it really work on macOS?

Copy link
Member Author

Choose a reason for hiding this comment

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

removed that line for now. It is hard to test, but looks possible

@jonnyzzz jonnyzzz force-pushed the tutorial-kn-apple-framework branch from d2bbab8 to e89bc8d Compare August 16, 2018 13:24
B1ggDave and others added 3 commits August 27, 2018 13:02
bit of proofreading on it
bit of proofreading
@jonnyzzz jonnyzzz merged commit 68f7189 into master Aug 29, 2018
@jonnyzzz jonnyzzz deleted the tutorial-kn-apple-framework branch August 29, 2018 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants