Hey there 👋
This is our template from which we build Kotlin Multiplatform applications that target Android and iOS platforms. It is our opinionated way of building KMP apps and shines a light on how we structure our architecture and what tools and libraries we use.
To give you a short overview of our stack, we use:
The template is a sample app with several screens to let you kick off the project with everything set up including navigation and some API calls.
-------8<------- CUT HERE AFTER CLONING -------8<-------
Short project description.
app.futured.project
28
34
app.futured.project
a@a.com
, password: hesloheslo
This project complies with Standard (F0), High (F1), Highest (F2) security standard.
clean
- Remove all build
folderslintCheck
- Run ktlint
, detekt
checks. The same runs on CI.ktlintFormat
- Reformat source code according to ktlint rules.generateMRcommonMain
- Regenerate shared resource IDs. :shared:network:graphql:downloadApolloSchemaFromIntrospection
- Download the latest Apollo schema:shared:network:graphql:generateApolloSources
- Generate Apollo sources (rebuilds models after adding modifying queries, mutations, etc.)The app utilizes Decompose to share presentation logic and navigation state in KMP.
The following meta-description provides an overview of the Decompose navigation tree:
Navigation("RootNavigation") {
Slot {
Screen("LoginScreen")
Navigation("HomeNavigation") {
Stack {
Screen("FirstScreen")
Screen("SecondScreen")
Screen("ThirdScreen")
}
}
}
}
Use init_template.kts
script to set up the template.
The script renames directories and package names in files to the given package name.
It is written in Kotlin. To run it you need to have kscript installed.
kscript init_template.kts
The project utilizes BuildKonfig plugin for implementing build flavors in the network module.
There are two product flavors: dev
and prod
, which select API url used in :shared:network:rest
and :shared:network:graphql
modules.
In general, the build flavor can be specified as a Gradle build flag
./gradlew whateverTask -P buildkonfig.flavor=dev
Please, refer to :shared:network:*
module Gradle configs for more info.
During local development, the build flavor can be specified in gradle.properties
file like so:
buildkonfig.flavor=dev
On iOS, we utilize .xcconfig Build Configuration files,
where each file per build configuration specifies a KMP_FLAVOR
environment variable.
This variable is then used in the shared framework build step to pass the flavor as Gradle build flag:
./gradlew :shared:app:embedAndSignAppleFrameworkForXcode -P buildkonfig.flavor=$KMP_FLAVOR
Currently, the Debug
build configuration uses the dev
flavor, and the Release
configuration uses the prod
flavor.
When adding new build configurations, please make sure to also define the KMP_FLAVOR
variable using the aforementioned method.
We can have symbolicated Kotlin crash reports on iOS. We use NSExceptionKt to achieve that. Everything is set up, but some finishing touches need to be made when you add Crashlytics to your project:
PlatformFirebaseCrashlyticsImpl
classes (follow comments).# https://firebase.google.com/docs/crashlytics/get-deobfuscated-reports?platform=ios&authuser=1#manually-upload-dsyms
# Location of GoogleService-Info.plist file for Firebase project (this might depend on build configuration)
GSPFILE="path_to_your_file.plist"
# `KOTLIN_FRAMEWORK_NAME` env variable should be defined in `.xcconfig` file for current build configuration.
# The other ones are implicitly provided by Xcode.
DSYMFILE="${SRCROOT}/../shared/app/build/xcode-frameworks/${CONFIGURATION}/${SDK_NAME}/${KOTLIN_FRAMEWORK_NAME}.framework.dSYM"
echo "Uploading Kotlin framework dSYM to Crashlytics"
echo "Google Services file: ${GSPFILE}"
echo "Shared framework dSYM file: ${DSYMFILE}"
# Validate
${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/upload-symbols --build-phase --validate -gsp ${GSPFILE} -p ios ${DSYMFILE}
# Upload
${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/upload-symbols -gsp ${GSPFILE} -p ios ${DSYMFILE}
Deep links are provided by each platform to common code and processed using DeepLinkResolver
and DeepLinkNavigator
classes.
The (example) app currently supports the following scheme: kmptemplate
and the following links:
kmptemplate://login
-- Navigates to login screenkmptemplate://a
-- Navigates to bottom navigation tab Akmptemplate://b
-- Navigates to bottom navigation tab Bkmptemplate://c
-- Navigates to bottom navigation tab Ckmptemplate://b/third
-- Navigates to third example screen on tab B.kmptemplate://b/secret?arg={OptionalArgument}
-- Navigates to secret screen reachable only by deep
link with optional argument arg
on tab B