a brief history of time
“In the beginning the Universe was created. This had made many people
angry and has been widely regarded as a bad move.”
Slide 6
Slide 6 text
android iOS
Slide 7
Slide 7 text
Dark Theme
Slide 8
Slide 8 text
android iOS
Slide 9
Slide 9 text
Photo by Fabian Grohs on Unsplash
Slide 10
Slide 10 text
Photo by Fabian Grohs on Unsplash
a wild application idea
appears!
develop it for Android and iOS.
Slide 11
Slide 11 text
-hire a team of specialised developers
-define requirements
-plan features development
-start development/ write unit tests
-write t-specs for SQA validation
-cross-checking validation between both platforms
(or jacks)
Steps
Slide 12
Slide 12 text
~2xthe team
the cost
the status meetings required
the time spent on development
the time spent on testing
native
the time spent on bug fixing
Slide 13
Slide 13 text
Photo by Fabian Grohs on Unsplash
a wild application idea
appears!
develop it for Android and iOS and web.
Slide 14
Slide 14 text
~3xthe team
the cost
the status meetings required
the time spent on development
the time spent on testing
the time spent on bug fixing
Slide 15
Slide 15 text
No content
Slide 16
Slide 16 text
let’s find a solution
that works on all
platforms.
- smaller team
- typically half of the cost needed on native
- small learning curve for web developers
(advantages)
Cross-platform
Slide 19
Slide 19 text
- chained to the framework implementation of UI
- new updates from the OS will take time to adopt
- performance is not the same
- some native code might need to be written
- OS/ device features are dependent on the fw support
- dart is not widely used language (for now)
(disadvantages)
Cross-platform
Slide 20
Slide 20 text
how can we have
the best of both
worlds?
Slide 21
Slide 21 text
how can we have
the best of both
worlds?
Slide 22
Slide 22 text
kotlin
“42.”
Slide 23
Slide 23 text
No content
Slide 24
Slide 24 text
- developed by JetBrains
- open source
- concise, safe, interoperable, tool-friendly
- supported/ used by Google for Android Development
- it’s more than “just for Android”
Kotlin
Server-side Android Kotlin JS Native
Slide 25
Slide 25 text
Mutability
etc.
Null safety
Default
values
Type
inference
String
interpolation
when
Collections
> [2, 4, 6, 8]
mutableListOf(1, null, 2, null, 3, 4, 5, 6, 7, 8, 9)
.filterNotNull()
.filter { it % 2 == 0 }
(collections)
Language features
output
Slide 34
Slide 34 text
> [8, 6, 4, 2]
mutableListOf(1, null, 2, null, 3, 4, 5, 6, 7, 8, 9)
.filterNotNull()
.filter { it % 2 == 0 }
.sortedDescending()
(collections)
Language features
output
Slide 35
Slide 35 text
- small learning curve involved
- easily to go from JavaScript/ Swift into Kotlin and back
(from a developers’ point of view)
Easy to learn
Slide 36
Slide 36 text
var variable = 42
variable = 1
let value = 42
var variable = 42
variable = 1
val value = 42
*adapted from http://nilhcem.com/swift-is-like-kotlin/
(variables and constants)
Kotlin vs Swift
Kotlin
Swift
Slide 37
Slide 37 text
fun greet(name: String, day: String): String {
return "Hello $name, today is $day."
}
greet(“Porto", “Saturday")
*updated from http://nilhcem.com/swift-is-like-kotlin/
func greet(_ name: String,_ day: String) -> String {
return "Hello \(name), today is \(day)."
}
greet(“Porto", “Saturday")
Kotlin
Swift
(functions)
Kotlin vs Swift
Slide 38
Slide 38 text
kotlin multiplatform
“‘Resistance is useless!’ How can anyone maintain a positive mental
attitude if you’re saying things like that?”
Slide 39
Slide 39 text
K
otlin multiplatform
using kotlin in projects
that target more than
one platform
Slide 40
Slide 40 text
(advantages)
- language features
- kotlin first!
- low risk
- you decide what’s worth to share across projects
- interoperability
- consistency across platforms
- strong community support
Kotlin Multiplatform
Slide 41
Slide 41 text
first reaction
Slide 42
Slide 42 text
view view view view
model
parser
network
presentation presentation presentation presentation
network network network
parser parser parser
model model model
presentation presentation presentation
desktop
web
iOS
android
Slide 43
Slide 43 text
business logic business logic business logic
model
parser
network
presentation
model
parser
network
presentation
model
parser
network
presentation
model
parser
network
presentation
business logic
view view view view
desktop
web
iOS
android
Slide 44
Slide 44 text
android iOS web desktop
model
parser
network
presentation
common
view view view view
java/kotlin objective-c/ swift (kotlin) JS supported in jvm
Slide 45
Slide 45 text
but how can they expect to communicate?
Slide 46
Slide 46 text
declared at common module
expect
declared at android module
actual
declared at iOS module
declared at …
Slide 47
Slide 47 text
we want a platform-specific value for name
Slide 48
Slide 48 text
expect object Platform {
val name: String
}
commonMain
src/commonMain/sample/Platform.kt
Slide 49
Slide 49 text
commonMain
src/commonMain/sample/Platform.kt
- define actual on android
- define actual on iOS
targets: android and iOS
expect object Platform {
val name: String
}
Slide 50
Slide 50 text
src/iOSMain/sample/Platform.kt
actual object Platform {
actual val name: String = "Android"
}
actual object Platform {
actual val name: String = "iOS"
}
src/androidMain/sample/Platform.kt
platform-dependent code
Slide 51
Slide 51 text
IntelliJ will ask you declare the implementations for actual
Slide 52
Slide 52 text
*.kt
common
expect
JVM
actual
*.kt, *.java, *.jar
Native
actual
*.kt, *C, Swift, Framework
JS
actual
*.kt, *.js, NPM
Slide 53
Slide 53 text
medium.com/@cafonsomota
Slide 54
Slide 54 text
demo
"For a moment, nothing happened. Then, after a second or so, nothing
continued to happen.”
Slide 55
Slide 55 text
github.com/cmota/commit19
Slide 56
Slide 56 text
- implement UI
- RecyclerView (Android)/ UITableViewController (iOS)
- make network request
- parse response object
- notify the UI
- that there’s new content available to be draw
- reload the list
project structure
class CommitAPI(engine: HttpClientEngine) {
private val client = HttpClient(engine) {
install(JsonFeature) {
serializer = KotlinxSerializer()
}
}
suspend fun fetchSpeakers(): AllSpeakersEntity {
val response = client.get{ url ("$URL")}
val json = response.readText()
return Json.parse(SpeakersEntity.serializer(), json)
}
}
network
src/commonMain/data/CommitAPI.kt
android
business logic
model
parser
network
presentation
view
Slide 61
Slide 61 text
class CommitAPI(engine: HttpClientEngine) {
private val client = HttpClient(engine) {
install(JsonFeature) {
serializer = KotlinxSerializer()
}
}
suspend fun fetchSpeakers(): AllSpeakersEntity {
val response = client.get{ url ("$URL")}
val json = response.readText()
return Json.parse(SpeakersEntity.serializer(), json)
}
}
parser
src/commonMain/data/CommitAPI.kt
android
business logic
model
parser
network
presentation
view
Slide 62
Slide 62 text
{
“speaker": "Carlos Mota",
"company": "WIT SOFTWARE",
"title": "Lead Software Engineer",
"bio": “... GDG Coimbra organizer and Kotlin evangelist, he also has
a huge passion for travel, photography, space and the occasional run.",
"talk_title": "The Hitchhikers Guide Through Kotlin Multiplatform",
“talk_description”:”... But what I told that you could just develop
in Kotlin and run it seamlessly on all devices? Here comes Kotlin
Multiplatform!",
"talk_time":"16:00",
"img": “https://commitporto.com/static/speakers/carlos.jpg"
...
}
json response
http://gist.github.com/cmota/…commit-speakers.json
Slide 63
Slide 63 text
{
"speaker": "Carlos Mota",
"company": "WIT SOFTWARE",
"title": "Lead Software Engineer",
"bio": "... GDG Coimbra organizer and Kotlin evangelist, he also has
a huge passion for travel, photography, space and the occasional run.",
"talk_title": "The Hitchhikers Guide Through Kotlin Multiplatform",
"talk_description”:"... But what I told that you could just develop
in Kotlin and run it seamlessly on all devices? Here comes Kotlin
Multiplatform!",
"talk_time":"16:00",
"img": "https://commitporto.com/static/speakers/carlos.jpg"
...
}
json response
http://gist.github.com/cmota/…commit-speakers.json
Slide 64
Slide 64 text
@Serializable
data class Speaker(val speaker: String,
val company: String,
val title: String,
val bio: String,
@SerialName("talk_title") val title: String,
@SerialName("talk_description") val description: String,
@SerialName("talk_time") val schedule: String,
val img: String)
model
src/commonMain/data/Speaker.kt
ktor-client-serialization
android
business logic
model
parser
network
presentation
view
Slide 65
Slide 65 text
class SpeakersListPresenter(val speakers: GetSpeakers) {
lateinit var view: ISpeakersListView
fun attachView(appView: ISpeakersListView) {
view = appView
fetchSpeakersList()
}
…
presenter
src/commonMain/presentation/SpeakersListPresenter.kt
android
business logic
model
parser
network
presentation
view
Slide 66
Slide 66 text
interface ISpeakerEvent {
fun onNewList(speakers: List)
fun onNewListFailed(e: Exception)
}
presenter
src/commonMain/presentation/ISpeakerEvent.kt
android
business logic
model
parser
network
presentation
view
Slide 67
Slide 67 text
class SpeakersListPresenter(val speakers: GetSpeakers) {
lateinit var view: ISpeakersListView
fun attachView(appView: ISpeakersListView) {
view = appView
fetchSpeakersList()
}
…
presenter
src/commonMain/presentation/SpeakersListPresenter.kt
android
business logic
model
parser
network
presentation
view
onCreate()/ viewDidLoad()
Slide 68
Slide 68 text
fun fetchSpeakersList() {
PresenterCoroutineScope(defaultDispatcher).launch {
speakers(
onSuccess = { view.onSpeakersListFetched(it) },
onFailure = { view.onSpeakersListFailed(it) })
}
}
presenter
src/commonMain/presentation/SpeakersListPresenter.kt
android
business logic
model
parser
network
presentation
view
Slide 69
Slide 69 text
class GetSpeakers(val commitAPI: CommitAPI) {
suspend operator fun invoke(
onSuccess: (List) -> Unit,
onFailure: (Exception) -> Unit) {
val result = commitAPI.fetchSpeakers()
coroutineScope {
launch(uiDispatcher) {
try {
onSuccess(result.speakers)
} catch (e: Exception) {
onFailure(e)
}
…
}
domain
src/commonMain/domain/GetSpeakers.kt
android
business logic
model
parser
network
presentation
view
Slide 70
Slide 70 text
No content
Slide 71
Slide 71 text
class MainActivity : AppCompatActivity(), ISpeakersEvent {
val presenter by lazy { ServiceLocator.getPresenter }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
presenter.attachView(this)
}
override fun onNewList(speakers: List) {
setUiComponents(speakers)
}
...
android
src/commonAndroid/presentation/MainActivity.kt
android
business logic
model
parser
network
presentation
view
Slide 72
Slide 72 text
class MainActivity : AppCompatActivity(), ISpeakersEvent {
val presenter by lazy { ServiceLocator.getPresenter }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
presenter.attachView(this)
}
override fun onNewList(speakers: List) {
setUiComponents(speakers)
}
...
android
src/commonAndroid/presentation/MainActivity.kt
android
business logic
model
parser
network
presentation
view
Slide 73
Slide 73 text
object ServiceLocator {
val commitApi =
CommitAPI(PlatformServiceLocator.httpClientEngine)
val getPresenter: SpeakersListPresenter
get() = SpeakersListPresenter(GetSpeakers(commitApi))
}
expect object PlatformServiceLocator {
val httpClientEngine: HttpClientEngine
}
init
src/commonMain/ServiceLocator.kt
android
business logic
model
parser
network
presentation
view
Slide 74
Slide 74 text
actual object PlatformServiceLocator {
actual val httpClientEngine: HttpClientEngine by lazy {
OkHttp.create ()
}
}
init
src/mainAndroid/PlatformServiceLocator.kt
android
business logic
model
parser
network
presentation
view
Slide 75
Slide 75 text
actual object PlatformServiceLocator {
actual val httpClientEngine: HttpClientEngine by lazy {
Ios.create()
}
}
init
src/iosMain/PlatformServiceLocator.kt
iOS
business logic
model
parser
network
presentation
view
Slide 76
Slide 76 text
override fun onBindViewHolder(viewHolder:
SpeakerViewHolder, position: Int) {
val speaker = speakers[position]
Glide.with(viewHolder.speakerPhoto)
.load(speaker.img)
.apply(RequestOptions.circleCropTransform())
.into(viewHolder.speakerPhoto)
viewHolder.speakerName.text = speaker.speaker
viewHolder.talkTitle.text = speaker.talkTitle
viewHolder.container.setOnClickListener {
action.onUserClickAction(speaker, it)
}
}
android
src/mainAndroid/presentation/SpeakersListAdapter.kt
android
business logic
model
parser
network
presentation
view
Slide 77
Slide 77 text
override fun onBindViewHolder(viewHolder:
SpeakerViewHolder, position: Int) {
val speaker = speakers[position]
Glide.with(viewHolder.speakerPhoto)
.load(speaker.img)
.apply(RequestOptions.circleCropTransform())
.into(viewHolder.speakerPhoto)
viewHolder.speakerName.text = speaker.speaker
viewHolder.talkTitle.text = speaker.talkTitle
viewHolder.container.setOnClickListener {
action.onUserClickAction(speaker, it)
}
}
android
src/mainAndroid/presentation/SpeakersListAdapter.kt
android
business logic
model
parser
network
presentation
view
you can keep using your android libraries
Slide 78
Slide 78 text
class SpeakerTableViewController:
UITableViewController, ISpeakersListView {
lazy var presenter = ServiceLocator.init().getPresenter
var speakers = [Speaker]()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
presenter.attachView(view: self)
}
…
iOS
iosApp/iosApp/SpeakerTableViewController.swift
iOS
business logic
model
parser
network
presentation
view
Slide 79
Slide 79 text
class SpeakerTableViewController:
UITableViewController, ISpeakersListView {
lazy var presenter = ServiceLocator.init().getPresenter
var speakers = [Speaker]()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
presenter.attachView(view: self)
}
…
iOS
iosApp/iosApp/SpeakerTableViewController.swift
iOS
business logic
model
parser
network
presentation
view
Slide 80
Slide 80 text
func onSpeakersListFetched(speakers: [Speaker]) {
self.speakers = speakers
self.tableView.reloadData()
}
iOS
iosApp/iosApp/SpeakerTableViewController.swift
iOS
business logic
model
parser
network
presentation
view
Slide 81
Slide 81 text
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "SpeakerTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier:
cellIdentifier, for: indexPath) as SpeakerTableViewCell
let speaker = speakers[indexPath.row]
cell.talkName.text = speaker.talkTitle
cell.speakerName.text = speaker.speaker
cell.speakerImage.image = speaker.img
return cell
}
iOS
iosApp/iosApp/SpeakerTableViewController.swift
iOS
business logic
model
parser
network
presentation
view
Slide 82
Slide 82 text
conclusions
don’t forget to bring your own towel.
Slide 83
Slide 83 text
- it’s in experimental state
- although there are projects in production
- you can spend some time resolving compilation issues
- specially if you try to target all platforms
- not possible to debug kotlin from Xcode
- lookout for plugins/ libraries updates
(in progress)
Kotlin Multiplatform
Slide 84
Slide 84 text
- strong community
- a lot of people are using kotlin nowadays
- Google and JetBrains are pushing Kotlin in
- you can ask questions directly on https://kotlinlang.slack.com
- 2x faster to develop your features business logic
- 2x faster writing unit tests
- one tech stack
- consistency across platforms
(conclusions)
Kotlin Multiplatform
Slide 85
Slide 85 text
- be extremely careful with all updates
- IDEA, jdk, kotlin, libraries - everything.
- start small, don’t try to reach 100% of shared logic
- keep versioning in mind
- remember that’s still on experimental state
(suggestions)
Kotlin Multiplatform
Slide 86
Slide 86 text
- Kotlin Slack
- Android @ Portugal Slack
- Kotlin Official
- Kotlin Training
- Kotlin by:
https://jakewharton.com/presentations/
http://antonioleiva.com/kotlin
(useful links)
more information