Given at the February 2023 Tokyo iOS Meetup, my goal with this presentation is to persuade you, the iOS Engineer, to become interested in using Kotlin Multi-platform for your iOS development to expand your audience to include Android users.
Main topics:
- Why KMM?
- Trade Offs (Pros/Cons)
- Practical Use Cases
- Typical Dev Cycle, Ways of Sharing Code
- Important Multiplatform Skills
- Summary / Q&A / Feedback
3. !
HELLO, I'M DEREK!
> Full Stack / XP Software Engineer
> Previously @ Pivotal Labs (VMware
Tanzu Labs)
> 10+ Years iOS Dev
> 20+ Years IT/Software Engineering
"
> 30+ Years Drumming
> Drumming Education Side Projects →
Now full time!
3
4. TODAY WE'RE GOING TO TALK ABOUT...
> Why KMM?
> Trade Offs (Pros/Cons)
> Practical Use Cases
> Typical Dev Cycle, Ways of Sharing Code
> Important Multiplatform Skills
> Summary / Q&A / Feedback
4
9. Check out the original presentation on this app on the
Tokyo iOS Meetup Youtube Channel:
DEREK LEE - INTRODUCING GAPCLICK - DECEMBER 8, 2018
!
https://youtu.be/z-3UdT7ye78
9
10. ← MR. BENNY GREB
(WELL-KNOWN GERMAN DRUMMING
PHENOM AND EDUCATOR)
10
11. ME →
(UNKNOWN AMERICAN ASPIRING DRUMMER
AND INDISPUTABLY TYPE-A SOFTWARE
ENGINEER)
ASKS DEREK:
"What platforms should we build the
app for? Do you know what kind of
device the majority of your users
have?""
11
12. ANSWERS BENNY:
"I have an iPhone, so let's just build
this for the iPhone!"
!
(I AM OF COURSE PARAPHRASING AND
EXAGGERATING FOR THE SAKE OF HUMOR
GIVEN THAT I'M PRESENTING TO AN AUDIENCE
OF IOS ENGINEERS AND CONSIDERING THE
SLIDES THAT FOLLOW....)
12
36. ROUGH TIMELINE
> Jan 2019: Research & incorporate KMM
(Swift → Kotlin Migration)
> June 2020: iOS Version Released!
(Already using KMM)
> February 2021: Android Version Released!
(about 8 months very much part-time work)
36
37. TO BUILD THE ANDROID APP USING KMM:
> UI (including phone + tablet layouts)
> Low-level audio (C++)
> Minimal Android Platform-Specific Code
(Preferences aka User Defaults)
> More Swift → Kotlin Migration
(MVC to MVP Presentation Layer)
37
42. BEAT NOTE: BUILT W/KMM FROM THE START
> April 2021: Development Begins
> June 2022: MVP v1.0 Released
> January 2023: v2.0 Released
> Android Target: Spring/Summer 2023
> Web Target: Fall/Winter 2023
42
43. WHY CONSIDER MULTIPLATFORM?
1. Platform Expansion: iOS (1.8B1
), Android (2.5B2
), web (4.9B3
)
(# of devices)
2. Maximize shared code & minimize maintenance for
multiple platforms for business logic
3
https://www.zippia.com/advice/how-many-people-use-the-internet
2
https://www.businessofapps.com/data/android-statistics/
1
https://www.macrumors.com/2022/01/27/apple-1-8-billion-active-devices-worldwide
43
44. WHY KOTLIN MULTIPLATFORM?
In addition to the above..
BUILD NATIVE APPS WITH NATIVE UX
AND FULL NATIVE API ACCESS
44
45. WHY NOT USE X?
FIRST AND FOREMOST: NOTHING ELSE PROVIDES A NATIVE UX
> Ionic/Cordova/PhoneGap/etc: Javascript
> React Native: JavaScript + React
> Flutter: Dart
"
+ Unsupported by
(FLUTTER KIND OF HAS A NATIVE UX... BUT NOT REALLY )
45
46. KMM PROS: PRODUCT
> Platform expansion (Android, Web, others)
> Access to native UI (Swift UI, UIKit, future?)
> Access to native APIs (limited via Kotlin; all via Swift)
> Possible to adopt incrementally
> Increased product stability through:
> Shared code; fix bugs once; improve maintainability
46
47. KMM PROS: DEVELOPER EXPERIENCE
> Android Studio > Xcode
> Kotlin is similar to Swift, making it easy to learn
> Kotlin is a modern language; supported by JetBrains
> Kotlin is an official development language for Android
47
49. KMM TRADEOFFS: ADDED BUILD PROCESS
COMPLEXITY
> Learn Kotlin DSL (or Gradle Groovy)
> Most of the time: once it's configured, there's rarely a
need to change
49
50. KMM TRADEOFFS: LONGER TIME TO BUILD
IOS PROJECT
> Buy an Mx Mac.
(You know you're just looking for an excuse anyway )
> Sometimes doesn't apply; depends on the task (more on
this later)
50
51. KMM TRADEOFFS: KOTLIN ↔ SWIFT/
OBJECTIVE-C LIMITATIONS
> Most applicable during migrations and learning KMM.
> Learn the necessary data transformations and
limitations.
> The more you maximize shared code, you minimize
integration points.
> Memory management constraints have improved
dramatically.
51
52. KMM TRADEOFFS: KOTLIN ↔ SWIFT/
OBJECTIVE-C LIMITATIONS
Your best resource for this is:
!
Interoperability with Swift/Objective-C
52
53. KMM TRADEOFFS: STILL EVOLVING
> Getting more stable: has recently entered beta
> Expect the tech to continue to further stabilize as
they move out of beta
53
54. KMM TRADEOFFS: NOT YET MAINSTREAM
> First-class support from JetBrains
> Please proactively contribute to this growing
community!
54
55. COMMON DEVELOPMENT CONCERNS
> Performance
> Build complexity
> Process complexity
> Team/engineer adoption hesitancy
> Risk of siloed skills
55
56. PRACTICAL USE CASES
1. Setting up a project from scratch
2. Swift → Kotlin Migration
3. Building a new feature in iOS
56
57. #1. SETTING UP A PROJECT FROM SCRATCH
> Would likely require it's own talk...
57
58. #1. SETTING UP A PROJECT FROM SCRATCH
> Try the Kotlin Multiplatform Plugin in Android Studio
58
59. #1. SETTING UP A PROJECT FROM SCRATCH
See also JetBrains Blog and YouTube channel for
resources and documentation:
!
Getting started and tutorials
!
Multiplatform Documentation
!
https://www.youtube.com/user/jetbrainstv
59
60. #1. SETTING UP A PROJECT FROM SCRATCH
Sample build.gradle.kts (Kotlin DSL)
plugins {
id("com.android.library")
kotlin("multiplatform")
}
kotlin {
android()
iosTarget()
...
}
val packForXcode by tasks.creating(Sync::class) {
...
}
60
61. #2. SWIFT → KOTLIN MIGRATION
> Start simple with domain model objects
> Start with a simple algorithm: Tap to set tempo
> Progress to more and more complex code paths
> Possible to move networking, local persistence, even
graphics rendering in shared code (!)
61
62. #2. SWIFT → KOTLIN MIGRATION
TAKE AN EXISTING ENUM IN SWIFT...
enum NoteValue: String, Codable {
case quarterNote
case eighthNote
var displayValue: String {
get {
switch self {
case .quarterNote: return "4"
case .eighthNote: return "8"
}
}
}
}
62
63. #2. SWIFT → KOTLIN MIGRATION
... AND MOVE IT OVER TO KOTLIN.
enum class NoteValue(val value: String) {
QuarterNote("QuarterNote"),
EighthNote("EighthNote");
val rawValue: String
get() = this.value
val displayValue: String
get() {
return when (this) {
QuarterNote -> "4"
EighthNote -> "8"
}
}
}
63
65. #2. SWIFT → KOTLIN MIGRATION
INTEGRATING CHANGES BETWEEN KOTLIN + SWIFT
> Re-builds XCFramework each time the Kotlin code
changes
> Common when migrating an existing app when learning
the integration points
VERDICT: TAKES SOME TIME
65
66. #3. BUILDING A NEW FEATURE IN IOS
SELECTING BEAT TAGS ON THE "MANAGE TAGS" SCREEN
66
67. #3. BUILDING A NEW FEATURE IN IOS
STEP #1: BUILD THE UI USING SWIFT
final class TagCollectionViewCell: AutoLayoutUICollectionViewCell {
func configure(tag: SelectableTag) {
nameLabel.text = tag.name
}
override var isSelected: Bool {
didSet {
let style: UILabelStyle = isSelected ? .tagNameSelected : .tagNameUnselected
nameLabel.applyStyle(style)
}
}
}
67
68. #3. BUILDING A NEW FEATURE IN IOS
STEP #1: BUILD THE UI USING SWIFT
> Builds shared (Kotlin) XCFramework once
> Performs incremental Xcode builds
> Write your code in Swift UI or UIKit
VERDICT: FAST!
68
69. #3. BUILDING A NEW FEATURE IN IOS
STEP #2: TDD BUSINESS LOGIC ENTIRELY IN KOTLIN
@Test
fun `gives list of multiple tags with one selected in beat cache`() {
spyStubBeatsCache.getBeat_returnValue = cacheBeatBuilder.withTags(setOf("rock")).build()
spyStubTagsRepository.allTags_returnValue = setOf(Tag("latin"), Tag("rock"))
val tags = presenter.tags
assertEquals("latin", tags[0].name)
assertEquals(false, tags[0].isSelected)
assertEquals("rock", tags[1].name)
assertEquals(true, tags[1].isSelected)
}
69
70. #3. BUILDING A NEW FEATURE IN IOS
STEP #2: TDD BUSINESS LOGIC ENTIRELY IN KOTLIN
class ManageTagsPresenter(...) : ManageTagsContract.Presenter {
override val tags: List<SelectableTag>
get() {
val beatTags = beatDataMediation.beat.tags
return tagsRepository.allTags.map { tag ->
val isTagSelectedForBeat = beatTags.contains(tag.name)
return@map SelectableTag(tag.name, isTagSelectedForBeat)
}
}
}
70
71. #3. BUILDING A NEW FEATURE IN IOS
STEP #2: TDD BUSINESS LOGIC ENTIRELY IN KOTLIN
35ms to run these three tests!
71
72. #3. BUILDING A NEW FEATURE IN IOS
STEP #2: TDD BUSINESS LOGIC ENTIRELY IN KOTLIN
> Does not require XC Framework to be built
> Super fast tests and compilation
> Quick TDD cycles (red - green - refactor)
VERDICT: FAST!
72
75. SO WHAT DOES THE BUILD
PERFORMANCE LOOK LIKE?
75
76. PERFORMANCE: BUILDING THE
XCFRAMEWORK
(2021 M1 MAX MACBOOK PRO | 64GB RAM)
Module Files Lines of Code Scratch Build Re-Build
GapClick Shared 62 2057 ~4-5sec ~1-2sec
Drum Notation 32 1082 ~10sec ~1-2sec
Beat Note Shared 328 15528 ~30sec ~1-2sec
76
77. WAYS OF SHARING CODE
> expect/actual
> Kotlin interface + Swift Implementation
> Kotlin Abstract Class + Swift Implementation
> 100% Kotlin (shared)
77
78. WAYS OF SHARING CODE
See by blog post series on this for more details and code
samples:
!
The iOS Engineer’s Guide to Beginning Kotlin
Multiplatform Development
78
79. IMPORTANT SKILLS FOR MULTIPLATFORM
DEVELOPMENT
> Identifying dependencies in code (3rd party and
platform)
> Dependency Injection
> SOLID: Single Responsibility
> SOLID: Dependency Inversion
79
80. JUST HOW MUCH CODE CAN BE SHARED?
This depends on how creative you are, and...
How clearly you can identify platform-specific
dependencies and isolate them in your code.
80
81. GAP CLICK CODE BREAKDOWN (GITHUB)
But this is misleading... there are a lot of tests in Swift.
81
89. SUMMARY
> KMM allows you to share common logic using Kotlin
> Additional platforms can expand your reach
> KMM can be adopted incrementally
> There are several different ways to share code
> KMM is in beta and the community is growing
89
95. CODE SHARING: EXPECT/ACTUAL
> Writing both Android and iOS code using Kotlin
> Limited to Objective-C APIs (no Swift APIs!)
> Sometimes frustrating how to write iOS code
> I recommended using this only for building libraries
> Others recommend this for Android developers
95
96. CODE SHARING: KOTLIN INTERFACE + SWIFT
IMPLEMENTATION
> Ideal for objects that heavily rely on platform specific
APIs
> Recommended for iOS engineers over expect/actual
> Write an interface in Kotlin
> Implement the interface in Android as you would
96
97. ABSTRACT CLASS + SWIFT IMPLEMENTATION
> Similar to the previous, only the abstract Kotlin class
can contain shared logic
> Use abstract methods for implementing platform-
specific details
> Similar to the Template Object-Oriented Design Pattern
97
98. 100% KOTLIN (SHARED)
> Write all of your code in Kotlin!
> Inject platform-specific dependencies as needed
98