Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/build-desktop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Build desktop app

on:
push:
branches:
- kotlin-toolchain
pull_request:

jobs:
test:
name: Build
runs-on: ubuntu-latest
# Only run build in template repo
if: github.event.repository.name == 'KMP-App-Template-Native' && github.repository_owner == 'Kotlin'
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Desktop debug build
run: ./kotlin build -m desktopApp
19 changes: 19 additions & 0 deletions .github/workflows/build-web.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Build web app

on:
push:
branches:
- kotlin-toolchain
pull_request:

jobs:
test:
name: Build
runs-on: ubuntu-latest
# Only run build in template repo
if: github.event.repository.name == 'KMP-App-Template-Native' && github.repository_owner == 'Kotlin'
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Web debug build
run: ./kotlin build -m webApp -v debug
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,23 @@
[![official project](http://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

This is a basic Kotlin Multiplatform app template for Android and iOS. It includes shared business logic and data handling, and native UI implementations using Jetpack Compose and SwiftUI.
This is a basic Kotlin Multiplatform app template for Android, iOS, desktop, and web. It includes shared business logic and data handling, and native UI implementations using Compose Multiplatform and SwiftUI.

> The template is also available [with shared UI written in Compose Multiplatform](https://github.com/kotlin/KMP-App-Template).

![Screenshots of the app](images/screenshots.png)

### Running the apps

Use the run configurations provided by the run widget in your IDE's toolbar (make sure that you have the [Kotlin Multiplatform plugin](https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform) installed.

You can also use these commands and options:

- Android app: `./kotlin run -m androidApp`
- Desktop app: `./kotlin run -m desktopApp --compose-hot-reload-mode`
- Web app: `./kotlin run -m webApp`
- iOS app: `./kotlin run -m iosApp -p iosSimulatorArm64`, or open the iOS project in Xcode and run it from there.

### Technologies

The data displayed by the app is from [The Metropolitan Museum of Art Collection API](https://metmuseum.github.io/).
Expand All @@ -22,8 +33,10 @@ The app uses the following multiplatform dependencies in its implementation:

> These are just some of the possible libraries to use for these tasks with Kotlin Multiplatform, and their usage here isn't a strong recommendation for these specific libraries over the available alternatives. You can find a wide variety of curated multiplatform libraries in the [kmp-awesome](https://github.com/terrakok/kmp-awesome) repository.

And the following Android-specific dependencies:
The Android, desktop, and web apps share these dependencies:

- [Jetpack Compose](https://developer.android.com/jetpack/compose)
- [Navigation component](https://developer.android.com/jetpack/compose/navigation)
- [Compose Multiplatform](https://jb.gg/compose) for UI
- [Compose Navigation](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-navigation-routing.html)
- [Coil](https://github.com/coil-kt/coil) for image loading

The iOS app is built with [SwiftUI](https://developer.apple.com/xcode/swiftui/).
12 changes: 1 addition & 11 deletions androidApp/module.yaml
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
product: android/app

dependencies:
- ../shared
- $compose.runtime
- $compose.foundation
- $compose.material3
- $libs.androidx.compose.ui.tooling.preview
- ../sharedUI
- $libs.androidx.activity.compose
- $libs.androidx.viewmodel.compose
- $libs.androidx.navigation.compose
- $libs.koin.compose.viewmodel
- $libs.coil.compose
- $libs.coil.network.ktor
- $libs.material.icons.core

settings:
compose: enabled
Expand Down
13 changes: 0 additions & 13 deletions androidApp/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
<resources>
<string name="app_name">KMP App</string>

<string name="label_title">Title</string>
<string name="label_artist">Artist</string>
<string name="label_date">Date</string>
<string name="label_dimensions">Dimensions</string>
<string name="label_medium">Medium</string>
<string name="label_department">Department</string>
<string name="label_repository">Repository</string>
<string name="label_credits">Credits</string>

<string name="back">Back</string>

<string name="no_data_available">No data available</string>
</resources>
46 changes: 0 additions & 46 deletions androidApp/src/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,7 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.jetbrains.kmpapp.screens.DetailScreen
import com.jetbrains.kmpapp.screens.ListScreen

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -32,37 +20,3 @@ class MainActivity : ComponentActivity() {
}
}
}

@Composable
fun App() {
MaterialTheme(
colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme()
) {
Surface {
NavigationContent()
}
}
}

@Composable
fun NavigationContent() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "list") {
composable("list") {
ListScreen(navigateToDetails = { objectId ->
navController.navigate("details/$objectId")
})
}
composable(
"details/{objectId}",
arguments = listOf(navArgument("objectId") { type = NavType.IntType })
) { backstack ->
DetailScreen(
objectId = backstack.arguments?.getInt("objectId")!!,
navigateBack = {
navController.popBackStack()
}
)
}
}
}
12 changes: 1 addition & 11 deletions androidApp/src/MuseumApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,10 @@ package com.jetbrains.kmpapp

import android.app.Application
import com.jetbrains.kmpapp.di.initKoin
import com.jetbrains.kmpapp.screens.DetailViewModel
import com.jetbrains.kmpapp.screens.ListViewModel
import org.koin.dsl.module

class MuseumApp : Application() {
override fun onCreate() {
super.onCreate()
initKoin(
listOf(
module {
factory { ListViewModel(get()) }
factory { DetailViewModel(get()) }
}
)
)
initKoin()
}
}
9 changes: 9 additions & 0 deletions desktopApp/module.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
product: jvm/app

dependencies:
- ../sharedUI
- $compose.desktop.currentOs
- $libs.kotlinx.coroutines.swing

settings:
compose: enabled
18 changes: 18 additions & 0 deletions desktopApp/src/main.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.jetbrains.kmpapp

import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import com.jetbrains.kmpapp.di.initKoin

fun main() {
initKoin()

application {
Window(
onCloseRequest = ::exitApplication,
title = "KMP-App-Template-Native",
) {
App()
}
}
}
14 changes: 7 additions & 7 deletions iosApp/module.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */

/* Begin PBXFileSystemSynchronizedRootGroup section */
3932829DBF51BBABB904748B /* src */ = {
3932829DBF51BBABB904748B /* PBXFileSystemSynchronizedRootGroup */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
393283536DE23C7351F8EE5D /* PBXFileSystemSynchronizedBuildFileExceptionSet */,
Expand All @@ -45,7 +45,7 @@
children = (
39328DDC0ADDB7D3E94BA030 /* Products */,
3932828801841158B0ECBD49 /* Info.plist */,
3932829DBF51BBABB904748B /* src */,
3932829DBF51BBABB904748B /* PBXFileSystemSynchronizedRootGroup */,
);
sourceTree = "<group>";
};
Expand All @@ -64,7 +64,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 393285F919542C808148B5B9 /* Build configuration list for PBXNativeTarget "app" */;
buildPhases = (
39328688721F461EA052737F /* Build Kotlin with Amper */,
39328688721F461EA052737F /* Build Kotlin */,
39328B0D6BF0CDA7332EA98C /* Sources */,
39328C22B84BD298BA58714D /* Frameworks */,
393282655F1B9FE38F516DCE /* Resources */,
Expand All @@ -74,7 +74,7 @@
dependencies = (
);
fileSystemSynchronizedGroups = (
3932829DBF51BBABB904748B /* src */,
3932829DBF51BBABB904748B /* PBXFileSystemSynchronizedRootGroup */,
);
name = app;
productName = app;
Expand Down Expand Up @@ -111,17 +111,17 @@
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
39328688721F461EA052737F /* Build Kotlin with Amper */ = {
39328688721F461EA052737F /* Build Kotlin */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
inputPaths = (
);
name = "Build Kotlin with Amper";
name = "Build Kotlin";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# !AMPER KMP INTEGRATION STEP!\n# This script is managed by the Kotlin Toolchain, do not edit manually!\n\"${KOTLIN_CLI_WRAPPER_PATH}\" tool xcode-integration\n";
shellScript = "# !KOTLIN INTEGRATION STEP!\n# This script is managed by the Kotlin Toolchain, do not edit manually!\n\"${KOTLIN_CLI_WRAPPER_PATH}\" tool xcode-integration\n";
};
/* End PBXShellScriptBuildPhase section */

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1640"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "39328533D0CE065BF4B94F89"
BuildableName = "iosApp.app"
BlueprintName = "app"
ReferencedContainer = "container:module.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "39328533D0CE065BF4B94F89"
BuildableName = "iosApp.app"
BlueprintName = "app"
ReferencedContainer = "container:module.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "39328533D0CE065BF4B94F89"
BuildableName = "iosApp.app"
BlueprintName = "app"
ReferencedContainer = "container:module.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
4 changes: 2 additions & 2 deletions kotlin
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
set -e -u

# The version of the Kotlin Toolchain (and CLI) distribution to provision and use
kotlin_cli_version=0.11.1
kotlin_cli_version=0.12.0
# Establish chain of trust from here by specifying exact checksum of Kotlin Toolchain (and CLI) distribution to be run
kotlin_cli_sha256=0ded2a434f6bf193b24e2a6d56c3ba443f4232721155a65aaa8372789412112f
kotlin_cli_sha256=626b2d6e8753ad75b4b02144e597d2f441dd940794be5103352af2e800e4e13e

KOTLIN_CLI_DOWNLOAD_ROOT="${KOTLIN_CLI_DOWNLOAD_ROOT:-https://packages.jetbrains.team/maven/p/amper/amper}"

Expand Down
Loading
Loading