
12- If you want to support older devices, consider iosArm32
too; Which you can find supported devices with their OS here:

13- For now, I’m gonna put versions into gradle.properties
in the root of the project. Later, I’ll separate modules with their own build.gradle
files.
14- We’re gonna create a temporary function to just have a sample app for now and run it on iOS and Android. So, create shared/src/commonMain/kotlin/common.kt
.
15- Create shared/src/androidMain/kotlin/common.kt
and shared/src/iosMain/kotlin/common.kta
and create platfrom
method with actual
keyword and your desired method body.
16- Note: in android module’s build.gradle.kts
you can use plugins { id("kotlin-multiplatfrom")
or plugins { id("kotlin-android")
. In case of the first choice, you’d need to put dependencies
scope into kotlin { sourceSets {
scope and add android()
as well.
But apart from this, in the case of choosing the first option, you’d have more than one “multiplatform” plugin which might be a cause for possible issues.
17- Use HelloWorldMessage()
in the Android module and run the Android app:

15- Create an iOS project: Create a new Xcode project -> iOS target -> Single View App and then fill the form and choose the root of the previous project. Remember to choose Storyboard for the user interface, instead of SwiftUI.
16- Note: Check gradle-wrapper.properties
and sure Gradle version is more than 4.7 (which I suggest to set it on class six (6.X.X)). Why? Run ./gradlew --version
on different gradle versions.
In this case, using val kotlinVersion: String by extra
doesn’t work in settings.gradle.kts
. So, we need to set the Kotlin version using requested.version
which use the version of the plugin if one was specified, otherwise null. So:

17- Run packForXcode
task to setup iOS Framework in the Xcode project model easily, whether with right Gradle panel in AS, Android Studio terminal, or Mac terminal.
You probably(👉 I didn’t get this error after I update Xcode, Android Studio and Kotlin version 👈) get this warning:

Which addressed here, here, etc.
18- Now, we need to add the shared framework to the Xcode project to be able to use common resources on iOS as well.
Go to Xcode, double click on the root of the project name(the blue icon: ios) to open target settings.
Under General tab, Under Frameworks, Libraries, and Embedded Content, press + -> Add Other… -> Add Files… -> address shared/build/xcode-frameworks/shared.framework folder.
19- Now we need to tell to the Xcode where to look for the framework.
Under the Build Settings tab, search for “Framework Search Paths”. Now add this relative path to both Debug and Release: “$(SRCROOT)/../shared/build/xcode-frameworks”.
20- Now we need to tell to the Xcode build our shared framework before each run.
Under the Build Phases -> + -> New Run Script Phase -> paste this snippet code in Shell content:

And drag this task on top of the other tasks.
21- Use HelloWorldMessage()
in the ViewController.swift.
Now run the iOS app:

Let’s start to build the OMDb App
22- Note: I’ve checked lots of articles and open-source projects (as I listed some of them as a reference).
For now, I’m gonna follow with some of Fandy Gotama’s snippet codes (which I do not agree with all of them) but, the idea is to have a working application; Especially, as I’m not an iOS developer(yet😄). Later on, I’m gonna improve and change the project and update the article.
23- Add the necessary APIs to the project. For example for Reactive:
Root’s build.gradle file:
allprojects {
repositories {
mavenCentral()
...
maven("https://dl.bintray.com/badoo/maven")google()
jcenter()
}
}
And in the shared’s build.gradle.kt:
val reactiveVersion: String by extra...sourceSets {
val commonMain by getting {
dependencies {
...
implementation("com.badoo.reaktive:reaktive:$reactiveVersion")
}
}
...
}}
24- About kotlinx.serialization, take it into account after Kotlin v1.4-M2:
Specifying dependencies only once
From now on, instead of specifying dependencies on different variants of the same library in shared and platform-specific source sets where it is used, you should specify a dependency only once in the shared source set.
Don’t use kotlinx library artifact names with suffixes specifying the platform, such as -common
, -native
, or similar, as they are NOT supported anymore. Instead, use the library base artifact name, which in the example above is kotlinx-coroutines-core
. However, the change doesn’t currently affect the stdlib
and kotlin.test
libraries (stdlib-common
and test-common
); they will be addressed later.
If you need a dependency only for a specific platform, you can still use platform-specific variants of standard and kotlinx libraries with such suffixes as -jvm
or-js
, for example kotlinx-coroutines-core-jvm
.
25- Regards enabling experimental features, please take a look here.
26- For Ktor version, take the latest version from the releases section, and not the download section of the Github: ❌👇

27- Remember if you tried the newer unstable Kotlin version, then regretted and set it back to a stable version(1.3.X), be sure that you uninstall Kotlin Plugin(with unstable version) and restart the IDE(to get the stable version automatically). Otherwise, you get this error:

28- Note: If you use kotlin() instead of id(), you must change dash(-) with a dot(.) and remove kotlin keyword if it exists. For example:

29- Objective-C supports “lightweight generics” defined on classes, with a relatively limited feature set. Swift can import generics defined on classes to help provide additional type information to the compiler.
Generic feature support for Objc and Swift differ from Kotlin, so the translation will inevitably lose some information, but the features supported retain meaningful information.
Generics are currently not enabled by default. To have the framework header written with generics, add an experimental flag to the compiler config:
iOSTarget("ios") {
binaries {
framework {
baseName = "shared"
freeCompilerArgs += "-Xobjc-generics"
}
}
}
For Kotlin/Native interoperability with Swift/Objective-C, check out here.
30- To have the logging feature in Ktor, check out this.
31- [Out of Date-Check #44]For determining build type in the shared module, there are a few tricks and libraries but, I preferred to pass the value(API_KEY) from android module to the shared module.
A minor advantage of this solution is, we avoid rebuilding the shared module when the API URL and token get changed.[Out of Date-Check #44]
32- Note: For the architecture, it’s not 100% MVVM that all we know; Cause we have to take care of our object lifecycle by ourselves. There is moko-mvvm but, I didn’t want to rely on a third-party component for the architecture. Maybe later when I try to improve the architecture(as I mentioned on ‘under development’ section), I realize that that component is the best solution so far.
33- If you use opt-in requirements for features in the experimental state, carefully handle the API graduation to avoid breaking the client code.
To use experimental opt-in annotation, we need to add -Xopt-in=kotlin.RequiresOptIn
to the compiler argument. I’ve passed as the second parameter to sections where we defined jvmTarget = “1.8” but, didn’t work for me. So, I’ve used it like below in the shared’s build.gradle, inside sourceSet scope:

34- About Ktor client, we used the built-in install method to implement the JSON feature and use kotlin serialization.
35- Note: we use suspend function from coroutines because it's required by Ktor.
36- I’m not gonna explain implemented architecture. If you have any doubts or questions regards Clean Architecture, you could take a look into this article of mine.
37- As I earlier mentioned, Ktor uses coroutines to do the asynchronous task. So, we need to transform suspend fun into the observable stream. One solution is using coroutines-interop
but, due to its temporary limitations and current bugs, do not use this for now:
implementation 'com.badoo.reaktive:coroutines-interop:<latest-version>'
For more information, please read this.
38- About ViewModel, just keep it in your mind that Inputs
represents any interaction/input from the view and Outputs
represents changes from the ViewModel that the view has to display. To understand it better, please take a look into this.
39- To work with Inputs
and Outputs
in the ViewModel on all platforms, we created ViewModelBinding
.
40- For image loading in iOS, I’m using Nuke library.
To add an external library to your project in XCode, first, connect your IDE to your GitHub account from Preferences -> Account, then to add a library from Github, follow File -> Swift Package -> Add Package Dependency.
41- About nil
keyword in Swift:
Swift’s nil
isn’t the same as nil
in Objective-C. In Objective-C, nil
is a pointer to a nonexistent object. In Swift, nil
isn’t a pointer—it’s the absence of a value of a certain type. Optionals of any type can be set to nil
, not just object types.
In another word, it’s null
in Java/Kotlin/etc.
42- In iOS project, we don’t pass MoviesUIMapper
(To map mapped API response(domain model), to UI model) to the ViewModel. We use the mapped model(from API response to domain model) via MoviesMapper
directly, which we passed to MoviesApiImpl
.
Update 07/2020:
43- According to Arkadii Ivanov’s comment, I’ve changed the definition of ViewModelBinding
to extends DisposableScope:

For more information, please take a look into this.
Update 08/2020:
44- After using Kodein as a DI/Service Locator library, I’m setting API_KEY in shared
module -> DI
class. A benefit of this, we’d not set this key multiple times in different platforms.
A few points we need to take into account:
- We have to set
jvmTarget
to 1.8 in android and shared modules.
- We use
kotlin.native.concurrent.ThreadLocal
on the DI
class to prevent “Unexpected mutability issue” on Koltin-Native.
- Changes to apply Kodein.
45- Done!
Result 📺

Under Development 🏭
- 🔘 Improve architecture.
- 🔘 Add search box.
- 🔘 Use Sqldelight to show how to use the database in Multiplatform apps.
- 🔘 Improve Clean Architecture approach and separate modules with the power of gradle.
- 🔘 Categorize dependencies.
- 🔘 Add tests.
- ✅ Use a DI framework or a Service Locator
Tags: Android,iOS,Kotlin Multiplatform,Rxjava,Kotlin
View original article at:

Originally published: July 27, 2020