Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cross-Platform Modules with Kotlin/Native (v. 2...

Cross-Platform Modules with Kotlin/Native (v. 2018.10)

Simone Civetta

October 03, 2018
Tweet

More Decks by Simone Civetta

Other Decks in Programming

Transcript

  1. 1.

  2. 2.

  3. Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform

    Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform Cross-Platform
  4. YES

  5. NO.

  6. My Goal Create viewer apps, sharing parsing logic: ☞ Shared

    parsing library: ☞ iOS ➡ .framework (Kotlin/Native) ☞ Android ➡ .aar (Kotlin/JVM) ☞ iOS (view logic in Swift) ☞ Android (view logic in Kotlin)
  7. Multiplatform ☞ Sharing code across multiple platforms: ☞ JVM ☞

    JS ☞ Native (iOS, macOS, Android Native, ...)
  8. 1. SourceSet ☞ A group of source files ☞ Usually

    what's inside the src/<XXX> folder
  9. 1. SourceSet ☞ A group of source files ☞ Usually

    what's inside the src/<XXX> folder ├── build.gradle │ └── src │ ├── main // ← SourceSet │ ├── iosMain // ← SourceSet │ └── ... // ← Other SourceSets
  10. 2. Target ☞ A variant of your project for a

    given platform (e.g. Android, iOS device, iOS simulator, Linux, etc) kotlin { targets { // ... } }
  11. 3. Preset ☞ A way to easily create a target

    ☞ It's composed of pre-defined configuration kotlin { targets { fromPreset(presets.iosX64, 'iosSim') } }
  12. 4. Compilation ☞ A "trasformation" of your sources for a

    specific target ☞ Each target can have one or more compilation, for instance "main", "test", etc
  13. Target+Compilation ☞ Kotlin automatically creates a sourceSet for each possible

    compilation + target combination ├── build.gradle │ └── src │ ├── main // almost useless in MPP │ ├── commonMain // created automatically │ ├── iosMain │ ├── iosTest │ └── ...
  14. ...you need a platform-specific implementation? class Service { fun log()

    { // Platform-specific code // NSLog on iOS // Log.d on Android } }
  15. 1. Interfaces Interfaces get translated conveniently across platforms interface Logger

    { fun log(string: String) } class Service(val logger: Logger) { fun log(string: String) { logger.log(string) } }
  16. 2. Actuals: 1/3 Actuals are a functionality introduced in KMPP

    1.2 // SourceSet commonMain expect class Logger { fun log(string: String) } class Service(val logger: Logger) { fun log(string: String) { logger.log(string) } }
  17. 2. Actuals: 2/3 // SourceSet iosMain actual class Logger {

    actual fun log(string: String) { NSLog(string) } }
  18. build.gradle: 1/4 buildscript { ext.kotlin_version = '1.3.0-rc-57' // ... Repositories

    and dependencies as usual } apply plugin: 'kotlin-multiplatform' // ...
  19. build.gradle: 2/4 apply plugin: 'com.android.library' android { compileSdkVersion 26 defaultConfig

    { minSdkVersion 21 targetSdkVersion 26 versionCode 1 versionName '1.0' } }
  20. build.gradle: 4/4 targets { fromPreset(presets.android, 'androidLibrary') def isSim = findProperty("kotlin.target")

    == "iosSim" def iosPreset = isSim ? presets.iosX64 : presets.iosArm64 fromPreset(iosPreset, 'iosSim') { compilations.main.outputKinds('FRAMEWORK') } } }
  21. Tasks ./gradlew tasks assemble - Assembles all variants of all

    applications and packages. build - Assembles and tests this project. linkDebugFrameworkIos - Links an Objective-C framework from the 'main' compilation for target 'native'.
  22. IDE

  23. import kotlin.test.* class TestFoo { @Test fun testBar() { assertTrue

    { Foo().bar().startsWith("bar-") } } } ... Kotlin/Native requires additional configuration → please refer to our blogs
  24. Objective-C Generated Headers └── KotlinSlideParser.framework ├── Headers │ └── KotlinSlideParser.h

    ‏‏‏ ├── Info.plist ├── KotlinSlideParser └── Modules └── module.modulemap
  25. #import <Foundation/Foundation.h> @class KSPSupport, KSPMyEnum, KSPStdlibEnum, KSPOtherEnum, KSPSlideEntity, KSPSlideEntityPage, KSPMarkdownEntity,

    KSPMarkdownEntityItalic, KSPMarkdownEntityBold; @class KSPMarkdownEntityHeader, KSPMarkdownEntityInlineCode, KSPMarkdownEntityCodeBlock, KSPMarkdownEntityLinks; @class KSPMarkdownEntityPlain, KSPMarkdownEntityRefer, KSPMarkdownEntityDelete, KSPSlideParser; @protocol KSPStdlibComparable; NS_ASSUME_NONNULL_BEGIN @interface KotlinBase : NSObject -(instancetype) init __attribute__((unavailable)); +(instancetype) new __attribute__((unavailable)); +(void)initialize __attribute__((objc_requires_super)); @end; @interface KotlinBase (KotlinBaseCopying) <NSCopying> @end; __attribute__((objc_runtime_name("KotlinMutableSet"))) @interface KSPMutableSet<ObjectType> : NSMutableSet<ObjectType> @end; __attribute__((objc_runtime_name("KotlinMutableDictionary"))) @interface KSPMutableDictionary<KeyType, ObjectType> : NSMutableDictionary<KeyType, ObjectType> @end; __attribute__((objc_subclassing_restricted)) @interface KSPSupport : KotlinBase -(instancetype)init NS_SWIFT_NAME(init()) NS_DESIGNATED_INITIALIZER; -(NSNumber* _Nullable)optionalInt NS_SWIFT_NAME(optionalInt()); -(void)somethingThatThrows NS_SWIFT_NAME(somethingThatThrows()); @end; @protocol KSPStdlibComparable @required -(int32_t)compareToOther:(id _Nullable)other NS_SWIFT_NAME(compareTo(other:)); @end; NS_ASSUME_NONNULL_END
  26. Kotlin / ObjC / Swift Type Mapping Kotlin Objective-C Swift

    Boolean BOOL Bool Float / Double float / double Float / Double Int int32_t Int32 String NSString * String List<String> NSArray<NSString *> * [String] Int? Nullable NSNumber * NSNumber?
  27. Kotlin Objective-C Swift interface @protocol @protocol class class class data

    class class class enum class StdlibEnum StdlibEnum Kotlin Objective-C Swift open (subclassable) open public (subclassable) open -→
  28. Debugging ☞ Debugging is supported ☞ Compiler produces a .dSYM

    file ☞ "official" LLVM symbolication file for debugging ☞ Allows using Xcode for debugging
  29. Memory Management ☞ ARC-based... ☞ ...with an automatic cycle collector

    on top ☞ Garbage Collection is performed periodically ☞ weak references supported since 0.7
  30. Exceptions ☞ All exceptions are unchecked in Kotlin ☞ @Throws

    is not supported in Kotlin/Native ☞ Cannot bridge Kotlin Exceptions to Swift Errors
  31. Exception (un)Handling fun somethingThatThrows() { throw Exception(message = "Oops.") }

    ‑ Uncaught Kotlin exception: kotlin.Exception: Oops. at 3 KotlinSlideParser 0x104cb54f3 kfun:kotlin.Exception.<init>(kotlin.String)kotlin.Exception + 115 at 4 KotlinSlideParser 0x104c81dea kfun:fr.xebia.slideparser.Support.somethingThatThrows() + 122 at 5 KotlinSlideParser 0x104c81d10 KotlinSlideParser + 7440 at 6 SlideRehearser 0x104978081 _T014SlideRehearser14ViewControllerC11viewDidLoadyyF + 81 at 7 SlideRehearser 0x104978104 _T014SlideRehearser14ViewControllerC11viewDidLoadyyFTo + 36 at 8 UIKit 0x107e8646c -[UIViewController loadViewIfRequired] + 1235 ...
  32. Concurrency Kotlin/Native supports: ☞ Workers (specific to Kotlin/Native) ☞ Coroutines!

    ☞ kotlinx.coroutines available since September ☞ Only single-threaded code is currently supported
  33. Kotlin public sealed class SlideEntity { data class Page(val contents:

    List<MarkdownEntity>): SlideEntity() } public sealed class MarkdownEntity { data class Header(val contents: List<MarkdownEntity>, val level: Int): MarkdownEntity() data class Plain(val contents: String): MarkdownEntity() } public class SlideParser { public fun parsePages(string: String): List<SlideEntity>? { return this.pageParser().process(string) } }
  34. iOS (Swift) import KotlinSlideParser // ... let parser = KSPSlideParser()

    guard let pages = parser.parsePages(string: myText) else { return } pages.first?.contents.forEach { entity in switch entity { case let header as KSPMarkdownEntityHeader: header.level // Level of the header case let plain as KSPMarkdownEntityPlain: plain.contents // Text of the entity default: break } }
  35. Android (Kotlin) import fr.xebia.slideparser.SlideParser // ... val parser = SlideParser()

    val pages = parser.parsePages(myText) pages?.first()?.contents?.forEach { when(it) { is MarkdownEntity.Header -> it.level // Level of the header is MarkdownEntity.Plain -> it.contents // Text of the entity } }
  36. Current Limitations ☞ Compilation time ☞ Binary size ☞ Not

    100% compatible with iOS bitcode ☞ Some everyday functions not available in K/N ☞ Lacking documentation
  37. Open Questions ☞ GC-like memory model: strength or weakness? ☞

    How to overcome platform differences? ☞ e.g. Concurrency / Workers
  38. The Future is Now ☞ Feature parity between Kotlin dialects

    ☞ New common extensions: ☞ coroutines ✅ ☞ Limited IDE support ✅ ☞ Simplified MPP Support ✅ ☞ Cannot inspect objects in the debugger ✅ ☞ Direct Interoperability with Swift ☞ Some new announcements at KotlinConf?
  39. Other Resources ☞ Kotlin Blog ☞ Deep Dive into Kotlin/Native

    by Andrey Breslav ☞ blog.xebia.fr | viteinfinite.com ☞ github.com/JetBrains/kotlin-mpp-example ☞ Kotlin Slack