SlideShare a Scribd company logo
1 of 98
Download to read offline
DEVELOPMENT
1
Kotlin Multiplatform in Action
Alexandr Pogrebnyak
Droidcon Tel Aviv
19.12.19.
DEVELOPMENT
● About us
● What is Kotlin Multiplatform?
● Our experience in Kotlin Multiplatform
● moko.icerock.dev – a set of multiplatform libraries
● moko-widgets
2
DEVELOPMENT
● 5 years in mobile development
● 80+ mobile projects (iOS & Android)
● 40+ developers in 2 offices
3
DEVELOPMENT
Focus on:
4
Avoid logic reduplication and
keep UI native
DEVELOPMENT
5
Code Sharing in IceRock
Flutter
React Native
J2ObjC
New programming language
Integration with OS via middle layer
Non-native UI
Legacy tech stacks
Asynchronous work with OS
Need to adopt Java-code to ObjC
Native UI
Hot reload
DEVELOPMENT
6
Kotlin Multiplatform
+
Kotlin/Native
Code Sharing at IceRock
Kotlin looks like Swift
Inner libraries already in Kotlin
Shift to native env at any moment
Familiar programming language
Native UI
Full access to Android OS and iOS features
and SDKs
There are some limitations in Kotlin > Swift direction
Devs worry about integration
DEVELOPMENT
7
Kotlin Multiplatform at IceRock
Kotlin MPP and Kotlin/Native since July 2018
1 year achievement
Reduce time and cost for both platforms by 1.5x
1.5x speed
50% of shared code on average
50:50
Easy learning curve for Android and iOS developers
Easy to learn, hard to master
DEVELOPMENT
8
Kotlin Multiplatform
Android Windows macOS Linux Java Java Script WebAssemblyiOS
DEVELOPMENT
9
expect class Logger(tag: String) {
fun log(string: String)
}
fun main() {
val logger = Logger("MPP")
logger.log("hello world!")
}
commonMain
DEVELOPMENT
10
iosMain
import platform.Foundation.NSLog
actual class Logger actual constructor(private val
tag: String) {
actual fun log(string: String) {
NSLog("[$tag] $string")
}
}
import android.util.Log
actual class Logger actual constructor(private val
tag: String) {
actual fun log(string: String) {
Log.v(tag, string)
}
}
androidMain
DEVELOPMENT
11
androidMain
└── kotlin
└── com.icerockdev.library
└── Logger.kt
commonMain
└── kotlin
└── com.icerockdev.library
└── Logger.kt
iosMain
└── kotlin
└── com.icerockdev.library
└── Logger.kt
Kotlin Multiplatform
DEVELOPMENT
12
Kotlin Multiplatform - past
View
AuthActivity AuthViewController ProfileViewController ProfileActivity
Presentation
AuthViewModel ProfileViewModel PostsViewModel AddPostViewModel
Domain
UserInteractor PostsInteractor Validator Payment
Data
User Post UserRepository PostRepository
RestUser RestPost UserApi PostApi
DEVELOPMENT
13
Kotlin Multiplatform - now
View
AuthScreen ProfileScreen
Presentation
AuthViewModel ProfileViewModel PostsViewModel
Domain
UserInteractor PostsInteractor Validator
Data
User Post UserRepository
RestUser RestPost UserApi
powered by
DEVELOPMENT
14
Shared Code in a Typical Project - past
Shared code
62%
Android-specific
16%
iOS-specific
22%
DEVELOPMENT
15
Shared Code in a Typical Project - now
Android-specific
3%
iOS-specific
2%
Shared code
95%
powered by
DEVELOPMENT
16
moko.icerock.dev
Community
DEVELOPMENT
17moko.icerock.dev
DEVELOPMENT
18moko.icerock.dev
DEVELOPMENT
19
Fast start
github.com/icerockdev/moko-template
codelabs.kmp.icerock.dev
DEVELOPMENT
20
Result
DEVELOPMENT
21
A moko-template (sample project)
https://github.com/icerockdev/moko-template.git
Features:
Install:
git clone Done!
app_name
app_icon
applicationId
● Shared business logic
● Kotlin Gradle DSL
● Modular-based architecture
● Independent feature and domain modules
● ViewModels, LiveData, Resource management, Runtime permissions access,
Media access, UI from shared code, Network layer generation from OpenAPI…
DEVELOPMENT
22
github.com/icerockdev/moko-template
widgets branch
DEVELOPMENT
Goal #1
1 Kotlin developer => 2 native mobile apps
23
goals
DEVELOPMENT
Goal #2
Fast start = the codebase should grow without
rewrites
24
goals
DEVELOPMENT
The basic architecture concepts:
1. compliance with platform rules
2. declare structure, not rendering
3. compile-time safety
4. reactive data handling
25
concepts
DEVELOPMENT
26
code
class App : BaseApplication() {
override fun setup() {
val theme = Theme()
registerScreenFactory(MainScreen::class) { MainScreen(theme) }
}
override fun getRootScreen(): KClass<out Screen<Args.Empty>> {
return MainScreen::class
}
}
common
DEVELOPMENT
27
code
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
mppApplication = App().apply {
setup()
}
}
companion object {
lateinit var mppApplication: App
}
}
class MainActivity : HostActivity() {
override val application: BaseApplication
get() = MainApplication.mppApplication
}
android
DEVELOPMENT
28
code
@UIApplicationMain
class AppDelegate: NSObject, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: ..., didFinishLaunchingWithOptions ...) -> Bool {
let app = App()
app.setup()
let screen = app.createRootScreen()
let rootViewController = screen.createViewController()
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = rootViewController
window?.makeKeyAndVisible()
return true
}
}
ios
DEVELOPMENT
29
code
class MainScreen(
private val theme: Theme
) : WidgetScreen<Args.Empty>() {
override fun createContentWidget() = with(theme) {
container(size = WidgetSize.AsParent) {
center {
text(
size = WidgetSize.WrapContent,
text = const(MR.strings.hello_world.desc())
)
}
}
}
}
common
DEVELOPMENT
30
code
class MainScreen(
private val theme: Theme
) : WidgetScreen<Args.Empty>() {
override fun createContentWidget() = with(theme) {
container(size = WidgetSize.AsParent) {
center {
text(
size = WidgetSize.WrapContent,
text = const(MR.strings.hello_world.desc())
)
}
}
}
}
common
DEVELOPMENT
31
code
class MainScreen(
private val theme: Theme
) : WidgetScreen<Args.Empty>() {
override fun createContentWidget() = with(theme) {
container(size = WidgetSize.AsParent) {
center {
text(
size = WidgetSize.WrapContent,
text = const(MR.strings.hello_world.desc())
)
}
}
}
}
common
DEVELOPMENT
32
code
class MainScreen(
private val theme: Theme
) : WidgetScreen<Args.Empty>() {
override fun createContentWidget() = with(theme) {
container(size = WidgetSize.AsParent) {
center {
text(
size = WidgetSize.WrapContent,
text = const(MR.strings.hello_world.desc())
)
}
}
}
}
common
DEVELOPMENT
33
code
class MainScreen(
private val theme: Theme
) : WidgetScreen<Args.Empty>() {
override fun createContentWidget() = with(theme) {
container(size = WidgetSize.AsParent) {
center {
text(
size = WidgetSize.WrapContent,
text = const(MR.strings.hello_world.desc())
)
}
}
}
}
common
DEVELOPMENT
34
code
class MainScreen(
private val theme: Theme
) : WidgetScreen<Args.Empty>() {
override fun createContentWidget() = with(theme) {
container(size = WidgetSize.AsParent) {
center {
text(
size = WidgetSize.WrapContent,
text = const(MR.strings.hello_world.desc())
)
}
}
}
}
common
DEVELOPMENT
35
code
DEVELOPMENT
36
code
class App : BaseApplication() {
override fun setup() {
val theme = Theme {
// custom styles here
}
registerScreenFactory(MainScreen::class) { MainScreen(theme) }
}
override fun getRootScreen(): KClass<out Screen<Args.Empty>> {
return MainScreen::class
}
}
common
DEVELOPMENT
37
code
val theme = Theme {
textFactory = DefaultTextWidgetViewFactory(
DefaultTextWidgetViewFactoryBase.Style(
textStyle = TextStyle(
size = 24,
color = Colors.black
),
padding = PaddingValues(padding = 16f)
)
)
}
common
DEVELOPMENT
38
code
val theme = Theme {
textFactory = DefaultTextWidgetViewFactory(
DefaultTextWidgetViewFactoryBase.Style(
textStyle = TextStyle(
size = 24,
color = Colors.black
),
padding = PaddingValues(padding = 16f)
)
)
}
common
DEVELOPMENT
39
code
val theme = Theme {
textFactory = DefaultTextWidgetViewFactory(
DefaultTextWidgetViewFactoryBase.Style(
textStyle = TextStyle(
size = 24,
color = Colors.black
),
padding = PaddingValues(padding = 16f)
)
)
}
common
DEVELOPMENT
40
code
DEVELOPMENT
Simple is simple.
How about some complexity?
41
code
DEVELOPMENT
42
code
DEVELOPMENT
43
code
image center
between
2 fields and a button
in the center
DEVELOPMENT
44
code
class LoginScreen(
private val theme: Theme
) : WidgetScreen<Args.Empty>() {
override fun createContentWidget() = with(theme) {
constraint(size = WidgetSize.AsParent) {
// ...
}
}
}
common
DEVELOPMENT
45
code
override fun createContentWidget() = with(theme) {
constraint(size = WidgetSize.AsParent) {
val logoImage = +image(
size = WidgetSize.Const(SizeSpec.WrapContent, SizeSpec.WrapContent),
image = const(Image.resource(MR.images.logo))
)
}
}
common
DEVELOPMENT
46
code
constraint(size = WidgetSize.AsParent) {
val logoImage = +image(...)
val emailInput = +input(
size = WidgetSize.WidthAsParentHeightWrapContent,
id = Id.EmailInputId,
label = const("Email".desc() as StringDesc),
field = viewModel.emailField
)
val passwordInput = +input(
size = WidgetSize.WidthAsParentHeightWrapContent,
id = Id.PasswordInputId,
label = const("Password".desc() as StringDesc),
field = viewModel.passwordField
)
}
common
DEVELOPMENT
47
code
constraint(size = WidgetSize.AsParent) {
val logoImage = +image(...)
val emailInput = +input(...)
val passwordInput = +input(...)
val loginButton = +button(
size = WidgetSize.Const(SizeSpec.AsParent, SizeSpec.Exact(50f)),
text = const("Login".desc() as StringDesc),
onTap = viewModel::onLoginPressed
)
}
common
DEVELOPMENT
48
code
constraint(size = WidgetSize.AsParent) {
val logoImage = +image(...)
val emailInput = +input(...)
val passwordInput = +input(...)
val loginButton = +button(...)
constraints {
passwordInput centerYToCenterY root
passwordInput leftRightToLeftRight root
emailInput bottomToTop passwordInput
emailInput leftRightToLeftRight root
loginButton topToBottom passwordInput
loginButton leftRightToLeftRight root
logoImage centerXToCenterX root
logoImage.verticalCenterBetween(
top = root.top,
bottom = emailInput.top
)
}
common
DEVELOPMENT
49
code
constraint(size = WidgetSize.AsParent) {
val logoImage = +image(...)
val emailInput = +input(...)
val passwordInput = +input(...)
val loginButton = +button(...)
constraints {
passwordInput centerYToCenterY root
passwordInput leftRightToLeftRight root
emailInput bottomToTop passwordInput
emailInput leftRightToLeftRight root
loginButton topToBottom passwordInput
loginButton leftRightToLeftRight root
logoImage centerXToCenterX root
logoImage.verticalCenterBetween(
top = root.top,
bottom = emailInput.top
)
}
common
DEVELOPMENT
50
code
constraint(size = WidgetSize.AsParent) {
val logoImage = +image(...)
val emailInput = +input(...)
val passwordInput = +input(...)
val loginButton = +button(...)
constraints {
passwordInput centerYToCenterY root
passwordInput leftRightToLeftRight root
emailInput bottomToTop passwordInput
emailInput leftRightToLeftRight root
loginButton topToBottom passwordInput
loginButton leftRightToLeftRight root
logoImage centerXToCenterX root
logoImage.verticalCenterBetween(
top = root.top,
bottom = emailInput.top
)
}
common
DEVELOPMENT
51
code
constraint(size = WidgetSize.AsParent) {
val logoImage = +image(...)
val emailInput = +input(...)
val passwordInput = +input(...)
val loginButton = +button(...)
constraints {
passwordInput centerYToCenterY root
passwordInput leftRightToLeftRight root
emailInput bottomToTop passwordInput
emailInput leftRightToLeftRight root
loginButton topToBottom passwordInput
loginButton leftRightToLeftRight root
logoImage centerXToCenterX root
logoImage.verticalCenterBetween(
top = root.top,
bottom = emailInput.top
)
}
common
DEVELOPMENT
52
code
DEVELOPMENT
53
code
DEVELOPMENT
54
code
val loginTheme = Theme(theme) {
constraintFactory = DefaultConstraintWidgetViewFactory(
DefaultConstraintWidgetViewFactoryBase.Style(
padding = PaddingValues(16f),
background = Background(
fill = Fill.Solid(Colors.white)
)
)
)
}
common
DEVELOPMENT
55
code
val loginTheme = Theme(theme) {
constraintFactory = DefaultConstraintWidgetViewFactory(...)
imageFactory = DefaultImageWidgetViewFactory(
DefaultImageWidgetViewFactoryBase.Style(
scaleType = DefaultImageWidgetViewFactoryBase.ScaleType.FIT
)
)
}
common
DEVELOPMENT
56
code
val loginTheme = Theme(theme) {
constraintFactory = DefaultConstraintWidgetViewFactory(...)
imageFactory = DefaultImageWidgetViewFactory(...)
val corners = platformSpecific(android = 8f, ios = 25f)
inputFactory = DefaultInputWidgetViewFactory(
DefaultInputWidgetViewFactoryBase.Style(
margins = MarginValues(bottom = 8f),
underLineColor = Color(0xe5e6eeFF),
labelTextStyle = TextStyle(
color = Color(0x777889FF)
)
)
)
}
common
DEVELOPMENT
57
code
val loginTheme = Theme(theme) {
constraintFactory = DefaultConstraintWidgetViewFactory(...)
imageFactory = DefaultImageWidgetViewFactory(...)
val corners = platformSpecific(android = 16f, ios = 25f)
inputFactory = DefaultInputWidgetViewFactory(
DefaultInputWidgetViewFactoryBase.Style(
margins = MarginValues(bottom = 8f),
underLineColor = Color(0xe5e6eeFF),
labelTextStyle = TextStyle(
color = Color(0x777889FF)
)
)
)
}
common
DEVELOPMENT
58
code
val loginTheme = Theme(theme) {
constraintFactory = DefaultConstraintWidgetViewFactory(...)
imageFactory = DefaultImageWidgetViewFactory(...)
val corners = ...
inputFactory = DefaultInputWidgetViewFactory(...)
buttonFactory = DefaultButtonWidgetViewFactory(
DefaultButtonWidgetViewFactoryBase.Style(
margins = MarginValues(top = 32f),
background = StateBackground(
normal = Background(
fill = Fill.Solid(Color(0x6770e0FF)),
shape = Shape.Rectangle(cornerRadius = corners)
),
pressed = Background(...),
disabled = Background(...)
),
textStyle = TextStyle(color = Colors.white)
)
)
}
common
DEVELOPMENT
59
code
DEVELOPMENT
60
code
how add different
button?
DEVELOPMENT
61
code
constraint(size = WidgetSize.AsParent) {
val logoImage = +image(...)
val emailInput = +input(...)
val passwordInput = +input(...)
val loginButton = +button(...)
val registerButton = +button(
id = Id.RegistrationButtonId,
size = WidgetSize.Const(SizeSpec.WrapContent, SizeSpec.Exact(40f)),
text = const("Registration".desc() as StringDesc),
onTap = viewModel::onRegistrationPressed
)
constraints {
// ...
registerButton topToBottom loginButton
registerButton rightToRight root
}
}
DEVELOPMENT
62
code
constraint(size = WidgetSize.AsParent) {
val logoImage = +image(...)
val emailInput = +input(...)
val passwordInput = +input(...)
val loginButton = +button(...)
val registerButton = +button(
id = Id.RegistrationButtonId,
size = WidgetSize.Const(SizeSpec.WrapContent, SizeSpec.Exact(40f)),
text = const("Registration".desc() as StringDesc),
onTap = viewModel::onRegistrationPressed
)
constraints {
// ...
registerButton topToBottom loginButton
registerButton rightToRight root
}
}
DEVELOPMENT
63
code
class LoginScreen(...) : WidgetScreen<Args.Empty>() {
override fun createContentWidget() = ...
object Id {
...
object RegisterButtonId : ButtonWidget.Id
}
}
DEVELOPMENT
64
code
val loginTheme = Theme(theme) {
// …
setButtonFactory(
DefaultButtonWidgetViewFactory(
DefaultButtonWidgetViewFactoryBase.Style(
// ...
)
),
LoginScreen.Id.RegistrationButtonId
)
}
common
DEVELOPMENT
65
code
DefaultButtonWidgetViewFactoryBase.Style(
margins = MarginValues(top = 16f),
padding = platformSpecific(
ios = PaddingValues(start = 16f, end = 16f),
android = null
),
background = StateBackground(
normal = Background(
fill = Fill.Solid(Colors.white),
border = Border(
color = Color(0xF2F2F8FF),
width = 2f
),
shape = Shape.Rectangle(cornerRadius = corners)
),
pressed = Background(...),
disabled = Background(...)
),
textStyle = TextStyle(color = Color(0x777889FF))
)
common
DEVELOPMENT
66
code
DefaultButtonWidgetViewFactoryBase.Style(
margins = MarginValues(top = 16f),
padding = platformSpecific(
ios = PaddingValues(start = 16f, end = 16f),
android = null
),
background = StateBackground(
normal = Background(
fill = Fill.Solid(Colors.white),
border = Border(
color = Color(0xF2F2F8FF),
width = 2f
),
shape = Shape.Rectangle(cornerRadius = corners)
),
pressed = Background(...),
disabled = Background(...)
),
textStyle = TextStyle(color = Color(0x777889FF))
)
common
DEVELOPMENT
67
code
DefaultButtonWidgetViewFactoryBase.Style(
margins = MarginValues(top = 16f),
padding = platformSpecific(
ios = PaddingValues(start = 16f, end = 16f),
android = null
),
background = StateBackground(
normal = Background(
fill = Fill.Solid(Colors.white),
border = Border(
color = Color(0xF2F2F8FF),
width = 2f
),
shape = Shape.Rectangle(cornerRadius = corners)
),
pressed = Background(...),
disabled = Background(...)
),
textStyle = TextStyle(color = Color(0x777889FF))
)
common
DEVELOPMENT
68
code
DefaultButtonWidgetViewFactoryBase.Style(
margins = MarginValues(top = 16f),
padding = platformSpecific(
ios = PaddingValues(start = 16f, end = 16f),
android = null
),
background = StateBackground(
normal = Background(
fill = Fill.Solid(Colors.white),
border = Border(
color = Color(0xF2F2F8FF),
width = 2f
),
shape = Shape.Rectangle(cornerRadius = corners)
),
pressed = Background(...),
disabled = Background(...)
),
textStyle = TextStyle(color = Color(0x777889FF))
)
common
DEVELOPMENT
69
code
DEVELOPMENT
Multiple screens?
70
code
DEVELOPMENT
71
results
DEVELOPMENT
72
results
DEVELOPMENT
73
results
DEVELOPMENT
Lists?
74
code
DEVELOPMENT
75
results
DEVELOPMENT
The basic architecture concepts:
1. compliance with platform rules
2. declare structure, not rendering
3. compile-time safety
4. reactive data handling
76
concepts
DEVELOPMENT
77
concepts
Compliance with platform rules:
1. Activity recreation on Android
2. Save instance state on Android
3. All elements are native (UI/UX)
DEVELOPMENT
78
concepts
Declare structure, not rendering
DEVELOPMENT
Compile-time safety:
1. Type match of WidgetFactory and Widget
2. Child Widgets sizes compile-time checks
3. Widgets Id match to Widget type
4. Arguments in Screens
79
concepts
DEVELOPMENT
80
concepts
Type match of WidgetFactory and Widget
val theme = Theme {
textFactory = DefaultTextWidgetViewFactory()
}
val theme = Theme {
textFactory = DefaultContainerWidgetViewFactory()
}
DEVELOPMENT
81
concepts
Child Widgets sizes compile-time checks
override fun createContentWidget() = with(theme) {
container(size = WidgetSize.AsParent) {
}
}
override fun createContentWidget() = with(theme) {
container(size = WidgetSize.WrapContent) {
}
}
DEVELOPMENT
82
concepts
Child Widgets sizes compile-time checks
fun createContentWidget(): Widget<WidgetSize.Const<SizeSpec.AsParent,
SizeSpec.AsParent>>
DEVELOPMENT
83
concepts
Widgets Id match to Widget type
setContainerFactory(
DefaultContainerWidgetViewFactory(),
RootContainerId
)
setTextFactory(
DefaultTextWidgetViewFactory(),
RootContainerId
)
object RootContainerId: ContainerWidget.Id
DEVELOPMENT
84
concepts
Arguments in Screens
routeToScreen(NoArgsScreen::class)
class NoArgsScreen : WidgetScreen<Args.Empty>()
DEVELOPMENT
85
concepts
Arguments in Screens
routeToScreen(ArgsScreen::class, ArgsScreen.Arg(10))
routeToScreen(ArgsScreen::class)
class ArgsScreen : WidgetScreen<Args.Parcel<ArgsScreen.Arg>>()
DEVELOPMENT
Reactive data handling:
1. One-way binding via LiveData
2. Two-way binding via MutableLiveData
86
concepts
DEVELOPMENT
87
concepts
One-way binding via LiveData
class TimerViewModel : ViewModel() {
val text: LiveData<StringDesc>
}
val viewModel = getViewModel { TimerViewModel() }
container(size = WidgetSize.AsParent) {
center {
text(size = WidgetSize.WrapContent, text = viewModel.text)
}
}
DEVELOPMENT
88
concepts
Two-way binding via MutableLiveData
class InputViewModel : ViewModel() {
val nameField: FormField<String, StringDesc> = FormField("")
}
val viewModel = getViewModel { InputViewModel() }
input(
size = WidgetSize.WidthAsParentHeightWrapContent,
id = Id.NameInput,
label = const(MR.strings.name_label.desc()),
field = viewModel.nameField
)
DEVELOPMENT
89
concepts
Two-way binding via MutableLiveData
class FormField<D, E> {
val data: MutableLiveData<D>
val error: LiveData<E?>
val isValid: LiveData<Boolean>
}
DEVELOPMENT
90
current list of widgets
with(theme) {
constraint(...)
container(...)
linear(...)
scroll(...)
stateful(...)
tabs(...)
clickable(...)
image(...)
text(...)
input(...)
button(...)
list(...)
collection(...)
flatAlert(...)
progressBar(...)
singleChoice(...)
switch(...)
}
DEVELOPMENT
91
current list of widgets
public abstract class Widget<WS : WidgetSize> public constructor() {
public abstract val size: WS
public abstract fun buildView(viewFactoryContext: ViewFactoryContext): ViewBundle<WS>
}
public interface ViewFactory<W : Widget<out WidgetSize>> {
public abstract fun <WS : WidgetSize> build(widget: W, size: WS,
viewFactoryContext: ViewFactoryContext): ViewBundle<WS>
}
DEVELOPMENT
● Widgets declare screens and structure of screens,
but rendering is native
● Jetpack Compose & SwiftUI can be used to render
widgets
92
references
DEVELOPMENT
● 1 kotlin developer can create for both platforms
● natively for developer and customer
● no limits with any modifications (feature, screen, …)
● MVP quickly ≠ must redo natively in the future
93
benefits
DEVELOPMENT
What’s available today (December, 19):
● Base widgets set
● Base navigation patterns
● Base styles for default widget-views
● A set of samples
● Actual version – 0.1.0-dev-5
94
roadmap
DEVELOPMENT
95
December-January:
● Implement in production project
● Check flexibility of API to detect and fix limits – you
can help with it.
Just try self and send feedback to github issues
● Screens actions API design (show toast, alert, route)
● More documentation and samples
roadmap
DEVELOPMENT
February-March:
● Release 0.1.0 (with flexible API for customization)
● Codelabs and Medium posts with details of new
version
96
roadmap
DEVELOPMENT
2020 Q2-Q3:
● New widgets
● More variations of widgets renders
● More styles
● Production usage
97
roadmap
DEVELOPMENT
98
ap@icerock.dev
Try MOKO widgets today =>
@kotlinmpp github/icerockdev

More Related Content

What's hot

Webkit/chromium contribution process
Webkit/chromium contribution processWebkit/chromium contribution process
Webkit/chromium contribution processNAVER LABS
 
Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014biicode
 
Build a lego app with CocoaPods
Build a lego app with CocoaPodsBuild a lego app with CocoaPods
Build a lego app with CocoaPodsCocoaHeads France
 
Deployment Automation with Docker
Deployment Automation with DockerDeployment Automation with Docker
Deployment Automation with DockerEgor Pushkin
 
Operator Framework Overview
Operator Framework OverviewOperator Framework Overview
Operator Framework OverviewRob Szumski
 
SPIFFE Meetup Tokyo #2 - Attestation Internals in SPIRE - Shingo Omura
SPIFFE Meetup Tokyo #2 - Attestation Internals in SPIRE - Shingo OmuraSPIFFE Meetup Tokyo #2 - Attestation Internals in SPIRE - Shingo Omura
SPIFFE Meetup Tokyo #2 - Attestation Internals in SPIRE - Shingo OmuraPreferred Networks
 
Kubernetes - from sketch to production
Kubernetes - from sketch to productionKubernetes - from sketch to production
Kubernetes - from sketch to productionSergio Shevchenko
 
Console Apps: php artisan forthe:win
Console Apps: php artisan forthe:winConsole Apps: php artisan forthe:win
Console Apps: php artisan forthe:winJoe Ferguson
 
Concourse CI Meetup Demo
Concourse CI Meetup DemoConcourse CI Meetup Demo
Concourse CI Meetup DemoToshiaki Maki
 
Google io extended '17 인천
Google io extended '17 인천Google io extended '17 인천
Google io extended '17 인천Pluu love
 
Kubernetes Operators: Rob Szumski
Kubernetes Operators: Rob SzumskiKubernetes Operators: Rob Szumski
Kubernetes Operators: Rob SzumskiRedis Labs
 
Griffon: Re-imaging Desktop Java Technology
Griffon: Re-imaging Desktop Java TechnologyGriffon: Re-imaging Desktop Java Technology
Griffon: Re-imaging Desktop Java TechnologyJames Williams
 
Php Dependency Management with Composer ZendCon 2016
Php Dependency Management with Composer ZendCon 2016Php Dependency Management with Composer ZendCon 2016
Php Dependency Management with Composer ZendCon 2016Clark Everetts
 
Dockerfiles building docker images automatically v (workdir, env, add, and ...
Dockerfiles   building docker images automatically v (workdir, env, add, and ...Dockerfiles   building docker images automatically v (workdir, env, add, and ...
Dockerfiles building docker images automatically v (workdir, env, add, and ...ansonjonel
 
Introduction to Flutter - truly crossplatform, amazingly fast
Introduction to Flutter - truly crossplatform, amazingly fastIntroduction to Flutter - truly crossplatform, amazingly fast
Introduction to Flutter - truly crossplatform, amazingly fastBartosz Kosarzycki
 
Cross-Platform App Development with Flutter, Xamarin, React Native
Cross-Platform App Development with Flutter, Xamarin, React NativeCross-Platform App Development with Flutter, Xamarin, React Native
Cross-Platform App Development with Flutter, Xamarin, React NativeKorhan Bircan
 
Automating stateful applications with kubernetes operators - Openstack Summit...
Automating stateful applications with kubernetes operators - Openstack Summit...Automating stateful applications with kubernetes operators - Openstack Summit...
Automating stateful applications with kubernetes operators - Openstack Summit...Jorge Morales
 

What's hot (20)

Webkit/chromium contribution process
Webkit/chromium contribution processWebkit/chromium contribution process
Webkit/chromium contribution process
 
Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014
 
Build a lego app with CocoaPods
Build a lego app with CocoaPodsBuild a lego app with CocoaPods
Build a lego app with CocoaPods
 
Deployment Automation with Docker
Deployment Automation with DockerDeployment Automation with Docker
Deployment Automation with Docker
 
Operator Framework Overview
Operator Framework OverviewOperator Framework Overview
Operator Framework Overview
 
SPIFFE Meetup Tokyo #2 - Attestation Internals in SPIRE - Shingo Omura
SPIFFE Meetup Tokyo #2 - Attestation Internals in SPIRE - Shingo OmuraSPIFFE Meetup Tokyo #2 - Attestation Internals in SPIRE - Shingo Omura
SPIFFE Meetup Tokyo #2 - Attestation Internals in SPIRE - Shingo Omura
 
Kubernetes - from sketch to production
Kubernetes - from sketch to productionKubernetes - from sketch to production
Kubernetes - from sketch to production
 
Console Apps: php artisan forthe:win
Console Apps: php artisan forthe:winConsole Apps: php artisan forthe:win
Console Apps: php artisan forthe:win
 
React Ecosystem
React EcosystemReact Ecosystem
React Ecosystem
 
Concourse CI Meetup Demo
Concourse CI Meetup DemoConcourse CI Meetup Demo
Concourse CI Meetup Demo
 
Google io extended '17 인천
Google io extended '17 인천Google io extended '17 인천
Google io extended '17 인천
 
Kubernetes Operators: Rob Szumski
Kubernetes Operators: Rob SzumskiKubernetes Operators: Rob Szumski
Kubernetes Operators: Rob Szumski
 
Griffon: Re-imaging Desktop Java Technology
Griffon: Re-imaging Desktop Java TechnologyGriffon: Re-imaging Desktop Java Technology
Griffon: Re-imaging Desktop Java Technology
 
Php Dependency Management with Composer ZendCon 2016
Php Dependency Management with Composer ZendCon 2016Php Dependency Management with Composer ZendCon 2016
Php Dependency Management with Composer ZendCon 2016
 
Dockerfiles building docker images automatically v (workdir, env, add, and ...
Dockerfiles   building docker images automatically v (workdir, env, add, and ...Dockerfiles   building docker images automatically v (workdir, env, add, and ...
Dockerfiles building docker images automatically v (workdir, env, add, and ...
 
Composer
ComposerComposer
Composer
 
Introduction to Flutter - truly crossplatform, amazingly fast
Introduction to Flutter - truly crossplatform, amazingly fastIntroduction to Flutter - truly crossplatform, amazingly fast
Introduction to Flutter - truly crossplatform, amazingly fast
 
Cross-Platform App Development with Flutter, Xamarin, React Native
Cross-Platform App Development with Flutter, Xamarin, React NativeCross-Platform App Development with Flutter, Xamarin, React Native
Cross-Platform App Development with Flutter, Xamarin, React Native
 
Automating stateful applications with kubernetes operators - Openstack Summit...
Automating stateful applications with kubernetes operators - Openstack Summit...Automating stateful applications with kubernetes operators - Openstack Summit...
Automating stateful applications with kubernetes operators - Openstack Summit...
 
Pc54
Pc54Pc54
Pc54
 

Similar to Kotlin Multiplatform in Action - Alexandr Pogrebnyak - IceRockDev

Intro to Eclipse Che, by Tyler Jewell
Intro to Eclipse Che, by Tyler JewellIntro to Eclipse Che, by Tyler Jewell
Intro to Eclipse Che, by Tyler Jewelljwi11iams
 
FooConf23_Bringing the cloud back down to earth.pptx
FooConf23_Bringing the cloud back down to earth.pptxFooConf23_Bringing the cloud back down to earth.pptx
FooConf23_Bringing the cloud back down to earth.pptxGrace Jansen
 
Mobile Web Apps and the Intel® XDK
Mobile Web Apps and the Intel® XDKMobile Web Apps and the Intel® XDK
Mobile Web Apps and the Intel® XDKIntel® Software
 
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023Nicolas HAAN
 
Jfokus_Bringing the cloud back down to earth.pptx
Jfokus_Bringing the cloud back down to earth.pptxJfokus_Bringing the cloud back down to earth.pptx
Jfokus_Bringing the cloud back down to earth.pptxGrace Jansen
 
SwissJUG_Bringing the cloud back down to earth.pptx
SwissJUG_Bringing the cloud back down to earth.pptxSwissJUG_Bringing the cloud back down to earth.pptx
SwissJUG_Bringing the cloud back down to earth.pptxGrace Jansen
 
Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)
Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)
Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)Derek Lee Boire
 
Tell Me Quando - Implementing Feature Flags
Tell Me Quando - Implementing Feature FlagsTell Me Quando - Implementing Feature Flags
Tell Me Quando - Implementing Feature FlagsJorge Ortiz
 
Rapid and Reliable Developing with HTML5 & GWT
Rapid and Reliable Developing with HTML5 & GWTRapid and Reliable Developing with HTML5 & GWT
Rapid and Reliable Developing with HTML5 & GWTManuel Carrasco Moñino
 
Developing for BlackBerry 10 – Tools and SDKs by Luca Filigheddu
 Developing for BlackBerry 10 – Tools and SDKs by Luca Filigheddu Developing for BlackBerry 10 – Tools and SDKs by Luca Filigheddu
Developing for BlackBerry 10 – Tools and SDKs by Luca FilighedduCodemotion
 
Visage Android Hands-on Lab
Visage Android Hands-on LabVisage Android Hands-on Lab
Visage Android Hands-on LabStephen Chin
 
TK2323 Lecture 1 - Introduction to Mobile Application.pdf
TK2323 Lecture 1 - Introduction to Mobile Application.pdfTK2323 Lecture 1 - Introduction to Mobile Application.pdf
TK2323 Lecture 1 - Introduction to Mobile Application.pdfLam Chun
 
whats-new-netbeans-ide-7x.pptx
whats-new-netbeans-ide-7x.pptxwhats-new-netbeans-ide-7x.pptx
whats-new-netbeans-ide-7x.pptxGabrielSoche
 
005528214.pdf
005528214.pdf005528214.pdf
005528214.pdfEidTahir
 
[20200720]cloud native develoment - Nelson Lin
[20200720]cloud native develoment - Nelson Lin[20200720]cloud native develoment - Nelson Lin
[20200720]cloud native develoment - Nelson LinHanLing Shen
 
PittsburgJUG_Cloud-Native Dev Tools: Bringing the cloud back to earth
PittsburgJUG_Cloud-Native Dev Tools: Bringing the cloud back to earthPittsburgJUG_Cloud-Native Dev Tools: Bringing the cloud back to earth
PittsburgJUG_Cloud-Native Dev Tools: Bringing the cloud back to earthGrace Jansen
 
iOS Development Survival Guide for the .NET Guy
iOS Development Survival Guide for the .NET GuyiOS Development Survival Guide for the .NET Guy
iOS Development Survival Guide for the .NET GuyNick Landry
 
Getting started with the NDK
Getting started with the NDKGetting started with the NDK
Getting started with the NDKKirill Kounik
 
Kotlin native for iOS and Android
Kotlin native for iOS and AndroidKotlin native for iOS and Android
Kotlin native for iOS and AndroidShady Selim
 

Similar to Kotlin Multiplatform in Action - Alexandr Pogrebnyak - IceRockDev (20)

Intro to Eclipse Che, by Tyler Jewell
Intro to Eclipse Che, by Tyler JewellIntro to Eclipse Che, by Tyler Jewell
Intro to Eclipse Che, by Tyler Jewell
 
FooConf23_Bringing the cloud back down to earth.pptx
FooConf23_Bringing the cloud back down to earth.pptxFooConf23_Bringing the cloud back down to earth.pptx
FooConf23_Bringing the cloud back down to earth.pptx
 
Mobile Web Apps and the Intel® XDK
Mobile Web Apps and the Intel® XDKMobile Web Apps and the Intel® XDK
Mobile Web Apps and the Intel® XDK
 
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
 
Jfokus_Bringing the cloud back down to earth.pptx
Jfokus_Bringing the cloud back down to earth.pptxJfokus_Bringing the cloud back down to earth.pptx
Jfokus_Bringing the cloud back down to earth.pptx
 
SwissJUG_Bringing the cloud back down to earth.pptx
SwissJUG_Bringing the cloud back down to earth.pptxSwissJUG_Bringing the cloud back down to earth.pptx
SwissJUG_Bringing the cloud back down to earth.pptx
 
Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)
Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)
Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)
 
Tell Me Quando - Implementing Feature Flags
Tell Me Quando - Implementing Feature FlagsTell Me Quando - Implementing Feature Flags
Tell Me Quando - Implementing Feature Flags
 
Rapid and Reliable Developing with HTML5 & GWT
Rapid and Reliable Developing with HTML5 & GWTRapid and Reliable Developing with HTML5 & GWT
Rapid and Reliable Developing with HTML5 & GWT
 
Developing for BlackBerry 10 – Tools and SDKs by Luca Filigheddu
 Developing for BlackBerry 10 – Tools and SDKs by Luca Filigheddu Developing for BlackBerry 10 – Tools and SDKs by Luca Filigheddu
Developing for BlackBerry 10 – Tools and SDKs by Luca Filigheddu
 
Visage Android Hands-on Lab
Visage Android Hands-on LabVisage Android Hands-on Lab
Visage Android Hands-on Lab
 
TK2323 Lecture 1 - Introduction to Mobile Application.pdf
TK2323 Lecture 1 - Introduction to Mobile Application.pdfTK2323 Lecture 1 - Introduction to Mobile Application.pdf
TK2323 Lecture 1 - Introduction to Mobile Application.pdf
 
whats-new-netbeans-ide-7x.pptx
whats-new-netbeans-ide-7x.pptxwhats-new-netbeans-ide-7x.pptx
whats-new-netbeans-ide-7x.pptx
 
005528214.pdf
005528214.pdf005528214.pdf
005528214.pdf
 
[20200720]cloud native develoment - Nelson Lin
[20200720]cloud native develoment - Nelson Lin[20200720]cloud native develoment - Nelson Lin
[20200720]cloud native develoment - Nelson Lin
 
PittsburgJUG_Cloud-Native Dev Tools: Bringing the cloud back to earth
PittsburgJUG_Cloud-Native Dev Tools: Bringing the cloud back to earthPittsburgJUG_Cloud-Native Dev Tools: Bringing the cloud back to earth
PittsburgJUG_Cloud-Native Dev Tools: Bringing the cloud back to earth
 
gopaddle-meetup
gopaddle-meetupgopaddle-meetup
gopaddle-meetup
 
iOS Development Survival Guide for the .NET Guy
iOS Development Survival Guide for the .NET GuyiOS Development Survival Guide for the .NET Guy
iOS Development Survival Guide for the .NET Guy
 
Getting started with the NDK
Getting started with the NDKGetting started with the NDK
Getting started with the NDK
 
Kotlin native for iOS and Android
Kotlin native for iOS and AndroidKotlin native for iOS and Android
Kotlin native for iOS and Android
 

More from DroidConTLV

Mobile Development in the Information Age - Yossi Elkrief, Nike
Mobile Development in the Information Age - Yossi Elkrief, NikeMobile Development in the Information Age - Yossi Elkrief, Nike
Mobile Development in the Information Age - Yossi Elkrief, NikeDroidConTLV
 
Doing work in the background - Darryn Campbell, Zebra Technologies
Doing work in the background - Darryn Campbell, Zebra TechnologiesDoing work in the background - Darryn Campbell, Zebra Technologies
Doing work in the background - Darryn Campbell, Zebra TechnologiesDroidConTLV
 
No more video loss - Alex Rivkin, Motorola Solutions
No more video loss - Alex Rivkin, Motorola SolutionsNo more video loss - Alex Rivkin, Motorola Solutions
No more video loss - Alex Rivkin, Motorola SolutionsDroidConTLV
 
Mobile at Scale: from startup to a big company - Dor Samet, Booking.com
Mobile at Scale: from startup to a big company - Dor Samet, Booking.comMobile at Scale: from startup to a big company - Dor Samet, Booking.com
Mobile at Scale: from startup to a big company - Dor Samet, Booking.comDroidConTLV
 
LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell
LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, ClimacellLiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell
LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, ClimacellDroidConTLV
 
MVVM In real life - Lea Cohen Tannoudji, Lightricks
MVVM In real life - Lea Cohen Tannoudji, LightricksMVVM In real life - Lea Cohen Tannoudji, Lightricks
MVVM In real life - Lea Cohen Tannoudji, LightricksDroidConTLV
 
Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)
Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)
Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)DroidConTLV
 
Building Apps with Flutter - Hillel Coren, Invoice Ninja
Building Apps with Flutter - Hillel Coren, Invoice NinjaBuilding Apps with Flutter - Hillel Coren, Invoice Ninja
Building Apps with Flutter - Hillel Coren, Invoice NinjaDroidConTLV
 
New Android Project: The Most Important Decisions - Vasiliy Zukanov
New Android Project: The Most Important Decisions - Vasiliy ZukanovNew Android Project: The Most Important Decisions - Vasiliy Zukanov
New Android Project: The Most Important Decisions - Vasiliy ZukanovDroidConTLV
 
Designing a Design System - Shai Mishali, Gett
Designing a Design System - Shai Mishali, GettDesigning a Design System - Shai Mishali, Gett
Designing a Design System - Shai Mishali, GettDroidConTLV
 
The Mighty Power of the Accessibility Service - Guy Griv, Pepper
The Mighty Power of the Accessibility Service - Guy Griv, PepperThe Mighty Power of the Accessibility Service - Guy Griv, Pepper
The Mighty Power of the Accessibility Service - Guy Griv, PepperDroidConTLV
 
Flutter State Management - Moti Bartov, Tikal
Flutter State Management - Moti Bartov, TikalFlutter State Management - Moti Bartov, Tikal
Flutter State Management - Moti Bartov, TikalDroidConTLV
 
Reactive UI in android - Gil Goldzweig Goldbaum, 10bis
Reactive UI in android - Gil Goldzweig Goldbaum, 10bisReactive UI in android - Gil Goldzweig Goldbaum, 10bis
Reactive UI in android - Gil Goldzweig Goldbaum, 10bisDroidConTLV
 
Fun with flutter animations - Divyanshu Bhargava, GoHighLevel
Fun with flutter animations - Divyanshu Bhargava, GoHighLevelFun with flutter animations - Divyanshu Bhargava, GoHighLevel
Fun with flutter animations - Divyanshu Bhargava, GoHighLevelDroidConTLV
 
DroidconTLV 2019
DroidconTLV 2019DroidconTLV 2019
DroidconTLV 2019DroidConTLV
 
Ok google, it's time to bot! - Hadar Franco, Albert + Stav Levi, Monday
Ok google, it's time to bot! - Hadar Franco, Albert + Stav Levi, MondayOk google, it's time to bot! - Hadar Franco, Albert + Stav Levi, Monday
Ok google, it's time to bot! - Hadar Franco, Albert + Stav Levi, MondayDroidConTLV
 
Introduction to React Native - Lev Vidrak, Wix
Introduction to React Native - Lev Vidrak, WixIntroduction to React Native - Lev Vidrak, Wix
Introduction to React Native - Lev Vidrak, WixDroidConTLV
 
Bang-Bang, you have been hacked - Yonatan Levin, KolGene
Bang-Bang, you have been hacked - Yonatan Levin, KolGeneBang-Bang, you have been hacked - Yonatan Levin, KolGene
Bang-Bang, you have been hacked - Yonatan Levin, KolGeneDroidConTLV
 
Educating your app – adding ML edge to your apps - Maoz Tamir
Educating your app – adding ML edge to your apps - Maoz TamirEducating your app – adding ML edge to your apps - Maoz Tamir
Educating your app – adding ML edge to your apps - Maoz TamirDroidConTLV
 
Constraint-ly motion - making your app dance - John Hoford, Google
Constraint-ly motion - making your app dance - John Hoford, GoogleConstraint-ly motion - making your app dance - John Hoford, Google
Constraint-ly motion - making your app dance - John Hoford, GoogleDroidConTLV
 

More from DroidConTLV (20)

Mobile Development in the Information Age - Yossi Elkrief, Nike
Mobile Development in the Information Age - Yossi Elkrief, NikeMobile Development in the Information Age - Yossi Elkrief, Nike
Mobile Development in the Information Age - Yossi Elkrief, Nike
 
Doing work in the background - Darryn Campbell, Zebra Technologies
Doing work in the background - Darryn Campbell, Zebra TechnologiesDoing work in the background - Darryn Campbell, Zebra Technologies
Doing work in the background - Darryn Campbell, Zebra Technologies
 
No more video loss - Alex Rivkin, Motorola Solutions
No more video loss - Alex Rivkin, Motorola SolutionsNo more video loss - Alex Rivkin, Motorola Solutions
No more video loss - Alex Rivkin, Motorola Solutions
 
Mobile at Scale: from startup to a big company - Dor Samet, Booking.com
Mobile at Scale: from startup to a big company - Dor Samet, Booking.comMobile at Scale: from startup to a big company - Dor Samet, Booking.com
Mobile at Scale: from startup to a big company - Dor Samet, Booking.com
 
LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell
LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, ClimacellLiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell
LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell
 
MVVM In real life - Lea Cohen Tannoudji, Lightricks
MVVM In real life - Lea Cohen Tannoudji, LightricksMVVM In real life - Lea Cohen Tannoudji, Lightricks
MVVM In real life - Lea Cohen Tannoudji, Lightricks
 
Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)
Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)
Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)
 
Building Apps with Flutter - Hillel Coren, Invoice Ninja
Building Apps with Flutter - Hillel Coren, Invoice NinjaBuilding Apps with Flutter - Hillel Coren, Invoice Ninja
Building Apps with Flutter - Hillel Coren, Invoice Ninja
 
New Android Project: The Most Important Decisions - Vasiliy Zukanov
New Android Project: The Most Important Decisions - Vasiliy ZukanovNew Android Project: The Most Important Decisions - Vasiliy Zukanov
New Android Project: The Most Important Decisions - Vasiliy Zukanov
 
Designing a Design System - Shai Mishali, Gett
Designing a Design System - Shai Mishali, GettDesigning a Design System - Shai Mishali, Gett
Designing a Design System - Shai Mishali, Gett
 
The Mighty Power of the Accessibility Service - Guy Griv, Pepper
The Mighty Power of the Accessibility Service - Guy Griv, PepperThe Mighty Power of the Accessibility Service - Guy Griv, Pepper
The Mighty Power of the Accessibility Service - Guy Griv, Pepper
 
Flutter State Management - Moti Bartov, Tikal
Flutter State Management - Moti Bartov, TikalFlutter State Management - Moti Bartov, Tikal
Flutter State Management - Moti Bartov, Tikal
 
Reactive UI in android - Gil Goldzweig Goldbaum, 10bis
Reactive UI in android - Gil Goldzweig Goldbaum, 10bisReactive UI in android - Gil Goldzweig Goldbaum, 10bis
Reactive UI in android - Gil Goldzweig Goldbaum, 10bis
 
Fun with flutter animations - Divyanshu Bhargava, GoHighLevel
Fun with flutter animations - Divyanshu Bhargava, GoHighLevelFun with flutter animations - Divyanshu Bhargava, GoHighLevel
Fun with flutter animations - Divyanshu Bhargava, GoHighLevel
 
DroidconTLV 2019
DroidconTLV 2019DroidconTLV 2019
DroidconTLV 2019
 
Ok google, it's time to bot! - Hadar Franco, Albert + Stav Levi, Monday
Ok google, it's time to bot! - Hadar Franco, Albert + Stav Levi, MondayOk google, it's time to bot! - Hadar Franco, Albert + Stav Levi, Monday
Ok google, it's time to bot! - Hadar Franco, Albert + Stav Levi, Monday
 
Introduction to React Native - Lev Vidrak, Wix
Introduction to React Native - Lev Vidrak, WixIntroduction to React Native - Lev Vidrak, Wix
Introduction to React Native - Lev Vidrak, Wix
 
Bang-Bang, you have been hacked - Yonatan Levin, KolGene
Bang-Bang, you have been hacked - Yonatan Levin, KolGeneBang-Bang, you have been hacked - Yonatan Levin, KolGene
Bang-Bang, you have been hacked - Yonatan Levin, KolGene
 
Educating your app – adding ML edge to your apps - Maoz Tamir
Educating your app – adding ML edge to your apps - Maoz TamirEducating your app – adding ML edge to your apps - Maoz Tamir
Educating your app – adding ML edge to your apps - Maoz Tamir
 
Constraint-ly motion - making your app dance - John Hoford, Google
Constraint-ly motion - making your app dance - John Hoford, GoogleConstraint-ly motion - making your app dance - John Hoford, Google
Constraint-ly motion - making your app dance - John Hoford, Google
 

Recently uploaded

AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDGMarianaLemus7
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piececharlottematthew16
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
costume and set research powerpoint presentation
costume and set research powerpoint presentationcostume and set research powerpoint presentation
costume and set research powerpoint presentationphoebematthew05
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clashcharlottematthew16
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsMiki Katsuragi
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 

Recently uploaded (20)

AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDG
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piece
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
costume and set research powerpoint presentation
costume and set research powerpoint presentationcostume and set research powerpoint presentation
costume and set research powerpoint presentation
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clash
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering Tips
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 

Kotlin Multiplatform in Action - Alexandr Pogrebnyak - IceRockDev

  • 1. DEVELOPMENT 1 Kotlin Multiplatform in Action Alexandr Pogrebnyak Droidcon Tel Aviv 19.12.19.
  • 2. DEVELOPMENT ● About us ● What is Kotlin Multiplatform? ● Our experience in Kotlin Multiplatform ● moko.icerock.dev – a set of multiplatform libraries ● moko-widgets 2
  • 3. DEVELOPMENT ● 5 years in mobile development ● 80+ mobile projects (iOS & Android) ● 40+ developers in 2 offices 3
  • 4. DEVELOPMENT Focus on: 4 Avoid logic reduplication and keep UI native
  • 5. DEVELOPMENT 5 Code Sharing in IceRock Flutter React Native J2ObjC New programming language Integration with OS via middle layer Non-native UI Legacy tech stacks Asynchronous work with OS Need to adopt Java-code to ObjC Native UI Hot reload
  • 6. DEVELOPMENT 6 Kotlin Multiplatform + Kotlin/Native Code Sharing at IceRock Kotlin looks like Swift Inner libraries already in Kotlin Shift to native env at any moment Familiar programming language Native UI Full access to Android OS and iOS features and SDKs There are some limitations in Kotlin > Swift direction Devs worry about integration
  • 7. DEVELOPMENT 7 Kotlin Multiplatform at IceRock Kotlin MPP and Kotlin/Native since July 2018 1 year achievement Reduce time and cost for both platforms by 1.5x 1.5x speed 50% of shared code on average 50:50 Easy learning curve for Android and iOS developers Easy to learn, hard to master
  • 8. DEVELOPMENT 8 Kotlin Multiplatform Android Windows macOS Linux Java Java Script WebAssemblyiOS
  • 9. DEVELOPMENT 9 expect class Logger(tag: String) { fun log(string: String) } fun main() { val logger = Logger("MPP") logger.log("hello world!") } commonMain
  • 10. DEVELOPMENT 10 iosMain import platform.Foundation.NSLog actual class Logger actual constructor(private val tag: String) { actual fun log(string: String) { NSLog("[$tag] $string") } } import android.util.Log actual class Logger actual constructor(private val tag: String) { actual fun log(string: String) { Log.v(tag, string) } } androidMain
  • 11. DEVELOPMENT 11 androidMain └── kotlin └── com.icerockdev.library └── Logger.kt commonMain └── kotlin └── com.icerockdev.library └── Logger.kt iosMain └── kotlin └── com.icerockdev.library └── Logger.kt Kotlin Multiplatform
  • 12. DEVELOPMENT 12 Kotlin Multiplatform - past View AuthActivity AuthViewController ProfileViewController ProfileActivity Presentation AuthViewModel ProfileViewModel PostsViewModel AddPostViewModel Domain UserInteractor PostsInteractor Validator Payment Data User Post UserRepository PostRepository RestUser RestPost UserApi PostApi
  • 13. DEVELOPMENT 13 Kotlin Multiplatform - now View AuthScreen ProfileScreen Presentation AuthViewModel ProfileViewModel PostsViewModel Domain UserInteractor PostsInteractor Validator Data User Post UserRepository RestUser RestPost UserApi powered by
  • 14. DEVELOPMENT 14 Shared Code in a Typical Project - past Shared code 62% Android-specific 16% iOS-specific 22%
  • 15. DEVELOPMENT 15 Shared Code in a Typical Project - now Android-specific 3% iOS-specific 2% Shared code 95% powered by
  • 21. DEVELOPMENT 21 A moko-template (sample project) https://github.com/icerockdev/moko-template.git Features: Install: git clone Done! app_name app_icon applicationId ● Shared business logic ● Kotlin Gradle DSL ● Modular-based architecture ● Independent feature and domain modules ● ViewModels, LiveData, Resource management, Runtime permissions access, Media access, UI from shared code, Network layer generation from OpenAPI…
  • 23. DEVELOPMENT Goal #1 1 Kotlin developer => 2 native mobile apps 23 goals
  • 24. DEVELOPMENT Goal #2 Fast start = the codebase should grow without rewrites 24 goals
  • 25. DEVELOPMENT The basic architecture concepts: 1. compliance with platform rules 2. declare structure, not rendering 3. compile-time safety 4. reactive data handling 25 concepts
  • 26. DEVELOPMENT 26 code class App : BaseApplication() { override fun setup() { val theme = Theme() registerScreenFactory(MainScreen::class) { MainScreen(theme) } } override fun getRootScreen(): KClass<out Screen<Args.Empty>> { return MainScreen::class } } common
  • 27. DEVELOPMENT 27 code class MainApplication : Application() { override fun onCreate() { super.onCreate() mppApplication = App().apply { setup() } } companion object { lateinit var mppApplication: App } } class MainActivity : HostActivity() { override val application: BaseApplication get() = MainApplication.mppApplication } android
  • 28. DEVELOPMENT 28 code @UIApplicationMain class AppDelegate: NSObject, UIApplicationDelegate { var window: UIWindow? func application(_ application: ..., didFinishLaunchingWithOptions ...) -> Bool { let app = App() app.setup() let screen = app.createRootScreen() let rootViewController = screen.createViewController() window = UIWindow(frame: UIScreen.main.bounds) window?.rootViewController = rootViewController window?.makeKeyAndVisible() return true } } ios
  • 29. DEVELOPMENT 29 code class MainScreen( private val theme: Theme ) : WidgetScreen<Args.Empty>() { override fun createContentWidget() = with(theme) { container(size = WidgetSize.AsParent) { center { text( size = WidgetSize.WrapContent, text = const(MR.strings.hello_world.desc()) ) } } } } common
  • 30. DEVELOPMENT 30 code class MainScreen( private val theme: Theme ) : WidgetScreen<Args.Empty>() { override fun createContentWidget() = with(theme) { container(size = WidgetSize.AsParent) { center { text( size = WidgetSize.WrapContent, text = const(MR.strings.hello_world.desc()) ) } } } } common
  • 31. DEVELOPMENT 31 code class MainScreen( private val theme: Theme ) : WidgetScreen<Args.Empty>() { override fun createContentWidget() = with(theme) { container(size = WidgetSize.AsParent) { center { text( size = WidgetSize.WrapContent, text = const(MR.strings.hello_world.desc()) ) } } } } common
  • 32. DEVELOPMENT 32 code class MainScreen( private val theme: Theme ) : WidgetScreen<Args.Empty>() { override fun createContentWidget() = with(theme) { container(size = WidgetSize.AsParent) { center { text( size = WidgetSize.WrapContent, text = const(MR.strings.hello_world.desc()) ) } } } } common
  • 33. DEVELOPMENT 33 code class MainScreen( private val theme: Theme ) : WidgetScreen<Args.Empty>() { override fun createContentWidget() = with(theme) { container(size = WidgetSize.AsParent) { center { text( size = WidgetSize.WrapContent, text = const(MR.strings.hello_world.desc()) ) } } } } common
  • 34. DEVELOPMENT 34 code class MainScreen( private val theme: Theme ) : WidgetScreen<Args.Empty>() { override fun createContentWidget() = with(theme) { container(size = WidgetSize.AsParent) { center { text( size = WidgetSize.WrapContent, text = const(MR.strings.hello_world.desc()) ) } } } } common
  • 36. DEVELOPMENT 36 code class App : BaseApplication() { override fun setup() { val theme = Theme { // custom styles here } registerScreenFactory(MainScreen::class) { MainScreen(theme) } } override fun getRootScreen(): KClass<out Screen<Args.Empty>> { return MainScreen::class } } common
  • 37. DEVELOPMENT 37 code val theme = Theme { textFactory = DefaultTextWidgetViewFactory( DefaultTextWidgetViewFactoryBase.Style( textStyle = TextStyle( size = 24, color = Colors.black ), padding = PaddingValues(padding = 16f) ) ) } common
  • 38. DEVELOPMENT 38 code val theme = Theme { textFactory = DefaultTextWidgetViewFactory( DefaultTextWidgetViewFactoryBase.Style( textStyle = TextStyle( size = 24, color = Colors.black ), padding = PaddingValues(padding = 16f) ) ) } common
  • 39. DEVELOPMENT 39 code val theme = Theme { textFactory = DefaultTextWidgetViewFactory( DefaultTextWidgetViewFactoryBase.Style( textStyle = TextStyle( size = 24, color = Colors.black ), padding = PaddingValues(padding = 16f) ) ) } common
  • 41. DEVELOPMENT Simple is simple. How about some complexity? 41 code
  • 44. DEVELOPMENT 44 code class LoginScreen( private val theme: Theme ) : WidgetScreen<Args.Empty>() { override fun createContentWidget() = with(theme) { constraint(size = WidgetSize.AsParent) { // ... } } } common
  • 45. DEVELOPMENT 45 code override fun createContentWidget() = with(theme) { constraint(size = WidgetSize.AsParent) { val logoImage = +image( size = WidgetSize.Const(SizeSpec.WrapContent, SizeSpec.WrapContent), image = const(Image.resource(MR.images.logo)) ) } } common
  • 46. DEVELOPMENT 46 code constraint(size = WidgetSize.AsParent) { val logoImage = +image(...) val emailInput = +input( size = WidgetSize.WidthAsParentHeightWrapContent, id = Id.EmailInputId, label = const("Email".desc() as StringDesc), field = viewModel.emailField ) val passwordInput = +input( size = WidgetSize.WidthAsParentHeightWrapContent, id = Id.PasswordInputId, label = const("Password".desc() as StringDesc), field = viewModel.passwordField ) } common
  • 47. DEVELOPMENT 47 code constraint(size = WidgetSize.AsParent) { val logoImage = +image(...) val emailInput = +input(...) val passwordInput = +input(...) val loginButton = +button( size = WidgetSize.Const(SizeSpec.AsParent, SizeSpec.Exact(50f)), text = const("Login".desc() as StringDesc), onTap = viewModel::onLoginPressed ) } common
  • 48. DEVELOPMENT 48 code constraint(size = WidgetSize.AsParent) { val logoImage = +image(...) val emailInput = +input(...) val passwordInput = +input(...) val loginButton = +button(...) constraints { passwordInput centerYToCenterY root passwordInput leftRightToLeftRight root emailInput bottomToTop passwordInput emailInput leftRightToLeftRight root loginButton topToBottom passwordInput loginButton leftRightToLeftRight root logoImage centerXToCenterX root logoImage.verticalCenterBetween( top = root.top, bottom = emailInput.top ) } common
  • 49. DEVELOPMENT 49 code constraint(size = WidgetSize.AsParent) { val logoImage = +image(...) val emailInput = +input(...) val passwordInput = +input(...) val loginButton = +button(...) constraints { passwordInput centerYToCenterY root passwordInput leftRightToLeftRight root emailInput bottomToTop passwordInput emailInput leftRightToLeftRight root loginButton topToBottom passwordInput loginButton leftRightToLeftRight root logoImage centerXToCenterX root logoImage.verticalCenterBetween( top = root.top, bottom = emailInput.top ) } common
  • 50. DEVELOPMENT 50 code constraint(size = WidgetSize.AsParent) { val logoImage = +image(...) val emailInput = +input(...) val passwordInput = +input(...) val loginButton = +button(...) constraints { passwordInput centerYToCenterY root passwordInput leftRightToLeftRight root emailInput bottomToTop passwordInput emailInput leftRightToLeftRight root loginButton topToBottom passwordInput loginButton leftRightToLeftRight root logoImage centerXToCenterX root logoImage.verticalCenterBetween( top = root.top, bottom = emailInput.top ) } common
  • 51. DEVELOPMENT 51 code constraint(size = WidgetSize.AsParent) { val logoImage = +image(...) val emailInput = +input(...) val passwordInput = +input(...) val loginButton = +button(...) constraints { passwordInput centerYToCenterY root passwordInput leftRightToLeftRight root emailInput bottomToTop passwordInput emailInput leftRightToLeftRight root loginButton topToBottom passwordInput loginButton leftRightToLeftRight root logoImage centerXToCenterX root logoImage.verticalCenterBetween( top = root.top, bottom = emailInput.top ) } common
  • 54. DEVELOPMENT 54 code val loginTheme = Theme(theme) { constraintFactory = DefaultConstraintWidgetViewFactory( DefaultConstraintWidgetViewFactoryBase.Style( padding = PaddingValues(16f), background = Background( fill = Fill.Solid(Colors.white) ) ) ) } common
  • 55. DEVELOPMENT 55 code val loginTheme = Theme(theme) { constraintFactory = DefaultConstraintWidgetViewFactory(...) imageFactory = DefaultImageWidgetViewFactory( DefaultImageWidgetViewFactoryBase.Style( scaleType = DefaultImageWidgetViewFactoryBase.ScaleType.FIT ) ) } common
  • 56. DEVELOPMENT 56 code val loginTheme = Theme(theme) { constraintFactory = DefaultConstraintWidgetViewFactory(...) imageFactory = DefaultImageWidgetViewFactory(...) val corners = platformSpecific(android = 8f, ios = 25f) inputFactory = DefaultInputWidgetViewFactory( DefaultInputWidgetViewFactoryBase.Style( margins = MarginValues(bottom = 8f), underLineColor = Color(0xe5e6eeFF), labelTextStyle = TextStyle( color = Color(0x777889FF) ) ) ) } common
  • 57. DEVELOPMENT 57 code val loginTheme = Theme(theme) { constraintFactory = DefaultConstraintWidgetViewFactory(...) imageFactory = DefaultImageWidgetViewFactory(...) val corners = platformSpecific(android = 16f, ios = 25f) inputFactory = DefaultInputWidgetViewFactory( DefaultInputWidgetViewFactoryBase.Style( margins = MarginValues(bottom = 8f), underLineColor = Color(0xe5e6eeFF), labelTextStyle = TextStyle( color = Color(0x777889FF) ) ) ) } common
  • 58. DEVELOPMENT 58 code val loginTheme = Theme(theme) { constraintFactory = DefaultConstraintWidgetViewFactory(...) imageFactory = DefaultImageWidgetViewFactory(...) val corners = ... inputFactory = DefaultInputWidgetViewFactory(...) buttonFactory = DefaultButtonWidgetViewFactory( DefaultButtonWidgetViewFactoryBase.Style( margins = MarginValues(top = 32f), background = StateBackground( normal = Background( fill = Fill.Solid(Color(0x6770e0FF)), shape = Shape.Rectangle(cornerRadius = corners) ), pressed = Background(...), disabled = Background(...) ), textStyle = TextStyle(color = Colors.white) ) ) } common
  • 61. DEVELOPMENT 61 code constraint(size = WidgetSize.AsParent) { val logoImage = +image(...) val emailInput = +input(...) val passwordInput = +input(...) val loginButton = +button(...) val registerButton = +button( id = Id.RegistrationButtonId, size = WidgetSize.Const(SizeSpec.WrapContent, SizeSpec.Exact(40f)), text = const("Registration".desc() as StringDesc), onTap = viewModel::onRegistrationPressed ) constraints { // ... registerButton topToBottom loginButton registerButton rightToRight root } }
  • 62. DEVELOPMENT 62 code constraint(size = WidgetSize.AsParent) { val logoImage = +image(...) val emailInput = +input(...) val passwordInput = +input(...) val loginButton = +button(...) val registerButton = +button( id = Id.RegistrationButtonId, size = WidgetSize.Const(SizeSpec.WrapContent, SizeSpec.Exact(40f)), text = const("Registration".desc() as StringDesc), onTap = viewModel::onRegistrationPressed ) constraints { // ... registerButton topToBottom loginButton registerButton rightToRight root } }
  • 63. DEVELOPMENT 63 code class LoginScreen(...) : WidgetScreen<Args.Empty>() { override fun createContentWidget() = ... object Id { ... object RegisterButtonId : ButtonWidget.Id } }
  • 64. DEVELOPMENT 64 code val loginTheme = Theme(theme) { // … setButtonFactory( DefaultButtonWidgetViewFactory( DefaultButtonWidgetViewFactoryBase.Style( // ... ) ), LoginScreen.Id.RegistrationButtonId ) } common
  • 65. DEVELOPMENT 65 code DefaultButtonWidgetViewFactoryBase.Style( margins = MarginValues(top = 16f), padding = platformSpecific( ios = PaddingValues(start = 16f, end = 16f), android = null ), background = StateBackground( normal = Background( fill = Fill.Solid(Colors.white), border = Border( color = Color(0xF2F2F8FF), width = 2f ), shape = Shape.Rectangle(cornerRadius = corners) ), pressed = Background(...), disabled = Background(...) ), textStyle = TextStyle(color = Color(0x777889FF)) ) common
  • 66. DEVELOPMENT 66 code DefaultButtonWidgetViewFactoryBase.Style( margins = MarginValues(top = 16f), padding = platformSpecific( ios = PaddingValues(start = 16f, end = 16f), android = null ), background = StateBackground( normal = Background( fill = Fill.Solid(Colors.white), border = Border( color = Color(0xF2F2F8FF), width = 2f ), shape = Shape.Rectangle(cornerRadius = corners) ), pressed = Background(...), disabled = Background(...) ), textStyle = TextStyle(color = Color(0x777889FF)) ) common
  • 67. DEVELOPMENT 67 code DefaultButtonWidgetViewFactoryBase.Style( margins = MarginValues(top = 16f), padding = platformSpecific( ios = PaddingValues(start = 16f, end = 16f), android = null ), background = StateBackground( normal = Background( fill = Fill.Solid(Colors.white), border = Border( color = Color(0xF2F2F8FF), width = 2f ), shape = Shape.Rectangle(cornerRadius = corners) ), pressed = Background(...), disabled = Background(...) ), textStyle = TextStyle(color = Color(0x777889FF)) ) common
  • 68. DEVELOPMENT 68 code DefaultButtonWidgetViewFactoryBase.Style( margins = MarginValues(top = 16f), padding = platformSpecific( ios = PaddingValues(start = 16f, end = 16f), android = null ), background = StateBackground( normal = Background( fill = Fill.Solid(Colors.white), border = Border( color = Color(0xF2F2F8FF), width = 2f ), shape = Shape.Rectangle(cornerRadius = corners) ), pressed = Background(...), disabled = Background(...) ), textStyle = TextStyle(color = Color(0x777889FF)) ) common
  • 76. DEVELOPMENT The basic architecture concepts: 1. compliance with platform rules 2. declare structure, not rendering 3. compile-time safety 4. reactive data handling 76 concepts
  • 77. DEVELOPMENT 77 concepts Compliance with platform rules: 1. Activity recreation on Android 2. Save instance state on Android 3. All elements are native (UI/UX)
  • 79. DEVELOPMENT Compile-time safety: 1. Type match of WidgetFactory and Widget 2. Child Widgets sizes compile-time checks 3. Widgets Id match to Widget type 4. Arguments in Screens 79 concepts
  • 80. DEVELOPMENT 80 concepts Type match of WidgetFactory and Widget val theme = Theme { textFactory = DefaultTextWidgetViewFactory() } val theme = Theme { textFactory = DefaultContainerWidgetViewFactory() }
  • 81. DEVELOPMENT 81 concepts Child Widgets sizes compile-time checks override fun createContentWidget() = with(theme) { container(size = WidgetSize.AsParent) { } } override fun createContentWidget() = with(theme) { container(size = WidgetSize.WrapContent) { } }
  • 82. DEVELOPMENT 82 concepts Child Widgets sizes compile-time checks fun createContentWidget(): Widget<WidgetSize.Const<SizeSpec.AsParent, SizeSpec.AsParent>>
  • 83. DEVELOPMENT 83 concepts Widgets Id match to Widget type setContainerFactory( DefaultContainerWidgetViewFactory(), RootContainerId ) setTextFactory( DefaultTextWidgetViewFactory(), RootContainerId ) object RootContainerId: ContainerWidget.Id
  • 85. DEVELOPMENT 85 concepts Arguments in Screens routeToScreen(ArgsScreen::class, ArgsScreen.Arg(10)) routeToScreen(ArgsScreen::class) class ArgsScreen : WidgetScreen<Args.Parcel<ArgsScreen.Arg>>()
  • 86. DEVELOPMENT Reactive data handling: 1. One-way binding via LiveData 2. Two-way binding via MutableLiveData 86 concepts
  • 87. DEVELOPMENT 87 concepts One-way binding via LiveData class TimerViewModel : ViewModel() { val text: LiveData<StringDesc> } val viewModel = getViewModel { TimerViewModel() } container(size = WidgetSize.AsParent) { center { text(size = WidgetSize.WrapContent, text = viewModel.text) } }
  • 88. DEVELOPMENT 88 concepts Two-way binding via MutableLiveData class InputViewModel : ViewModel() { val nameField: FormField<String, StringDesc> = FormField("") } val viewModel = getViewModel { InputViewModel() } input( size = WidgetSize.WidthAsParentHeightWrapContent, id = Id.NameInput, label = const(MR.strings.name_label.desc()), field = viewModel.nameField )
  • 89. DEVELOPMENT 89 concepts Two-way binding via MutableLiveData class FormField<D, E> { val data: MutableLiveData<D> val error: LiveData<E?> val isValid: LiveData<Boolean> }
  • 90. DEVELOPMENT 90 current list of widgets with(theme) { constraint(...) container(...) linear(...) scroll(...) stateful(...) tabs(...) clickable(...) image(...) text(...) input(...) button(...) list(...) collection(...) flatAlert(...) progressBar(...) singleChoice(...) switch(...) }
  • 91. DEVELOPMENT 91 current list of widgets public abstract class Widget<WS : WidgetSize> public constructor() { public abstract val size: WS public abstract fun buildView(viewFactoryContext: ViewFactoryContext): ViewBundle<WS> } public interface ViewFactory<W : Widget<out WidgetSize>> { public abstract fun <WS : WidgetSize> build(widget: W, size: WS, viewFactoryContext: ViewFactoryContext): ViewBundle<WS> }
  • 92. DEVELOPMENT ● Widgets declare screens and structure of screens, but rendering is native ● Jetpack Compose & SwiftUI can be used to render widgets 92 references
  • 93. DEVELOPMENT ● 1 kotlin developer can create for both platforms ● natively for developer and customer ● no limits with any modifications (feature, screen, …) ● MVP quickly ≠ must redo natively in the future 93 benefits
  • 94. DEVELOPMENT What’s available today (December, 19): ● Base widgets set ● Base navigation patterns ● Base styles for default widget-views ● A set of samples ● Actual version – 0.1.0-dev-5 94 roadmap
  • 95. DEVELOPMENT 95 December-January: ● Implement in production project ● Check flexibility of API to detect and fix limits – you can help with it. Just try self and send feedback to github issues ● Screens actions API design (show toast, alert, route) ● More documentation and samples roadmap
  • 96. DEVELOPMENT February-March: ● Release 0.1.0 (with flexible API for customization) ● Codelabs and Medium posts with details of new version 96 roadmap
  • 97. DEVELOPMENT 2020 Q2-Q3: ● New widgets ● More variations of widgets renders ● More styles ● Production usage 97 roadmap
  • 98. DEVELOPMENT 98 ap@icerock.dev Try MOKO widgets today => @kotlinmpp github/icerockdev