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

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

3f309c992e2b1a5c3c014e63810a2f68?s=128

Simone Civetta

October 03, 2018
Tweet

Transcript

  1. Shared Cross-Platform Modules with Kotlin

  2. Simone @viteinfinite

  3. None
  4. Microsoft Surface

  5. None
  6. None
  7. None
  8. 1.

  9. None
  10. 2.

  11. None
  12. None
  13. ❝"A Surface app is like an iPad app"❞

  14. None
  15. None
  16. Until...

  17. None
  18. None
  19. ❝"An iPad app is like a Xoom app"❞

  20. Right?

  21. WRONG.

  22. WRONG.

  23. WRONG.

  24. None
  25. Cross-Platform

  26. Cross-Platform

  27. Cross-Platform Cross-Platform Cross-Platform

  28. 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
  29. None
  30. None
  31. None
  32. None
  33. Years Later...

  34. None
  35. None
  36. None
  37. None
  38. None
  39. None
  40. None
  41. None
  42. None
  43. None
  44. None
  45. Konan

  46. Konan Kotlin Native Backend

  47. None
  48. Server-Side

  49. Kotlin for JavaScript Transpilation to JavaScript

  50. None
  51. ❝"Kotlin is like Swift"❞

  52. None
  53. None
  54. How It Works

  55. None
  56. None
  57. ❝A Kotlin/Native compiler is like a Swift compiler❞

  58. None
  59. Supported Targets ➀ Android Native ➁ Web Assembly ➂ Linux

    ➃ Embedded Systems ➄ macOS ➅ iOS
  60. Interoperable Can make use of C / C++ / Objective-C

    libraries
  61. None
  62. Can I Write an Entire iOS App in Kotlin?

  63. YES

  64. https://github.com/JetBrains/kotlinconf-app/tree/master/ios

  65. Can I Write a Complete Cross-Platform App in Kotlin?

  66. Can I Write a Complete Cross-Platform App in Kotlin?

  67. NO.

  68. None
  69. But...

  70. iOS Frameworks

  71. Share a Cross-Platform Module Between Android and iOS

  72. Let's Try Out

  73. My Use Case Written in Markdown, presented with Deckset

  74. 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)
  75. Multiplatform Since Kotlin 1.2, Kotlin/Native 0.6

  76. Multiplatform ☞ Sharing code across multiple platforms: ☞ JVM ☞

    JS ☞ Native (iOS, macOS, Android Native, ...)
  77. None
  78. Kotlin 1.3

  79. Kotlin 1.3 New Multiplatform DSL

  80. Kotlin 1.3 New Multiplatform DSL Key Concepts

  81. Kotlin Gradle Extension > build.gradle kotlin { // ... }

  82. 4 Key Concepts ➀ SourceSet ➁ Target ➂ Preset ➃

    Compilation
  83. 1. SourceSet ☞ A group of source files ☞ Usually

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

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

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

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

    specific target ☞ Each target can have one or more compilation, for instance "main", "test", etc
  88. 4. Compilation kotlin { targets { fromPreset(presets.iosX64, 'iosSim') { compilations.main.foo

    = bar } } }
  89. Target+Compilation ☞ Kotlin automatically creates a sourceSet for each possible

    compilation + target combination
  90. 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 │ └── ...
  91. What if...

  92. ...you need a platform-specific implementation? class Service { fun log()

    { // Platform-specific code // NSLog on iOS // Log.d on Android } }
  93. 2 Solutions

  94. 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) } }
  95. 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) } }
  96. 2. Actuals: 2/3 // SourceSet iosMain actual class Logger {

    actual fun log(string: String) { NSLog(string) } }
  97. 2. Actuals: 3/3 kotlin { sourceSets { iosMain { dependsOn

    commonMain } } ...
  98. Project Setup

  99. Project Setup ├── build.gradle │ └── src │ ├── commonMain

    │ ├── iosMain │ └── main
  100. None
  101. build.gradle: 1/4 buildscript { ext.kotlin_version = '1.3.0-rc-57' // ... Repositories

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

    { minSdkVersion 21 targetSdkVersion 26 versionCode 1 versionName '1.0' } }
  103. build.gradle: 3/4 kotlin { sourceSets { iosMain { dependsOn commonMain

    } }
  104. 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') } } }
  105. 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'.
  106. Build ./gradlew -p . linkDebugFrameworkIos

  107. ❝"A Kotlin Framework is like a Swift Framework"❞

  108. ❝"A Kotlin Framework is like a Swift Framework"❞

  109. ❝"A Framework is a Framework"❞

  110. None
  111. IDE

  112. None
  113. None
  114. None
  115. Digging Deeper

  116. Testing

  117. Testing ☞ Unit tests supported in Kotlin Multiplatform ☞ IDE

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

    { Foo().bar().startsWith("bar-") } } } ... Kotlin/Native requires additional configuration → please refer to our blogs
  119. Digging Deeper Objective-C Headers

  120. Objective-C Generated Headers

  121. Objective-C Generated Headers └── KotlinSlideParser.framework ├── Headers │ └── KotlinSlideParser.h

    ‏‏‏ ├── Info.plist ├── KotlinSlideParser └── Modules └── module.modulemap
  122. #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
  123. 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?
  124. 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 -→
  125. Digging Deeper Debugging

  126. Debugging ☞ Debugging is supported ☞ Compiler produces a .dSYM

    file ☞ "official" LLVM symbolication file for debugging ☞ Allows using Xcode for debugging
  127. Debugging (lldb) breakpoint set --func-regex "myFunc" (lldb) b kfun:

  128. None
  129. ❝Xcode is like AppCode!❞

  130. Features ☞ Breakpoints ✅ ☞ Stack Trace ✅ ☞ Stepping

    ✅ ☞ Object Value Inspection ⚠
  131. Digging Deeper Memory Management

  132. Memory Management ☞ ARC-based... ☞ ...with an automatic cycle collector

    on top ☞ Garbage Collection is performed periodically ☞ weak references supported since 0.7
  133. Digging Deeper Exceptions

  134. Exceptions ☞ All exceptions are unchecked in Kotlin ☞ @Throws

    is not supported in Kotlin/Native ☞ Cannot bridge Kotlin Exceptions to Swift Errors
  135. 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 ...
  136. None
  137. Digging Deeper Concurrency

  138. Concurrency Kotlin/Native supports: ☞ Workers (specific to Kotlin/Native) ☞ Coroutines!

    ☞ kotlinx.coroutines available since September ☞ Only single-threaded code is currently supported
  139. In Action!

  140. 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) } }
  141. 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 } }
  142. 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 } }
  143. None
  144. None
  145. So this thing is perfect! Right?

  146. Current Limitations

  147. Still a Technology Preview

  148. Current Limitations ☞ Compilation time ☞ Binary size ☞ Not

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

    How to overcome platform differences? ☞ e.g. Concurrency / Workers
  150. The Future is Now

  151. 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?
  152. Summing Up

  153. Kotlin Multiplatform + Kotlin/Native

  154. Technology Preview

  155. Encourages Modularity

  156. Feels Familiar to All Mobile Developers

  157. A New Approach to Cross-Platform

  158. None
  159. None
  160. None
  161. None
  162. Thank You!

  163. Simone @viteinfinite

  164. Full demo: github.com/xebia-france/kotlin-ios- framework

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