├── .gitignore
├── .idea
├── .gitignore
├── artifacts
│ ├── compose_ui_desktop.xml
│ ├── desktop_jvm.xml
│ ├── main_desktop.xml
│ ├── root_desktop.xml
│ └── utils_desktop.xml
├── compiler.xml
├── deploymentTargetDropDown.xml
├── gradle.xml
├── jarRepositories.xml
├── kotlinScripting.xml
├── misc.xml
└── vcs.xml
├── README.md
├── androidApp
├── build.gradle.kts
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── nikolam
│ │ └── kmm_weather
│ │ └── androidApp
│ │ ├── App.kt
│ │ ├── Color.kt
│ │ ├── MainActivity.kt
│ │ ├── Shape.kt
│ │ ├── Theme.kt
│ │ └── Type.kt
│ └── res
│ └── values
│ └── colors.xml
├── build.gradle.kts
├── buildSrc
├── build.gradle.kts
├── buildSrc
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ ├── AndroidGradle.kt
│ │ └── Deps.kt
└── src
│ └── main
│ └── kotlin
│ ├── android-setup.gradle.kts
│ ├── multiplatform-compose-setup.gradle.kts
│ └── multiplatform-setup.gradle.kts
├── common
├── compose-ui
│ ├── build.gradle.kts
│ └── src
│ │ ├── androidMain
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── nikolam
│ │ │ │ └── kmm_weather
│ │ │ │ └── ui
│ │ │ │ ├── Composables.kt
│ │ │ │ └── Utils.kt
│ │ └── res
│ │ │ └── drawable
│ │ │ ├── angry_clouds.xml
│ │ │ ├── cloudy.xml
│ │ │ ├── day_clear.xml
│ │ │ ├── day_partial_cloud.xml
│ │ │ ├── day_rain.xml
│ │ │ ├── day_rain_thunder.xml
│ │ │ ├── day_sleet.xml
│ │ │ ├── day_snow.xml
│ │ │ ├── day_snow_thunder.xml
│ │ │ ├── fog.xml
│ │ │ ├── ic_air_black_24dp.xml
│ │ │ ├── ic_water_drop_black_24dp.xml
│ │ │ ├── mist.xml
│ │ │ ├── night_full_moon_clear.xml
│ │ │ ├── night_full_moon_partial_cloud.xml
│ │ │ ├── night_full_moon_rain.xml
│ │ │ ├── night_full_moon_rain_thunder.xml
│ │ │ ├── night_full_moon_sleet.xml
│ │ │ ├── night_full_moon_snow.xml
│ │ │ ├── night_full_moon_snow_thunder.xml
│ │ │ ├── night_half_moon_clear.xml
│ │ │ ├── night_half_moon_partial_cloud.xml
│ │ │ ├── night_half_moon_rain.xml
│ │ │ ├── night_half_moon_rain_thunder.xml
│ │ │ ├── night_half_moon_sleet.xml
│ │ │ ├── night_half_moon_snow.xml
│ │ │ ├── night_half_moon_snow_thunder.xml
│ │ │ ├── overcast.xml
│ │ │ ├── rain.xml
│ │ │ ├── rain_thunder.xml
│ │ │ ├── sleet.xml
│ │ │ ├── snow.xml
│ │ │ ├── snow_thunder.xml
│ │ │ ├── thunder.xml
│ │ │ ├── tornado.xml
│ │ │ └── wind.xml
│ │ ├── commonMain
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── nikolam
│ │ │ └── kmm_weather
│ │ │ └── ui
│ │ │ ├── Composables.kt
│ │ │ ├── Utils.kt
│ │ │ ├── WeatherMainContent.kt
│ │ │ ├── WeatherRootContent.kt
│ │ │ └── colors.kt
│ │ └── desktopMain
│ │ ├── kotlin
│ │ └── com.nikolam.kmm_weather.ui
│ │ │ └── Composables.kt
│ │ └── resources
│ │ └── images
│ │ ├── air.png
│ │ ├── angry_clouds.png
│ │ ├── cloudy.png
│ │ ├── day_clear.png
│ │ ├── day_partial_cloud.png
│ │ ├── day_rain.png
│ │ ├── day_rain_thunder.png
│ │ ├── day_sleet.png
│ │ ├── day_snow.png
│ │ ├── day_snow_thunder.png
│ │ ├── fog.png
│ │ ├── mist.png
│ │ ├── night_full_moon_clear.png
│ │ ├── night_full_moon_partial_cloud.png
│ │ ├── night_full_moon_rain.png
│ │ ├── night_full_moon_rain_thunder.png
│ │ ├── night_full_moon_sleet.png
│ │ ├── night_full_moon_snow.png
│ │ ├── night_full_moon_snow_thunder.png
│ │ ├── night_half_moon_clear.png
│ │ ├── night_half_moon_partial_cloud.png
│ │ ├── night_half_moon_rain.png
│ │ ├── night_half_moon_rain_thunder.png
│ │ ├── night_half_moon_sleet.png
│ │ ├── night_half_moon_snow.png
│ │ ├── night_half_moon_snow_thunder.png
│ │ ├── overcast.png
│ │ ├── rain.png
│ │ ├── rain_thunder.png
│ │ ├── sleet.png
│ │ ├── snow.png
│ │ ├── snow_thunder.png
│ │ ├── thunder.png
│ │ ├── tornado.png
│ │ ├── water_drop.png
│ │ └── wind.png
├── main
│ ├── build.gradle.kts
│ └── src
│ │ ├── androidMain
│ │ └── AndroidManifest.xml
│ │ └── commonMain
│ │ └── kotlin
│ │ └── com.nikolam.kmm_weather.common.main
│ │ ├── WeatherMain.kt
│ │ ├── data
│ │ ├── model
│ │ │ ├── CurrentWeatherModel.kt
│ │ │ └── CurrentWeatherNetworkModel.kt
│ │ └── network
│ │ │ └── WeatherAPI.kt
│ │ ├── integration
│ │ ├── Mappers.kt
│ │ └── WeatherMainComponent.kt
│ │ └── store
│ │ ├── WeatherMainStore.kt
│ │ └── WeatherMainStoreProvider.kt
├── root
│ ├── build.gradle.kts
│ └── src
│ │ ├── androidMain
│ │ └── AndroidManifest.xml
│ │ └── commonMain
│ │ └── kotlin
│ │ └── com.nikolam.kmm_weather.common.root
│ │ ├── WeatherRoot.kt
│ │ └── integration
│ │ └── WeatherRootComponent.kt
└── utils
│ ├── build.gradle.kts
│ └── src
│ ├── androidMain
│ ├── AndroidManifest.xml
│ └── kotlin
│ │ └── com.nikolam.kmm_weather.common.utils
│ │ └── PlatformServiceLocator.kt
│ ├── commonMain
│ └── kotlin
│ │ └── com.nikolam.kmm_weather.common.utils
│ │ ├── InstanceKeeperExt.kt
│ │ ├── PlatformServiceLocator.kt
│ │ ├── ReaktiveExt.kt
│ │ └── StoreExt.kt
│ ├── desktopMain
│ └── kotlin
│ │ └── com.nikolam.kmm_weather.common.utils
│ │ └── PlatformServiceLocator.kt
│ └── iosMain
│ └── kotlin
│ └── com.nikolam.kmm_weather.common.utils
│ └── PlatformServiceLocator.kt
├── desktop
├── build.gradle.kts
└── src
│ └── jvmMain
│ └── kotlin
│ └── com.nikolam.kmm_weather.desktop
│ └── Main.kt
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── iosApp
├── kmm_weather.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── kmm_weather
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ ├── cloudy.imageset
│ │ ├── Contents.json
│ │ └── cloudy.svg
│ ├── day_clear.imageset
│ │ ├── Contents.json
│ │ └── day_clear.svg
│ ├── day_partial_cloud.imageset
│ │ ├── Contents.json
│ │ └── day_partial_cloud.svg
│ ├── day_rain.imageset
│ │ ├── Contents.json
│ │ └── day_rain.svg
│ ├── day_rain_thunder.imageset
│ │ ├── Contents.json
│ │ └── day_rain_thunder.svg
│ ├── day_sleet.imageset
│ │ ├── Contents.json
│ │ └── day_sleet.svg
│ ├── day_snow.imageset
│ │ ├── Contents.json
│ │ └── day_snow.svg
│ ├── day_snow_thunder.imageset
│ │ ├── Contents.json
│ │ └── day_snow_thunder.svg
│ ├── fog.imageset
│ │ ├── Contents.json
│ │ └── fog.svg
│ ├── mist.imageset
│ │ ├── Contents.json
│ │ └── mist.svg
│ ├── night_clear.imageset
│ │ ├── Contents.json
│ │ └── night_clear.svg
│ ├── night_partial_cloud.imageset
│ │ ├── Contents.json
│ │ └── night_partial_cloud.svg
│ ├── night_rain.imageset
│ │ ├── Contents.json
│ │ └── night_rain.svg
│ ├── night_rain_thunder.imageset
│ │ ├── Contents.json
│ │ └── night_rain_thunder.svg
│ ├── night_sleet.imageset
│ │ ├── Contents.json
│ │ └── night_sleet.svg
│ ├── night_snow.imageset
│ │ ├── Contents.json
│ │ └── night_snow.svg
│ ├── night_snow_thunder.imageset
│ │ ├── Contents.json
│ │ └── night_snow_thunder.svg
│ ├── overcast.imageset
│ │ ├── Contents.json
│ │ └── overcast.svg
│ ├── rain.imageset
│ │ ├── Contents.json
│ │ └── rain.svg
│ ├── rain_thunder.imageset
│ │ ├── Contents.json
│ │ └── rain_thunder.svg
│ ├── sleet.imageset
│ │ ├── Contents.json
│ │ └── sleet.svg
│ ├── snow.imageset
│ │ ├── Contents.json
│ │ └── snow.svg
│ ├── snow_thunder.imageset
│ │ ├── Contents.json
│ │ └── snow_thunder.svg
│ ├── thunder.imageset
│ │ ├── Contents.json
│ │ └── thunder.svg
│ ├── tornado.imageset
│ │ ├── Contents.json
│ │ └── tornado.svg
│ └── wind.imageset
│ │ ├── Contents.json
│ │ └── wind.svg
│ ├── ComponentHolder.swift
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── LaunchScreen.storyboard
│ ├── MainView.swift
│ ├── MutableValueBuilder.swift
│ ├── ObservableValue.swift
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── RootView.swift
│ ├── SceneDelegate.swift
│ ├── SearchBox.swift
│ ├── SimpleRouterState.swift
│ ├── Utils.swift
│ └── WeatherAppDelegate.swift
├── settings.gradle.kts
└── showcase
├── android_dark.png
├── android_light.png
├── desktop.png
└── ios.png
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
17 | # Built application files
18 | *.apk
19 | *.aar
20 | *.ap_
21 | *.aab
22 |
23 | # Files for the ART/Dalvik VM
24 | *.dex
25 |
26 | # Java class files
27 | *.class
28 |
29 | # Generated files
30 | bin/
31 | gen/
32 | out/
33 | # Uncomment the following line in case you need and you don't have the release build type files in your app
34 | release/
35 |
36 |
37 | # Xcode
38 | #
39 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
40 |
41 | ## User settings
42 | xcuserdata/
43 |
44 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
45 | *.xcscmblueprint
46 | *.xccheckout
47 |
48 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
49 | build/
50 | DerivedData/
51 | *.moved-aside
52 | *.pbxuser
53 | !default.pbxuser
54 | *.mode1v3
55 | !default.mode1v3
56 | *.mode2v3
57 | !default.mode2v3
58 | *.perspectivev3
59 | !default.perspectivev3
60 |
61 | ## Obj-C/Swift specific
62 | *.hmap
63 |
64 | ## App packaging
65 | *.ipa
66 | *.dSYM.zip
67 | *.dSYM
68 |
69 | ## Playgrounds
70 | timeline.xctimeline
71 | playground.xcworkspace
72 |
73 | # Swift Package Manager
74 | #
75 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
76 | # Packages/
77 | # Package.pins
78 | # Package.resolved
79 | # *.xcodeproj
80 | #
81 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
82 | # hence it is not needed unless you have added a package configuration file to your project
83 | # .swiftpm
84 |
85 | .build/
86 |
87 | # CocoaPods
88 | #
89 | # We recommend against adding the Pods directory to your .gitignore. However
90 | # you should judge for yourself, the pros and cons are mentioned at:
91 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
92 | #
93 | # Pods/
94 | #
95 | # Add this line if you want to avoid checking in source code from the Xcode workspace
96 | # *.xcworkspace
97 |
98 | # Carthage
99 | #
100 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
101 | # Carthage/Checkouts
102 |
103 | Carthage/Build/
104 |
105 | # Accio dependency management
106 | Dependencies/
107 | .accio/
108 |
109 | # fastlane
110 | #
111 | # It is recommended to not store the screenshots in the git repo.
112 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
113 | # For more information about the recommended setup visit:
114 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
115 |
116 | fastlane/report.xml
117 | fastlane/Preview.html
118 | fastlane/screenshots/**/*.png
119 | fastlane/test_output
120 |
121 | # Code Injection
122 | #
123 | # After new code Injection tools there's a generated folder /iOSInjectionProject
124 | # https://github.com/johnno1962/injectionforxcode
125 |
126 | iOSInjectionProject/
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/artifacts/compose_ui_desktop.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | $PROJECT_DIR$/common/compose-ui/build/libs
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/artifacts/desktop_jvm.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | $PROJECT_DIR$/desktop/build/libs
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/artifacts/main_desktop.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | $PROJECT_DIR$/common/main/build/libs
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/artifacts/root_desktop.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | $PROJECT_DIR$/common/root/build/libs
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/artifacts/utils_desktop.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | $PROJECT_DIR$/common/utils/build/libs
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.idea/deploymentTargetDropDown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
26 |
27 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/.idea/kotlinScripting.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Kotlin Multiplatform Sample Weather App
3 |
4 | This is a sample weather app made to play around with Kotlin Multiplatform. I tried to strive for maximum code sharing between platforms, all the business logic is shared. Only thing that is platform depended is the native UI. Jetpack Compose for Desktop and Android and SwiftUI for IOS.
5 |
6 | This kind of architecture is only achiveable thanks to the work of
7 | Arkadii Ivanov and his libraries [Decompose](https://github.com/arkivanov/Decompose) and [MVIKotlin](https://github.com/arkivanov/MVIKotlin).
8 |
9 | Kotlin Multiplatform and libraries supporting it are highly violatile at the moment and in the early stages of development. It can be kinda a pain in the bottom to work with but nevertheless it has been a great experience. I can't wait to see what the future holds, I truly believe this will be the future of cross platform development. Just thinking about using Kotlin to develop a codebase shared across platforms is almost like a dream.
10 |
11 |
12 | ## UI
13 |
14 | The Android and Desktop app share most of the UI code that resides in the :common:compose-ui. There are slight platform specific implementations that allow for dark mode support on Android. Besides that, it's super easy to develop a single UI and have it work on Android and Desktop.
15 |
16 | The IOS UI is made with the help of SwiftUI and considering it's pretty similar to Jetpack Compose it's also an easy switch. You can pretty much just go and 1 to 1 copy the Compose code with the respected SwiftUI alternatives.
17 |
18 |
19 | ## Demo
20 |
21 | ### Android app
22 |
23 |
24 |
25 | Light Mode Dark Mode
26 |
27 |
28 |
29 | ### Desktop app
30 |
31 |
32 |
33 |
34 | ### Ios app
35 |
36 |
37 |
38 |
39 | ## Shared code
40 |
41 | With the help of the amazing libraries from [Arkadii Ivanov](https://github.com/arkivanov) like [MVIKotlin](https://github.com/arkivanov/MVIKotlin) and [Decompose](https://github.com/arkivanov/Decompose) sharing 90%+ of the business logic is achieveable. Currently, albeit a simple Weather app, I am using the exact same models, logic, use-cases for all platforms. That means, with the single common module I am able to have the same logic running on Android, Ios and Desktop (Mac, Linux and Windows!). Which makes testing easier, development (as most of the apps share functionality across platforms) and it's just nice to write in Kotlin.
42 |
--------------------------------------------------------------------------------
/androidApp/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.compose.compose
2 |
3 | plugins {
4 | id("com.android.application")
5 | kotlin("android")
6 | id("org.jetbrains.compose")
7 | }
8 |
9 | android {
10 | compileSdkVersion(AndroidGradle.TARGETSDK)
11 | defaultConfig {
12 | applicationId = "com.nikolam.kmm_weather.androidApp"
13 | minSdkVersion(AndroidGradle.MINSDK)
14 | targetSdkVersion(AndroidGradle.TARGETSDK)
15 | versionCode = 1
16 | versionName = "1.0"
17 | }
18 | compileOptions {
19 | sourceCompatibility = JavaVersion.VERSION_1_8
20 | targetCompatibility = JavaVersion.VERSION_1_8
21 | }
22 |
23 | packagingOptions {
24 | exclude("META-INF/*")
25 | }
26 | }
27 | dependencies {
28 | implementation(project(":common:utils"))
29 | implementation(project(":common:root"))
30 | implementation(project(":common:compose-ui"))
31 | implementation(compose.material)
32 | implementation(Deps.ArkIvanov.MVIKotlin.mvikotlin)
33 | implementation(Deps.ArkIvanov.MVIKotlin.mvikotlinMain)
34 | implementation(Deps.ArkIvanov.MVIKotlin.mvikotlinLogging)
35 | implementation(Deps.ArkIvanov.MVIKotlin.mvikotlinTimeTravel)
36 | implementation(Deps.ArkIvanov.Decompose.decompose)
37 | implementation(Deps.ArkIvanov.Decompose.extensionsCompose)
38 | implementation(Deps.AndroidX.AppCompat.appCompat)
39 | implementation(Deps.AndroidX.Activity.activityCompose)
40 |
41 | implementation(Deps.Utils.napier)
42 | }
--------------------------------------------------------------------------------
/androidApp/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/androidApp/src/main/java/com/nikolam/kmm_weather/androidApp/App.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.androidApp
2 |
3 | import android.app.Application
4 | import com.arkivanov.mvikotlin.timetravel.server.TimeTravelServer
5 |
6 | class App : Application() {
7 |
8 | override fun onCreate() {
9 | super.onCreate()
10 | TimeTravelServer().start()
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/androidApp/src/main/java/com/nikolam/kmm_weather/androidApp/Color.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.androidApp
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val DarkPurple = Color(0xFF5550F2)
6 | val SkyBlue = Color(0xFF8CCEF4)
7 | val MediumPurple = Color(0xFF6187F6)
8 |
9 | val DarkDarkPurple = Color(0xFF00002c)
10 | val DarkSkyBlue = Color(0xFF443381)
11 | val DarkMediumPurple = Color(0xFF110c54)
12 | val DarkTextColor = Color(0xFF87a1ff)
--------------------------------------------------------------------------------
/androidApp/src/main/java/com/nikolam/kmm_weather/androidApp/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.androidApp
2 |
3 | import android.os.Bundle
4 | import androidx.activity.compose.setContent
5 | import androidx.appcompat.app.AppCompatActivity
6 | import androidx.compose.foundation.isSystemInDarkTheme
7 | import androidx.compose.material.MaterialTheme
8 | import androidx.compose.material.Surface
9 | import androidx.compose.ui.graphics.toArgb
10 | import com.arkivanov.decompose.ComponentContext
11 | import com.arkivanov.decompose.extensions.compose.jetbrains.rememberRootComponent
12 | import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory
13 | import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
14 | import com.arkivanov.mvikotlin.timetravel.store.TimeTravelStoreFactory
15 | import com.nikolam.kmm_weather.common.root.WeatherRoot
16 | import com.nikolam.kmm_weather.common.root.integration.WeatherRootComponent
17 | import com.nikolam.kmm_weather.ui.WeatherRootContent
18 | import io.github.aakira.napier.DebugAntilog
19 | import io.github.aakira.napier.Napier
20 |
21 | class MainActivity : AppCompatActivity() {
22 | override fun onCreate(savedInstanceState: Bundle?) {
23 | super.onCreate(savedInstanceState)
24 | Napier.base(DebugAntilog("my_weather_tag"))
25 | setContent {
26 | ComposeAppTheme {
27 | if (!isSystemInDarkTheme()) {
28 | window.statusBarColor = DarkPurple.toArgb()
29 | } else {
30 | window.statusBarColor = DarkDarkPurple.toArgb()
31 | }
32 |
33 | Surface(color = MaterialTheme.colors.background) {
34 | WeatherRootContent(rememberRootComponent(::weatherRoot))
35 | }
36 | }
37 | }
38 | }
39 |
40 | private fun weatherRoot(componentContext: ComponentContext): WeatherRoot =
41 | WeatherRootComponent(
42 | componentContext = componentContext,
43 | storeFactory = LoggingStoreFactory(TimeTravelStoreFactory(DefaultStoreFactory))
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/androidApp/src/main/java/com/nikolam/kmm_weather/androidApp/Shape.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.androidApp
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material.Shapes
5 | import androidx.compose.ui.unit.dp
6 |
7 | val shapes = Shapes(
8 | small = RoundedCornerShape(4.dp),
9 | medium = RoundedCornerShape(4.dp),
10 | large = RoundedCornerShape(0.dp)
11 | )
12 |
--------------------------------------------------------------------------------
/androidApp/src/main/java/com/nikolam/kmm_weather/androidApp/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.androidApp
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.material.MaterialTheme
5 | import androidx.compose.material.darkColors
6 | import androidx.compose.material.lightColors
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.graphics.Color
9 |
10 |
11 | private val DarkColorPalette = darkColors(
12 | primary = DarkDarkPurple,
13 | primaryVariant = DarkMediumPurple,
14 | secondary = DarkSkyBlue,
15 | onPrimary = DarkTextColor,
16 | onSecondary = DarkTextColor
17 | )
18 |
19 | private val LightColorPalette = lightColors(
20 | primary = DarkPurple,
21 | primaryVariant = MediumPurple,
22 | secondary = SkyBlue,
23 | onPrimary = Color.White,
24 | onSecondary = Color.White
25 |
26 | /* Other default colors to override
27 | background = Color.White,
28 | surface = Color.White,
29 | onPrimary = Color.White,
30 | onSecondary = Color.Black,
31 | onBackground = Color.Black,
32 | onSurface = Color.Black,
33 | */
34 | )
35 |
36 | @Composable
37 | fun ComposeAppTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) {
38 | val colors = if (darkTheme) {
39 | DarkColorPalette
40 | } else {
41 | LightColorPalette
42 | }
43 |
44 | MaterialTheme(
45 | colors = colors,
46 | typography = typography,
47 | shapes = shapes,
48 | content = content
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/androidApp/src/main/java/com/nikolam/kmm_weather/androidApp/Type.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.androidApp
2 |
3 | import androidx.compose.material.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val typography = Typography(
11 | body1 = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp
15 | )
16 | )
17 |
--------------------------------------------------------------------------------
/androidApp/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl`
3 | }
4 |
5 | apply(plugin = "com.github.ben-manes.versions")
6 |
7 | allprojects {
8 | repositories {
9 | google()
10 | mavenCentral()
11 | mavenLocal()
12 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
13 | }
14 |
15 |
16 | // afterEvaluate {
17 | // tasks.withType {
18 | // kotlinOptions {
19 | // useIR = true
20 | // if (configurations.findByName("kotlinCompilerPluginClasspath")
21 | // ?.dependencies
22 | // ?.any { it.group == "androidx.compose.compiler" } == true
23 | // ) {
24 | // freeCompilerArgs += listOf(
25 | // "-P",
26 | // "plugin:androidx.compose.compiler.plugins.kotlin:suppressKotlinVersionCompatibilityCheck=true"
27 | // )
28 | // }
29 | // }
30 | // }
31 | // }
32 | }
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl`
3 | }
4 |
5 | repositories {
6 | // TODO: remove after new build is published
7 | mavenLocal()
8 | google()
9 | mavenCentral()
10 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
11 | gradlePluginPortal()
12 | }
13 |
14 | dependencies {
15 | implementation(Deps.JetBrains.Compose.gradlePlugin)
16 | implementation(Deps.JetBrains.Kotlin.gradlePlugin)
17 | implementation(Deps.Android.Tools.Build.gradlePlugin)
18 | implementation(Deps.Squareup.SQLDelight.gradlePlugin)
19 | implementation(Deps.JetBrains.Kotlin.serializationGradle)
20 | implementation(Deps.Utils.dependenciesCheck)
21 | }
22 |
23 | kotlin {
24 | // Add Deps to compilation, so it will become available in main project
25 | sourceSets.getByName("main").kotlin.srcDir("buildSrc/src/main/kotlin")
26 | }
27 |
--------------------------------------------------------------------------------
/buildSrc/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl`
3 | }
4 |
5 | repositories {
6 | mavenCentral()
7 | }
8 |
--------------------------------------------------------------------------------
/buildSrc/buildSrc/src/main/kotlin/AndroidGradle.kt:
--------------------------------------------------------------------------------
1 | object AndroidGradle {
2 | val MINSDK = 21
3 | val MAXSDK = 30
4 | val TARGETSDK = 30
5 | }
6 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/android-setup.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.library")
3 | }
4 |
5 | android {
6 | compileSdkVersion(AndroidGradle.TARGETSDK)
7 |
8 | defaultConfig {
9 | minSdkVersion(AndroidGradle.MINSDK)
10 | targetSdkVersion(AndroidGradle.TARGETSDK)
11 | }
12 |
13 | compileOptions {
14 | sourceCompatibility = JavaVersion.VERSION_1_8
15 | targetCompatibility = JavaVersion.VERSION_1_8
16 | }
17 |
18 | sourceSets {
19 | named("main") {
20 | manifest.srcFile("src/androidMain/AndroidManifest.xml")
21 | res.srcDirs("src/androidMain/res")
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/multiplatform-compose-setup.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.compose.compose
2 |
3 | plugins {
4 | id("com.android.library")
5 | id("kotlin-multiplatform")
6 | id("org.jetbrains.compose")
7 | }
8 |
9 | ////workaround for https://youtrack.jetbrains.com/issue/KT-43944
10 | //android {
11 | // configurations {
12 | // create("androidTestApi")
13 | // create("androidTestDebugApi")
14 | // create("androidTestReleaseApi")
15 | // create("testApi")
16 | // create("testDebugApi")
17 | // create("testReleaseApi")
18 | // }
19 | //}
20 | kotlin {
21 | jvm("desktop")
22 | android()
23 |
24 | sourceSets {
25 | named("commonMain") {
26 | dependencies {
27 | implementation(compose.runtime)
28 | implementation(compose.foundation)
29 | implementation(compose.material)
30 | }
31 | }
32 |
33 | named("androidMain") {
34 | dependencies {
35 | implementation("androidx.appcompat:appcompat:1.3.0-beta01")
36 | implementation("androidx.core:core-ktx:1.3.1")
37 | }
38 | }
39 |
40 | named("desktopMain") {
41 | dependencies {
42 | implementation(compose.desktop.common)
43 | }
44 | }
45 | }
46 |
47 | tasks.withType {
48 | kotlinOptions.jvmTarget = "1.8"
49 | }
50 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/multiplatform-setup.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.library")
3 | id("kotlin-multiplatform")
4 | }
5 |
6 | ////workaround for https://youtrack.jetbrains.com/issue/KT-43944
7 | //android {
8 | // configurations {
9 | // create("androidTestApi")
10 | // create("androidTestDebugApi")
11 | // create("androidTestReleaseApi")
12 | // create("testApi")
13 | // create("testDebugApi")
14 | // create("testReleaseApi")
15 | // }
16 | //}
17 |
18 | kotlin {
19 | jvm("desktop")
20 | android()
21 | ios()
22 |
23 | sourceSets {
24 | named("commonTest") {
25 | dependencies {
26 | implementation(Deps.JetBrains.Kotlin.testCommon)
27 | implementation(Deps.JetBrains.Kotlin.testAnnotationsCommon)
28 | }
29 | }
30 |
31 | named("androidTest") {
32 | dependencies {
33 | implementation(Deps.JetBrains.Kotlin.testJunit)
34 | }
35 | }
36 | named("desktopTest") {
37 | dependencies {
38 | implementation(Deps.JetBrains.Kotlin.testJunit)
39 | }
40 | }
41 | }
42 |
43 | tasks.withType {
44 | kotlinOptions.jvmTarget = "1.8"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/common/compose-ui/build.gradle.kts:
--------------------------------------------------------------------------------
1 |
2 | import org.jetbrains.compose.compose
3 |
4 | plugins {
5 | id("multiplatform-compose-setup")
6 | id("android-setup")
7 | }
8 |
9 | kotlin {
10 | sourceSets {
11 | named("commonMain") {
12 | dependencies {
13 | implementation(project(":common:main"))
14 | implementation(project(":common:root"))
15 | implementation(Deps.ArkIvanov.Decompose.decompose)
16 | implementation(Deps.ArkIvanov.Decompose.extensionsCompose)
17 |
18 | implementation(Deps.Utils.napier)
19 | }
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/kotlin/com/nikolam/kmm_weather/ui/Composables.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.ui
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.foundation.isSystemInDarkTheme
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.graphics.ColorFilter
8 | import androidx.compose.ui.res.painterResource
9 | import com.nikolam.kmm_weather.common.ui.R
10 |
11 | @Composable
12 | actual fun isDarkMode(): Boolean {
13 | return isSystemInDarkTheme()
14 | }
15 |
16 | @Composable
17 | actual fun KMPImage(
18 | id: Int,
19 | modifier: Modifier,
20 | colorFilter: ColorFilter?,
21 | contentDescription: String
22 | ) {
23 | var resId = R.drawable.angry_clouds
24 |
25 | when (id) {
26 | 1 -> {
27 | resId = R.drawable.ic_water_drop_black_24dp
28 | }
29 | 2 -> {
30 | resId = R.drawable.ic_air_black_24dp
31 | }
32 | in 200..232 -> {
33 | resId = R.drawable.thunder
34 | }
35 | in 300..321 -> {
36 | resId = R.drawable.day_rain
37 | }
38 | in 500..531 -> {
39 | resId = R.drawable.rain
40 | }
41 | in 600..622 -> {
42 | resId = R.drawable.snow
43 | }
44 | 800 -> {
45 | resId = R.drawable.day_clear
46 | }
47 | in 801..804 -> {
48 | resId = R.drawable.cloudy
49 | }
50 | else -> {
51 | resId = R.drawable.day_clear
52 | }
53 | }
54 | Image(
55 | painter = painterResource(resId), modifier = modifier,
56 | contentDescription = contentDescription,
57 | colorFilter = colorFilter
58 | )
59 | }
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/kotlin/com/nikolam/kmm_weather/ui/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.ui
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.view.inputmethod.InputMethodManager
6 |
7 |
8 | fun hideKeyboard(context: Context) {
9 | val imm = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
10 | imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
11 | }
12 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/res/drawable/angry_clouds.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
14 |
17 |
20 |
21 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/res/drawable/cloudy.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
14 |
17 |
20 |
21 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/res/drawable/fog.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
14 |
17 |
20 |
24 |
28 |
32 |
33 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/res/drawable/ic_air_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/res/drawable/ic_water_drop_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/res/drawable/mist.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
14 |
17 |
20 |
24 |
28 |
29 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/res/drawable/night_full_moon_clear.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
10 |
16 |
17 |
18 |
19 |
20 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
67 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/res/drawable/night_half_moon_clear.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
10 |
16 |
17 |
18 |
19 |
20 |
21 |
26 |
31 |
36 |
41 |
46 |
47 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/res/drawable/rain.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
14 |
17 |
20 |
23 |
26 |
29 |
30 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/res/drawable/rain_thunder.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
10 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
35 |
38 |
41 |
46 |
49 |
52 |
53 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/res/drawable/sleet.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
18 |
21 |
24 |
26 |
27 |
32 |
33 |
34 |
35 |
36 |
37 |
39 |
40 |
45 |
46 |
47 |
48 |
49 |
50 |
53 |
54 |
--------------------------------------------------------------------------------
/common/compose-ui/src/androidMain/res/drawable/wind.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
14 |
17 |
20 |
23 |
26 |
27 |
--------------------------------------------------------------------------------
/common/compose-ui/src/commonMain/kotlin/com/nikolam/kmm_weather/ui/Composables.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.ui
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import androidx.compose.ui.graphics.ColorFilter
6 | import androidx.compose.ui.graphics.ImageBitmap
7 |
8 |
9 | @Composable
10 | expect fun isDarkMode(): Boolean
11 |
12 | @Composable
13 | expect fun KMPImage(
14 | id: Int,
15 | modifier: Modifier,
16 | colorFilter: ColorFilter?,
17 | contentDescription: String
18 | )
19 |
20 | //@Composable
21 | //expect fun SearchBox(modifier: Modifier)
22 |
--------------------------------------------------------------------------------
/common/compose-ui/src/commonMain/kotlin/com/nikolam/kmm_weather/ui/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.ui
2 |
3 | import kotlin.math.roundToInt
4 |
5 | fun String.toTempUnit(unit : String) : String{
6 | return if (unit == "C") "$this°"
7 | else (this.toInt() * 1.8f + 32).roundToInt().toString() +"F"
8 | }
--------------------------------------------------------------------------------
/common/compose-ui/src/commonMain/kotlin/com/nikolam/kmm_weather/ui/WeatherRootContent.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("EXPERIMENTAL_API_USAGE")
2 |
3 | package com.nikolam.kmm_weather.ui
4 |
5 | import androidx.compose.runtime.Composable
6 | import com.arkivanov.decompose.extensions.compose.jetbrains.Children
7 | import com.arkivanov.decompose.extensions.compose.jetbrains.animation.child.crossfadeScale
8 | import com.nikolam.kmm_weather.common.root.WeatherRoot
9 |
10 | @Composable
11 | fun WeatherRootContent(component: WeatherRoot) {
12 | Children(routerState = component.routerState, animation = crossfadeScale()) {
13 | when (val child = it.instance) {
14 | is WeatherRoot.Child.Main -> WeatherMainContent(child.component)
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/common/compose-ui/src/commonMain/kotlin/com/nikolam/kmm_weather/ui/colors.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.ui
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val DarkPurple = Color(0xFF5550F2)
6 | val SkyBlue = Color(0xFF8CCEF4)
7 | val MediumPurple = Color(0xFF6187F6)
8 |
9 | val DarkDarkPurple = Color(0xFF00002c)
10 | val DarkSkyBlue = Color(0xFF443381)
11 | val DarkMediumPurple = Color(0xFF110c54)
12 | val DarkTextColor = Color(0xFF87a1ff)
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/kotlin/com.nikolam.kmm_weather.ui/Composables.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.ui
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.graphics.ColorFilter
7 | import androidx.compose.ui.res.imageResource
8 |
9 | @Composable
10 | actual fun isDarkMode(): Boolean {
11 | return false //isSystemInDarkTheme()
12 | }
13 |
14 | @Composable
15 | actual fun KMPImage(
16 | id: Int,
17 | modifier: Modifier,
18 | colorFilter: ColorFilter?,
19 | contentDescription: String
20 | ) {
21 |
22 | var fileName = "images/day_clear.png"
23 |
24 | when (id) {
25 | 1 -> {
26 | fileName = "images/water_drop.png"
27 | }
28 | 2 -> {
29 | fileName = "images/air.png"
30 | }
31 | in 200..232 -> {
32 | fileName = "images/thunder.png"
33 | }
34 | in 300..321 -> {
35 | fileName = "images/day_rain.png"
36 | }
37 | in 500..531 -> {
38 | fileName = "images/rain.png"
39 | }
40 | in 600..622 -> {
41 | fileName = "images/snow.png"
42 | }
43 | 800 -> {
44 | fileName = "images/day_clear.png"
45 | }
46 | in 801..804 -> {
47 | fileName = "images/cloudy.png"
48 | }
49 | else -> {
50 | fileName = "images/day_clear.png"
51 | }
52 | }
53 |
54 | Image(imageResource(fileName),
55 | modifier = modifier,
56 | contentDescription = contentDescription,
57 | colorFilter = colorFilter
58 | )
59 | }
60 |
61 | //@Composable
62 | //actual fun SearchBox(modifier: Modifier) {
63 | //}
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/air.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/air.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/angry_clouds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/angry_clouds.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/cloudy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/cloudy.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/day_clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/day_clear.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/day_partial_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/day_partial_cloud.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/day_rain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/day_rain.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/day_rain_thunder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/day_rain_thunder.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/day_sleet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/day_sleet.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/day_snow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/day_snow.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/day_snow_thunder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/day_snow_thunder.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/fog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/fog.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/mist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/mist.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_full_moon_clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_full_moon_clear.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_full_moon_partial_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_full_moon_partial_cloud.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_full_moon_rain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_full_moon_rain.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_full_moon_rain_thunder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_full_moon_rain_thunder.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_full_moon_sleet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_full_moon_sleet.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_full_moon_snow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_full_moon_snow.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_full_moon_snow_thunder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_full_moon_snow_thunder.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_half_moon_clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_half_moon_clear.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_half_moon_partial_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_half_moon_partial_cloud.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_half_moon_rain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_half_moon_rain.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_half_moon_rain_thunder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_half_moon_rain_thunder.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_half_moon_sleet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_half_moon_sleet.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_half_moon_snow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_half_moon_snow.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/night_half_moon_snow_thunder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/night_half_moon_snow_thunder.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/overcast.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/overcast.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/rain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/rain.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/rain_thunder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/rain_thunder.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/sleet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/sleet.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/snow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/snow.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/snow_thunder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/snow_thunder.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/thunder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/thunder.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/tornado.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/tornado.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/water_drop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/water_drop.png
--------------------------------------------------------------------------------
/common/compose-ui/src/desktopMain/resources/images/wind.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/common/compose-ui/src/desktopMain/resources/images/wind.png
--------------------------------------------------------------------------------
/common/main/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
2 | import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
3 |
4 | plugins {
5 | id("multiplatform-setup")
6 | id("android-setup")
7 | id("kotlinx-serialization")
8 | }
9 |
10 | kotlin {
11 | sourceSets {
12 | named("commonMain") {
13 | dependencies {
14 | implementation(project(":common:utils"))
15 | // implementation(project(":common:database"))
16 | implementation(Deps.ArkIvanov.Decompose.decompose)
17 | implementation(Deps.ArkIvanov.MVIKotlin.mvikotlin)
18 | // implementation(Deps.ArkIvanov.MVIKotlin.mvikotlinExtensionsReaktive)
19 | implementation(Deps.ArkIvanov.MVIKotlin.mvikotlinExtensionsCoroutines)
20 | implementation(Deps.Badoo.Reaktive.reaktive)
21 |
22 | implementation(Deps.Utils.napier)
23 |
24 | implementation(Deps.JetBrains.Ktor.core)
25 | implementation(Deps.JetBrains.Ktor.json)
26 | implementation(Deps.JetBrains.Ktor.serialization)
27 | }
28 | }
29 |
30 | named("androidMain") {
31 | dependencies {
32 | implementation(Deps.Utils.napier)
33 |
34 | implementation(Deps.JetBrains.Ktor.android)
35 | implementation(Deps.JetBrains.Ktor.jsonJVM)
36 | implementation(Deps.JetBrains.Ktor.serializationJVM)
37 | //implementation(Deps.JetBrains.Ktor.logging)
38 | }
39 | }
40 |
41 | // named("iosMain") {
42 | // dependencies {
43 | // implementation(Deps.JetBrains.Ktor.ios)
44 | // implementation(Deps.JetBrains.Ktor.iosSerialization)
45 | // // implementation(Deps.JetBrains.Ktor.jsonNative)
46 | // }
47 | // }
48 |
49 | named("commonTest") {
50 | dependencies {
51 | implementation(Deps.ArkIvanov.MVIKotlin.mvikotlinMain)
52 | implementation(Deps.Badoo.Reaktive.reaktiveTesting)
53 | implementation(Deps.Badoo.Reaktive.utils)
54 | }
55 | }
56 | }
57 |
58 | targets.getByName("iosX64").compilations.forEach {
59 | it.kotlinOptions.freeCompilerArgs += arrayOf("-linker-options", "-lsqlite3")
60 | }
61 | }
--------------------------------------------------------------------------------
/common/main/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/common/main/src/commonMain/kotlin/com.nikolam.kmm_weather.common.main/WeatherMain.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.main
2 |
3 | import com.arkivanov.decompose.value.Value
4 | import com.nikolam.kmm_weather.common.main.data.model.CurrentWeatherModel
5 |
6 | interface WeatherMain {
7 | val models : Value
8 |
9 | fun onItemClicked(id : Long)
10 |
11 | data class Model(
12 | val currentWeather : CurrentWeatherModel? = null,
13 | val isLoading : Boolean = true,
14 | val isError : Boolean = true
15 | )
16 |
17 | sealed class Output {
18 | data class SelectedDay(val id : Long) : Output()
19 | }
20 |
21 |
22 | }
--------------------------------------------------------------------------------
/common/main/src/commonMain/kotlin/com.nikolam.kmm_weather.common.main/data/model/CurrentWeatherModel.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.main.data.model
2 |
3 | data class CurrentWeatherModel(val weatherID : Int, val daily : List, val temp: Int, val wind : Int, val humidity : Int, val weatherDesc : String
4 | )
5 | data class DailyWeatherModel(val weatherID : Int, val index : Int, val temp: Int)
--------------------------------------------------------------------------------
/common/main/src/commonMain/kotlin/com.nikolam.kmm_weather.common.main/data/model/CurrentWeatherNetworkModel.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.main.data.model
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class CurrentWeatherNetworkModel(
8 | val lat: Double,
9 | val lon: Double,
10 | val timezone: String,
11 |
12 | @SerialName("timezone_offset")
13 | val timezoneOffset: Long,
14 |
15 | val current: Current,
16 | val hourly: List,
17 | val daily: List,
18 | var alerts: List = listOf()
19 | ) {
20 | fun toBusinessModel(): CurrentWeatherModel {
21 | val newList = arrayListOf()
22 | for (i in daily.indices) {
23 | val temp = daily[i].temp.day
24 | newList.add(DailyWeatherModel(daily[i].weather[0].id.toInt(), i, temp.toInt()))
25 | }
26 |
27 | return CurrentWeatherModel(
28 | weatherID = current.weather[0].id.toInt(), newList, current.temp.toInt(),
29 | current.windSpeed.toInt(), current.humidity.toInt(), current.weather[0].description
30 | )
31 | }
32 | }
33 |
34 | @Serializable
35 | data class Alert(
36 | @SerialName("sender_name")
37 | val senderName: String,
38 |
39 | val event: String,
40 | val start: Long,
41 | val end: Long,
42 | val description: String
43 | )
44 |
45 | @Serializable
46 | data class Current(
47 | val dt: Long,
48 | val sunrise: Long? = null,
49 | val sunset: Long? = null,
50 | val temp: Double,
51 |
52 | @SerialName("feels_like")
53 | val feelsLike: Double,
54 |
55 | val pressure: Long,
56 | val humidity: Long,
57 |
58 | @SerialName("dew_point")
59 | val dewPoint: Double,
60 |
61 | val uvi: Double,
62 | val clouds: Long,
63 | val visibility: Long,
64 |
65 | @SerialName("wind_speed")
66 | val windSpeed: Double,
67 |
68 | @SerialName("wind_deg")
69 | val windDeg: Long,
70 |
71 | val weather: List,
72 |
73 | @SerialName("wind_gust")
74 | val windGust: Double? = null
75 | )
76 |
77 | @Serializable
78 | data class Rain(
79 | @SerialName("1h")
80 | val the1H: Double
81 | )
82 |
83 | @Serializable
84 | data class Weather(
85 | val id: Long,
86 | val main: String,
87 | val description: String,
88 | val icon: String
89 | )
90 |
91 | @Serializable
92 | data class Daily(
93 | val dt: Long,
94 | val sunrise: Long,
95 | val sunset: Long,
96 | val moonrise: Long,
97 | val moonset: Long,
98 |
99 | @SerialName("moon_phase")
100 | val moonPhase: Double,
101 |
102 | val temp: Temp,
103 |
104 | @SerialName("feels_like")
105 | val feelsLike: FeelsLike,
106 |
107 | val pressure: Long,
108 | val humidity: Long,
109 |
110 | @SerialName("dew_point")
111 | val dewPoint: Double,
112 |
113 | @SerialName("wind_speed")
114 | val windSpeed: Double,
115 |
116 | @SerialName("wind_deg")
117 | val windDeg: Long,
118 |
119 | val weather: List,
120 | val clouds: Long,
121 | val pop: Double,
122 | val uvi: Double
123 | )
124 |
125 | @Serializable
126 | data class FeelsLike(
127 | val day: Double,
128 | val night: Double,
129 | val eve: Double,
130 | val morn: Double
131 | )
132 |
133 | @Serializable
134 | data class Temp(
135 | val day: Double,
136 | val min: Double,
137 | val max: Double,
138 | val night: Double,
139 | val eve: Double,
140 | val morn: Double
141 | )
142 |
--------------------------------------------------------------------------------
/common/main/src/commonMain/kotlin/com.nikolam.kmm_weather.common.main/data/network/WeatherAPI.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.main.data.network
2 |
3 | import com.nikolam.kmm_weather.common.main.data.model.CurrentWeatherModel
4 | import com.nikolam.kmm_weather.common.main.data.model.CurrentWeatherNetworkModel
5 | import io.ktor.client.*
6 | import io.ktor.client.engine.*
7 | import io.ktor.client.features.json.*
8 | import io.ktor.client.features.json.serializer.*
9 | import io.ktor.client.request.*
10 | import io.ktor.client.statement.*
11 | import io.ktor.http.*
12 | import kotlinx.serialization.json.Json
13 | import io.github.aakira.napier.Napier
14 |
15 | class WeatherAPI(clientEngine: HttpClientEngine) {
16 |
17 | private val client = HttpClient(clientEngine) {
18 | install(JsonFeature) {
19 | serializer = KotlinxSerializer()
20 | }
21 | }
22 |
23 | suspend fun getWeather(): CurrentWeatherModel {
24 | // Actually we're able to just return the get()-call and Ktor's JsonFeature will automatically do the
25 | // JSON parsing for us. However, this currently doesn't work with Kotlin/Native as it doesn't support reflection
26 | // and we have to manually use PopularMoviesEntity.serializer()
27 | val response = client.get {
28 | url {
29 | protocol = URLProtocol.HTTPS
30 | host = "api.openweathermap.org/"
31 | encodedPath = "/data/2.5/onecall"
32 | // parameter("q", "Belgrade")
33 | parameter("appid", "f3a39ee4cb7053b4b27f3bbb8bca11c8")
34 | parameter("units", "metric")
35 | parameter("lat", 44.787197)
36 | parameter("lon", 20.457273)
37 | // header(HEADER_AUTHORIZATION, API_KEY.asBearerToken())
38 | }
39 | }
40 |
41 | val jsonBody = response.readText()
42 | val netModel = Json {
43 | ignoreUnknownKeys = true
44 | }.decodeFromString(CurrentWeatherNetworkModel.serializer(), jsonBody)
45 |
46 | return netModel.toBusinessModel()
47 | }
48 | }
--------------------------------------------------------------------------------
/common/main/src/commonMain/kotlin/com.nikolam.kmm_weather.common.main/integration/Mappers.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.main.integration
2 |
3 | import com.nikolam.kmm_weather.common.main.store.WeatherMainStore
4 | import com.nikolam.kmm_weather.common.main.WeatherMain
5 |
6 | internal val STATE_TO: (WeatherMainStore.State) -> WeatherMain.Model =
7 | {
8 | WeatherMain.Model(
9 | currentWeather = it.currentWeather,
10 | isError = it.isError,
11 | isLoading = it.isLoading
12 | )
13 | }
--------------------------------------------------------------------------------
/common/main/src/commonMain/kotlin/com.nikolam.kmm_weather.common.main/integration/WeatherMainComponent.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.main.integration
2 |
3 | import com.nikolam.kmm_weather.common.utils.asValue
4 | import com.arkivanov.decompose.ComponentContext
5 | import com.arkivanov.decompose.value.Value
6 | import com.arkivanov.decompose.value.operator.map
7 | import com.arkivanov.mvikotlin.core.store.StoreFactory
8 | import com.badoo.reaktive.base.Consumer
9 | import com.badoo.reaktive.base.invoke
10 | import com.nikolam.kmm_weather.common.main.store.WeatherMainStoreProvider
11 | import com.nikolam.kmm_weather.common.utils.getStore
12 | import com.nikolam.kmm_weather.common.main.WeatherMain
13 |
14 | class WeatherMainComponent(
15 | componentContext: ComponentContext,
16 | storeFactory: StoreFactory,
17 | private val output: Consumer
18 | ) : WeatherMain, ComponentContext by componentContext {
19 |
20 | private val store =
21 | instanceKeeper.getStore {
22 | WeatherMainStoreProvider(
23 | storeFactory = storeFactory,
24 | /// database = TodoMainStoreDatabase(queries = database.todoDatabaseQueries)
25 | ).provide()
26 | }
27 |
28 | override val models: Value = store.asValue().map(STATE_TO)
29 |
30 | override fun onItemClicked(id: Long) {
31 | output(WeatherMain.Output.SelectedDay(id = id))
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/common/main/src/commonMain/kotlin/com.nikolam.kmm_weather.common.main/store/WeatherMainStore.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.main.store
2 |
3 | import com.arkivanov.mvikotlin.core.store.Store
4 | import com.nikolam.kmm_weather.common.main.data.model.CurrentWeatherModel
5 |
6 |
7 | internal interface WeatherMainStore : Store {
8 |
9 | sealed class Intent {
10 | }
11 |
12 | data class State(
13 | val currentWeather : CurrentWeatherModel? = null,
14 | val isLoading : Boolean = true,
15 | val isError : Boolean = true
16 | )
17 | }
--------------------------------------------------------------------------------
/common/main/src/commonMain/kotlin/com.nikolam.kmm_weather.common.main/store/WeatherMainStoreProvider.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.main.store
2 |
3 | import com.arkivanov.mvikotlin.core.store.Reducer
4 | import com.arkivanov.mvikotlin.core.store.SimpleBootstrapper
5 | import com.arkivanov.mvikotlin.core.store.Store
6 | import com.arkivanov.mvikotlin.core.store.StoreFactory
7 | import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
8 | import com.nikolam.kmm_weather.common.main.data.model.CurrentWeatherModel
9 | import com.nikolam.kmm_weather.common.main.data.network.WeatherAPI
10 | import com.nikolam.kmm_weather.common.main.store.WeatherMainStore.State
11 | import com.nikolam.kmm_weather.common.utils.PlatformServiceLocator
12 | import io.github.aakira.napier.Napier
13 |
14 | internal class WeatherMainStoreProvider(
15 | private val storeFactory: StoreFactory,
16 | ) {
17 |
18 | private val weatherAPI by lazy { WeatherAPI(PlatformServiceLocator.httpClientEngine) }
19 |
20 | fun provide(): WeatherMainStore =
21 | object : WeatherMainStore,
22 | Store by storeFactory.create(
23 | name = "TodoListStore",
24 | initialState = State(),
25 | bootstrapper = SimpleBootstrapper(Unit),
26 | executorFactory = ::ExecutorImpl,
27 | reducer = ReducerImpl
28 | ) {}
29 |
30 |
31 | private sealed class Result {
32 | data class WeatherLoaded(
33 | val currentWeather: CurrentWeatherModel
34 | ) : Result()
35 |
36 | data class ItemsLoadFailed(val err: Int) : Result()
37 | }
38 |
39 | private inner class ExecutorImpl :
40 | SuspendExecutor() {
41 | override suspend fun executeAction(action: Unit, getState: () -> State) {
42 | try {
43 | val m = weatherAPI.getWeather()
44 | Napier.d("Sucess $m\n", tag = "my_tag")
45 |
46 | dispatch(Result.WeatherLoaded(m))
47 | } catch (e: Exception) {
48 | print(e.message)
49 | Napier.e(e.toString(), e, tag = "my_tag")
50 | dispatch(Result.ItemsLoadFailed(-1))
51 | }
52 | }
53 | }
54 |
55 | private object ReducerImpl : Reducer {
56 | override fun State.reduce(result: Result): State =
57 | when (result) {
58 | is Result.WeatherLoaded -> copy(
59 | currentWeather = result.currentWeather,
60 | isLoading = false,
61 | isError = false
62 | )
63 | is Result.ItemsLoadFailed -> copy(isLoading = false, isError = true)
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/common/root/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
2 |
3 | plugins {
4 | id("multiplatform-setup")
5 | id("android-setup")
6 | id("kotlin-parcelize")
7 | }
8 |
9 | kotlin {
10 | ios {
11 | binaries {
12 | framework {
13 | baseName = "Weather"
14 | export(project(":common:main"))
15 | export(project(":common:utils"))
16 | export(Deps.ArkIvanov.Decompose.decompose)
17 | export(Deps.ArkIvanov.MVIKotlin.mvikotlinMain)
18 | }
19 | }
20 | }
21 |
22 | sourceSets {
23 | named("commonMain") {
24 | dependencies {
25 | implementation(project(":common:utils"))
26 | // implementation(project(":common:database"))
27 | implementation(project(":common:main"))
28 | // implementation(project(":common:edit"))
29 | implementation(Deps.ArkIvanov.MVIKotlin.mvikotlin)
30 | implementation(Deps.ArkIvanov.Decompose.decompose)
31 | implementation(Deps.Badoo.Reaktive.reaktive)
32 | }
33 | }
34 | }
35 |
36 | sourceSets {
37 | named("iosMain") {
38 | dependencies {
39 | // api(project(":common:database"))
40 | api(project(":common:main"))
41 | // api(project(":common:edit"))
42 | api(Deps.ArkIvanov.Decompose.decompose)
43 | api(Deps.ArkIvanov.MVIKotlin.mvikotlinMain)
44 | }
45 | }
46 | }
47 | }
48 |
49 | fun getIosTarget(): String {
50 | val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator"
51 |
52 | return if (sdkName.startsWith("iphoneos")) "iosArm64" else "iosX64"
53 | }
54 |
55 | val packForXcode by tasks.creating(Sync::class) {
56 | group = "build"
57 | val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
58 | val targetName = getIosTarget()
59 | val framework = kotlin.targets.getByName(targetName).binaries.getFramework(mode)
60 | inputs.property("mode", mode)
61 | dependsOn(framework.linkTask)
62 | val targetDir = File(buildDir, "xcode-frameworks")
63 | from(framework.outputDirectory)
64 | into(targetDir)
65 | }
66 |
--------------------------------------------------------------------------------
/common/root/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/common/root/src/commonMain/kotlin/com.nikolam.kmm_weather.common.root/WeatherRoot.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.root
2 |
3 | import com.arkivanov.decompose.RouterState
4 | import com.arkivanov.decompose.value.Value
5 | import com.nikolam.kmm_weather.common.main.WeatherMain
6 |
7 | interface WeatherRoot {
8 | val routerState : Value>
9 |
10 | sealed class Child {
11 | data class Main(val component: WeatherMain) : Child()
12 | }
13 | }
--------------------------------------------------------------------------------
/common/root/src/commonMain/kotlin/com.nikolam.kmm_weather.common.root/integration/WeatherRootComponent.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.root.integration
2 |
3 | import com.arkivanov.decompose.ComponentContext
4 | import com.arkivanov.decompose.RouterState
5 | import com.arkivanov.decompose.router
6 | import com.arkivanov.decompose.statekeeper.Parcelable
7 | import com.arkivanov.decompose.statekeeper.Parcelize
8 | import com.arkivanov.decompose.value.Value
9 | import com.arkivanov.mvikotlin.core.store.StoreFactory
10 | import com.badoo.reaktive.base.Consumer
11 | import com.nikolam.kmm_weather.common.root.WeatherRoot
12 | import com.nikolam.kmm_weather.common.main.WeatherMain
13 | import com.nikolam.kmm_weather.common.main.integration.WeatherMainComponent
14 | import com.nikolam.kmm_weather.common.root.WeatherRoot.*
15 | import com.nikolam.kmm_weather.common.utils.Consumer
16 |
17 | class WeatherRootComponent internal constructor(
18 | componentContext: ComponentContext,
19 | private val weatherMain : (ComponentContext, Consumer) -> WeatherMain
20 | ) : WeatherRoot, ComponentContext by componentContext{
21 |
22 | constructor(
23 | componentContext: ComponentContext,
24 | storeFactory: StoreFactory
25 | ) : this(
26 | componentContext = componentContext,
27 | weatherMain = { childContext, output ->
28 | WeatherMainComponent(
29 | componentContext = childContext,
30 | storeFactory = storeFactory,
31 | output = output
32 | )
33 | }
34 | )
35 |
36 | private val router =
37 | router(
38 | initialConfiguration = Configuration.Main,
39 | handleBackButton = true,
40 | childFactory = ::createChild
41 | )
42 |
43 | override val routerState: Value> = router.state
44 |
45 | private fun createChild(configuration: Configuration, componentContext: ComponentContext): Child =
46 | when (configuration) {
47 | is Configuration.Main -> Child.Main(weatherMain(componentContext, Consumer(::onMainOutput)))
48 | }
49 |
50 | private fun onMainOutput(output: WeatherMain.Output): Unit =
51 | when (output) {
52 | is WeatherMain.Output.SelectedDay -> {}// router.push(Configuration.Edit(itemId = output.id))
53 | }
54 |
55 |
56 | private sealed class Configuration : Parcelable {
57 | @Parcelize
58 | object Main : Configuration()
59 | }
60 |
61 | }
--------------------------------------------------------------------------------
/common/utils/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("multiplatform-setup")
3 | id("android-setup")
4 | id("kotlinx-serialization")
5 | }
6 |
7 | kotlin {
8 | sourceSets {
9 | named("commonMain") {
10 | dependencies {
11 | implementation(Deps.ArkIvanov.MVIKotlin.rx)
12 | implementation(Deps.ArkIvanov.MVIKotlin.mvikotlin)
13 | implementation(Deps.ArkIvanov.Decompose.decompose)
14 | implementation(Deps.Badoo.Reaktive.reaktive)
15 |
16 | implementation(Deps.JetBrains.Ktor.core)
17 | }
18 | }
19 |
20 | named("androidMain") {
21 | dependencies {
22 | implementation(Deps.JetBrains.Ktor.android)
23 | implementation(Deps.JetBrains.Ktor.jsonJVM)
24 | implementation(Deps.JetBrains.Ktor.serializationJVM)
25 | // implementation(Deps.JetBrains.Ktor.logging)
26 | implementation(Deps.JetBrains.Ktor.okhttp)
27 | }
28 | }
29 |
30 |
31 | named("iosMain") {
32 | dependencies {
33 | implementation(Deps.JetBrains.Ktor.ios)
34 | }
35 | }
36 |
37 | named("desktopMain"){
38 | dependencies{
39 | implementation(Deps.JetBrains.Ktor.core)
40 | implementation(Deps.JetBrains.Ktor.javaClient)
41 | // implementation(Deps.JetBrains.Ktor.curlClient)
42 | }
43 | }
44 |
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/common/utils/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/common/utils/src/androidMain/kotlin/com.nikolam.kmm_weather.common.utils/PlatformServiceLocator.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.utils
2 |
3 | import io.ktor.client.engine.HttpClientEngine
4 | import io.ktor.client.engine.okhttp.OkHttp
5 |
6 | actual object PlatformServiceLocator {
7 | actual val httpClientEngine: HttpClientEngine by lazy {
8 | OkHttp.create {
9 | // val networkInterceptor = HttpLoggingInterceptor().apply {
10 | // level = HttpLoggingInterceptor.Level.BODY
11 | // }
12 | // addNetworkInterceptor(networkInterceptor)
13 | // }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/common/utils/src/commonMain/kotlin/com.nikolam.kmm_weather.common.utils/InstanceKeeperExt.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.utils
2 |
3 | import com.arkivanov.decompose.instancekeeper.InstanceKeeper
4 | import com.arkivanov.decompose.instancekeeper.getOrCreate
5 | import com.arkivanov.mvikotlin.core.store.Store
6 |
7 | fun > InstanceKeeper.getStore(key: Any, factory: () -> T): T =
8 | getOrCreate(key) { StoreHolder(factory()) }
9 | .store
10 |
11 | inline fun > InstanceKeeper.getStore(noinline factory: () -> T): T =
12 | getStore(T::class, factory)
13 |
14 | private class StoreHolder>(
15 | val store: T
16 | ) : InstanceKeeper.Instance {
17 | override fun onDestroy() {
18 | store.dispose()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/common/utils/src/commonMain/kotlin/com.nikolam.kmm_weather.common.utils/PlatformServiceLocator.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.utils
2 |
3 | import io.ktor.client.engine.*
4 |
5 | /**
6 | * Contains some expected dependencies for the [ServiceLocator] that have to be resolved by Android/iOS.
7 | */
8 | expect object PlatformServiceLocator {
9 | val httpClientEngine: HttpClientEngine
10 | }
--------------------------------------------------------------------------------
/common/utils/src/commonMain/kotlin/com.nikolam.kmm_weather.common.utils/ReaktiveExt.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.utils
2 |
3 | import com.badoo.reaktive.base.Consumer
4 |
5 | @Suppress("FunctionName") // Factory function
6 | inline fun Consumer(crossinline block: (T) -> Unit): Consumer =
7 | object : Consumer {
8 | override fun onNext(value: T) {
9 | block(value)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/common/utils/src/commonMain/kotlin/com.nikolam.kmm_weather.common.utils/StoreExt.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.utils
2 |
3 | import com.arkivanov.decompose.value.Value
4 | import com.arkivanov.decompose.value.ValueObserver
5 | import com.arkivanov.mvikotlin.core.store.Store
6 | import com.arkivanov.mvikotlin.rx.Disposable
7 |
8 | fun Store<*, T, *>.asValue(): Value =
9 | object : Value() {
10 | override val value: T get() = state
11 | private var disposables = emptyMap, Disposable>()
12 |
13 | override fun subscribe(observer: ValueObserver) {
14 | val disposable = states(com.arkivanov.mvikotlin.rx.observer(onNext = observer))
15 | this.disposables += observer to disposable
16 | }
17 |
18 | override fun unsubscribe(observer: ValueObserver) {
19 | val disposable = disposables[observer] ?: return
20 | this.disposables -= observer
21 | disposable.dispose()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/common/utils/src/desktopMain/kotlin/com.nikolam.kmm_weather.common.utils/PlatformServiceLocator.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.utils
2 |
3 | import io.ktor.client.*
4 | import io.ktor.client.engine.*
5 | import io.ktor.client.engine.java.*
6 |
7 | actual object PlatformServiceLocator {
8 | actual val httpClientEngine: HttpClientEngine = Java.create() {
9 | // this: JavaHttpConfig
10 | threadsCount = 8
11 | pipelining = true
12 | }
13 | }
--------------------------------------------------------------------------------
/common/utils/src/iosMain/kotlin/com.nikolam.kmm_weather.common.utils/PlatformServiceLocator.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.common.utils
2 |
3 | import io.ktor.client.engine.*
4 | import io.ktor.client.engine.ios.*
5 |
6 | actual object PlatformServiceLocator {
7 | actual val httpClientEngine: HttpClientEngine = Ios.create()
8 | }
--------------------------------------------------------------------------------
/desktop/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.compose.compose
2 | import org.jetbrains.compose.desktop.application.dsl.TargetFormat
3 |
4 | plugins {
5 | kotlin("multiplatform") // kotlin("jvm") doesn't work well in IDEA/AndroidStudio (https://github.com/JetBrains/compose-jb/issues/22)
6 | id("org.jetbrains.compose")
7 | }
8 |
9 | kotlin {
10 | jvm {
11 | withJava()
12 | }
13 |
14 | sourceSets {
15 | named("jvmMain") {
16 | dependencies {
17 | implementation(compose.desktop.currentOs)
18 | implementation(project(":common:utils"))
19 | implementation(project(":common:root"))
20 | implementation(project(":common:compose-ui"))
21 | implementation(Deps.ArkIvanov.Decompose.decompose)
22 | implementation(Deps.ArkIvanov.Decompose.extensionsCompose)
23 | implementation(Deps.ArkIvanov.MVIKotlin.mvikotlin)
24 | implementation(Deps.ArkIvanov.MVIKotlin.mvikotlinMain)
25 | implementation(Deps.Badoo.Reaktive.reaktive)
26 | implementation(Deps.Badoo.Reaktive.coroutinesInterop)
27 | }
28 | }
29 | }
30 | }
31 |
32 | compose.desktop {
33 | application {
34 | mainClass = "com.nikolam.kmm_weather.desktop.MainKt"
35 |
36 | nativeDistributions {
37 | targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
38 | packageName = "WeatherApp"
39 | packageVersion = "1.0.0"
40 |
41 | modules("java.sql")
42 |
43 | windows {
44 | menuGroup = "Compose Examples"
45 | // see https://wixtoolset.org/documentation/manual/v3/howtos/general/generate_guids.html
46 | upgradeUuid = "BF9CDA6A-1391-46D5-9ED5-383D6E68CCEB"
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/desktop/src/jvmMain/kotlin/com.nikolam.kmm_weather.desktop/Main.kt:
--------------------------------------------------------------------------------
1 | package com.nikolam.kmm_weather.desktop
2 |
3 | import androidx.compose.desktop.DesktopTheme
4 | import androidx.compose.desktop.Window
5 | import androidx.compose.foundation.layout.fillMaxSize
6 | import androidx.compose.material.MaterialTheme
7 | import androidx.compose.material.Surface
8 | import androidx.compose.material.Text
9 | import androidx.compose.material.TextField
10 | import androidx.compose.ui.Modifier
11 | import com.arkivanov.decompose.ComponentContext
12 | import com.arkivanov.decompose.extensions.compose.jetbrains.rememberRootComponent
13 | import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
14 | import com.badoo.reaktive.coroutinesinterop.asScheduler
15 | import com.badoo.reaktive.scheduler.overrideSchedulers
16 | import com.nikolam.kmm_weather.common.root.WeatherRoot
17 | import com.nikolam.kmm_weather.common.root.integration.WeatherRootComponent
18 | import com.nikolam.kmm_weather.ui.WeatherRootContent
19 | import kotlinx.coroutines.Dispatchers
20 |
21 | fun main() {
22 | //overrideSchedulers(main = Dispatchers.Main::asScheduler)
23 |
24 | Window("Weather") {
25 | Surface(modifier = Modifier.fillMaxSize()) {
26 | MaterialTheme {
27 | DesktopTheme {
28 | WeatherRootContent(rememberRootComponent(factory = ::weatherRoot))
29 | }
30 | }
31 | }
32 | }
33 | }
34 |
35 | private fun weatherRoot(componentContext: ComponentContext): WeatherRoot =
36 | WeatherRootComponent(
37 | componentContext = componentContext,
38 | storeFactory = DefaultStoreFactory
39 | )
40 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dkotlin.daemon.jvm.options=--illegal-access=permit
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 | org.gradle.parallel=true
23 | org.gradle.caching=true
24 | kotlin.native.disableCompilerDaemon=true
25 | kotlin.mpp.enableGranularSourceSetsMetadata=true
26 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri May 21 18:00:24 CEST 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-bin.zip
7 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // kmm_weather
4 | //
5 | // Created by Nikola Milovic on 19/06/2021.
6 | //
7 | import UIKit
8 |
9 | @UIApplicationMain
10 | class AppDelegate: UIResponder, UIApplicationDelegate {
11 |
12 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
13 | // Override point for customization after application launch.
14 | return true
15 | }
16 |
17 | // MARK: UISceneSession Lifecycle
18 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
19 | // Called when a new scene session is being created.
20 | // Use this method to select a configuration to create the new scene with.
21 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
22 | }
23 |
24 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
25 | // Called when the user discards a scene session.
26 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
27 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
28 | }
29 |
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/cloudy.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "cloudy.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/cloudy.imageset/cloudy.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
39 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/day_clear.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "day_clear.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/day_clear.imageset/day_clear.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
26 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/day_partial_cloud.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "day_partial_cloud.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/day_partial_cloud.imageset/day_partial_cloud.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
55 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/day_rain.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "day_rain.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/day_rain.imageset/day_rain.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
57 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/day_rain_thunder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "day_rain_thunder.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/day_rain_thunder.imageset/day_rain_thunder.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
61 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/day_sleet.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "day_sleet.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/day_snow.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "day_snow.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/day_snow_thunder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "day_snow_thunder.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/fog.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "fog.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/fog.imageset/fog.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
40 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/mist.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "mist.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/mist.imageset/mist.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
39 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/night_clear.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "night_clear.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/night_clear.imageset/night_clear.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/night_partial_cloud.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "night_partial_cloud.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/night_partial_cloud.imageset/night_partial_cloud.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
46 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/night_rain.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "night_rain.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/night_rain.imageset/night_rain.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
48 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/night_rain_thunder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "night_rain_thunder.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/night_rain_thunder.imageset/night_rain_thunder.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
53 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/night_sleet.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "night_sleet.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/night_sleet.imageset/night_sleet.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
56 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/night_snow.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "night_snow.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/night_snow_thunder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "night_snow_thunder.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/overcast.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "overcast.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/rain.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "rain.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/rain.imageset/rain.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
41 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/rain_thunder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "rain_thunder.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/rain_thunder.imageset/rain_thunder.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
46 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/sleet.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "sleet.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/sleet.imageset/sleet.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
49 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/snow.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "snow.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/snow.imageset/snow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
56 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/snow_thunder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "snow_thunder.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/thunder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "thunder.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/thunder.imageset/thunder.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
49 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/tornado.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "tornado.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/tornado.imageset/tornado.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/wind.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "wind.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Assets.xcassets/wind.imageset/wind.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
41 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/ComponentHolder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ComponentHolder.swift
3 | // kmm_weather
4 | //
5 | // Created by Nikola Milovic on 19/06/2021.
6 | //
7 |
8 | import Weather
9 |
10 | class ComponentHolder {
11 | let lifecycle: LifecycleRegistry
12 | let component: T
13 |
14 | init(factory: (ComponentContext) -> T) {
15 | let lifecycle = LifecycleRegistryKt.LifecycleRegistry()
16 | let component = factory(DefaultComponentContext(lifecycle: lifecycle))
17 | self.lifecycle = lifecycle
18 | self.component = component
19 |
20 | lifecycle.onCreate()
21 | }
22 |
23 | deinit {
24 | lifecycle.onDestroy()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // kmm_weather
4 | //
5 | // Created by Nikola Milovic on 12/06/2021.
6 | //
7 |
8 | import SwiftUI
9 | import Weather
10 |
11 | struct ContentView: View {
12 | @State
13 | private var componentHolder = ComponentHolder{ WeatherRootComponent(componentContext: $0, storeFactory: DefaultStoreFactory())}
14 |
15 | var body: some View {
16 | RootView(componentHolder.component)
17 | .onAppear { LifecycleRegistryExtKt.resume(self.componentHolder.lifecycle) }
18 | .onDisappear { LifecycleRegistryExtKt.stop(self.componentHolder.lifecycle) }
19 | }
20 | }
21 |
22 | struct ContentView_Previews: PreviewProvider {
23 | static var previews: some View {
24 | ContentView()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UISceneConfigurations
26 |
27 | UIWindowSceneSessionRoleApplication
28 |
29 |
30 | UISceneDelegateClassName
31 | $(PRODUCT_MODULE_NAME).SceneDelegate
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneStoryboardFile
35 | LaunchScreen
36 |
37 |
38 |
39 | UIApplicationSupportsMultipleScenes
40 |
41 |
42 | UIApplicationSupportsIndirectInputEvents
43 |
44 | UILaunchScreen
45 |
46 | UILaunchStoryboardName
47 | LaunchScreen
48 | UIRequiredDeviceCapabilities
49 |
50 | armv7
51 |
52 | UISupportedInterfaceOrientations
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationLandscapeLeft
56 | UIInterfaceOrientationLandscapeRight
57 |
58 | UISupportedInterfaceOrientations~ipad
59 |
60 | UIInterfaceOrientationPortrait
61 | UIInterfaceOrientationPortraitUpsideDown
62 | UIInterfaceOrientationLandscapeLeft
63 | UIInterfaceOrientationLandscapeRight
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/MutableValueBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MutableValueBuilder.swift
3 | // kmm_weather
4 | //
5 | // Created by Nikola Milovic on 19/06/2021.
6 | //
7 |
8 | import Foundation
9 | import Weather
10 |
11 | func valueOf(_ value: T) -> Value {
12 | return MutableValueBuilderKt.MutableValue(initialValue: value) as! MutableValue
13 | }
14 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/ObservableValue.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ObservableValue.swift
3 | // kmm_weather
4 | //
5 | // Created by Nikola Milovic on 19/06/2021.
6 | //
7 |
8 | import SwiftUI
9 | import Weather
10 |
11 | public class ObservableValue : ObservableObject {
12 | private let observableValue: Value
13 |
14 | @Published
15 | var value: T
16 |
17 | private var observer: ((T) -> Void)?
18 |
19 | init(_ value: Value) {
20 | self.observableValue = value
21 | self.value = observableValue.value
22 |
23 | self.observer = { value in
24 | self.value = value
25 | }
26 | observableValue.subscribe(observer: observer!)
27 | }
28 |
29 | deinit {
30 | self.observableValue.unsubscribe(observer: self.observer!)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/RootView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootView.swift
3 | // kmm_weather
4 | //
5 | // Created by Nikola Milovic on 19/06/2021.
6 | //
7 |
8 | import SwiftUI
9 | import Weather
10 |
11 | struct RootView: View {
12 | @ObservedObject
13 | private var component: ObservableValue
14 |
15 | init(_ component: WeatherRoot) {
16 | self.component = ObservableValue(valueOf(component))
17 | }
18 |
19 | var body: some View {
20 | let weatherMain = component.value.routerState.value.activeChild.instance as! WeatherRootChild.Main
21 | MainView(weatherMain.component as! WeatherMainComponent)
22 | }
23 | }
24 |
25 | struct RootView_Previews: PreviewProvider {
26 | static var previews: some View {
27 | RootView(StubWeatherRoot())
28 | .previewDevice("iPhone 8")
29 | }
30 |
31 | class StubWeatherRoot: WeatherRoot {
32 | var routerState: Value> = simpleRouterState(WeatherRootChild.Main(component:StubWeatherMain()))
33 | }
34 |
35 | class StubWeatherMain : WeatherMain {
36 | func onItemClicked(id: Int64) {}
37 |
38 | var models: Value = valueOf(WeatherMainModel(currentWeather: CurrentWeatherModel(weatherID: 400, daily: [DailyWeatherModel(weatherID: 500, index:1, temp: 28)], temp: 32, wind: 15, humidity: 15, weatherDesc: "Cloudy"), isLoading: false, isError: false))
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // kmm_weather
4 | //
5 | // Created by Nikola Milovic on 19/06/2021.
6 | //
7 |
8 | import UIKit
9 | import SwiftUI
10 |
11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
12 |
13 | var window: UIWindow?
14 |
15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
19 | // Create the SwiftUI view that provides the window contents.
20 | let contentView = ContentView()
21 |
22 | // Use a UIHostingController as window root view controller.
23 | if let windowScene = scene as? UIWindowScene {
24 | let window = UIWindow(windowScene: windowScene)
25 | window.rootViewController = UIHostingController(rootView: contentView)
26 | self.window = window
27 | window.makeKeyAndVisible()
28 | }
29 | }
30 |
31 | func sceneDidDisconnect(_ scene: UIScene) {
32 | // Called as the scene is being released by the system.
33 | // This occurs shortly after the scene enters the background, or when its session is discarded.
34 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
35 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
36 | }
37 |
38 | func sceneDidBecomeActive(_ scene: UIScene) {
39 | // Called when the scene has moved from an inactive state to an active state.
40 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
41 | }
42 |
43 | func sceneWillResignActive(_ scene: UIScene) {
44 | // Called when the scene will move from an active state to an inactive state.
45 | // This may occur due to temporary interruptions (ex. an incoming phone call).
46 | }
47 |
48 | func sceneWillEnterForeground(_ scene: UIScene) {
49 | // Called as the scene transitions from the background to the foreground.
50 | // Use this method to undo the changes made on entering the background.
51 | }
52 |
53 | func sceneDidEnterBackground(_ scene: UIScene) {
54 | // Called as the scene transitions from the foreground to the background.
55 | // Use this method to save data, release shared resources, and store enough scene-specific state information
56 | // to restore the scene back to its current state.
57 | }
58 |
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/SearchBox.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchBox.swift
3 | // kmm_weather
4 | //
5 | // Created by Nikola Milovic on 19/06/2021.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SearchBox: View {
11 | @Binding var text: String
12 |
13 | @State private var isEditing = false
14 |
15 | var body: some View {
16 | HStack {
17 |
18 | TextField("Search ...", text: $text)
19 | .padding(7)
20 | .padding(.horizontal, 25)
21 | .background(Color(.systemGray6))
22 | .cornerRadius(8)
23 | .overlay(
24 | HStack {
25 | Image(systemName: "magnifyingglass")
26 | .foregroundColor(.gray)
27 | .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
28 | .padding(.leading, 8)
29 |
30 | if isEditing {
31 | Button(action: {
32 | self.text = ""
33 |
34 | }) {
35 | Image(systemName: "multiply.circle.fill")
36 | .foregroundColor(.gray)
37 | .padding(.trailing, 8)
38 | }
39 | }
40 | }
41 | )
42 | .padding(.horizontal, 10)
43 | .onTapGesture {
44 | self.isEditing = true
45 | }
46 |
47 | if isEditing {
48 | Button(action: {
49 | self.isEditing = false
50 | self.text = ""
51 |
52 | // Dismiss the keyboard
53 | UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
54 | }) {
55 | Text("Cancel").foregroundColor(.white)
56 | }
57 | .padding(.trailing, 10)
58 | .transition(.move(edge: .trailing))
59 | .animation(.default)
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/SimpleRouterState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimpleRouterState.swift
3 | // kmm_weather
4 | //
5 | // Created by Nikola Milovic on 19/06/2021.
6 | //
7 |
8 | import Weather
9 |
10 | func simpleRouterState(_ child: T) -> Value> {
11 | return valueOf(
12 | RouterState(
13 | activeChild: ChildCreated(
14 | configuration: "config" as AnyObject,
15 | instance: child
16 | ),
17 | backStack: []
18 | )
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Utils.swift
3 | // kmm_weather
4 | //
5 | // Created by Nikola Milovic on 19/06/2021.
6 | //
7 |
8 | import SwiftUI
9 |
10 | func hexStringToColor (hex: String) -> Color {
11 | var cString: String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
12 |
13 | if (cString.hasPrefix("#")) {
14 | cString.remove(at: cString.startIndex)
15 | }
16 |
17 | if ((cString.count) != 6) {
18 | return Color.gray
19 | }
20 |
21 | var rgbValue: UInt64 = 0
22 | Scanner(string: cString).scanHexInt64(&rgbValue)
23 |
24 | return Color(
25 | red: Double(CGFloat((rgbValue & 0xFF0000) >> 16)) / 255.0,
26 | green: Double(CGFloat((rgbValue & 0x00FF00) >> 8)) / 255.0,
27 | blue: Double(CGFloat(rgbValue & 0x0000FF) / 255.0)
28 | )
29 | }
30 |
31 |
32 | func getCurrentDate() -> String {
33 | let currentDateTime = Date()
34 | // initialize the date formatter and set the style
35 | let formatter = DateFormatter()
36 | formatter.timeStyle = .short
37 | formatter.dateStyle = .medium
38 |
39 | return formatter.string(from: currentDateTime)
40 | }
41 |
42 | func formatTempString(str: String, fahr: Bool) -> String {
43 | if !fahr {
44 | var appendString = str + "C"
45 | return appendString
46 | } else {
47 | return String(Int(((Double(str) ?? 0 * 1.8) + 32))) + "F"
48 | }
49 | }
50 |
51 | func getWeatherIconStringFromWeatherID(id : Int32) -> String {
52 | switch id {
53 | case 200..<233:
54 | return "thunder"
55 | case 300..<322:
56 | return "day_rain"
57 | case 500..<532:
58 | return "rain"
59 | case 600..<623:
60 | return "snow"
61 | case 800:
62 | return "day_clear"
63 | case 801..<805:
64 | return "cloudy"
65 | default:
66 | return "day_clear"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/iosApp/kmm_weather/WeatherAppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // kmm_weatherApp.swift
3 | // kmm_weather
4 | //
5 | // Created by Nikola Milovic on 12/06/2021.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct kmm_weatherApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | include(
2 | ":common:root",
3 | ":common:main",
4 | ":common:utils",
5 | ":common:compose-ui",
6 | ":androidApp",
7 | ":desktop"
8 | )
--------------------------------------------------------------------------------
/showcase/android_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/showcase/android_dark.png
--------------------------------------------------------------------------------
/showcase/android_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/showcase/android_light.png
--------------------------------------------------------------------------------
/showcase/desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/showcase/desktop.png
--------------------------------------------------------------------------------
/showcase/ios.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nikola-Milovic/KotlinMultiplaftorm-WeatherApp/281cd1638d92723a05b1ef49875e361d4e725006/showcase/ios.png
--------------------------------------------------------------------------------