Skip to content

Commit 6ba0652

Browse files
authored
Merge pull request #5 from doneill/feature/jsmain
2 parents 4b2022c + 0700ef5 commit 6ba0652

File tree

23 files changed

+251
-75
lines changed

23 files changed

+251
-75
lines changed

README.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,28 @@
11
# Kotlin Multiplatform Playground Template
22
This project is for experimenting with Kotlin Multiplatform
33

4-
![kmp image](kmp-app.png)
4+
| Android | Web | iOS |
5+
| ------------- | ------------- | ------------- |
6+
| ![android](images/android.png) | ![web](images/web.png) | ![iOS](images/ios.png) |
57

68
## Dependencies
79
- [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html)
810
- [Kotlin Ktor](https://ktor.io/clients/index.html)
911
- [Kotlin Serialization](https://github.com/Kotlin/kotlinx.serialization)
12+
- [Kotlin React](https://github.com/JetBrains/kotlin-wrappers/blob/master/kotlin-react/README.md)
1013
- [SQLDelight](https://cashapp.github.io/sqldelight/)
1114
- [Open Weather Map](https://openweathermap.org/)
1215

1316
## Open Weather Map
1417
To access the OpenWeatherMap API you need an [API Key](http://openweathermap.org/appid).
1518

16-
### Android
17-
To use your API Key, create a **gradle.properties** file in the root of the **app** module with a string value pair representing your API Key. This file is not tracked in Git as it is for personal use.
19+
### Common Lib
20+
The common library needs access to your api key to interact with the Open Weather Map API. Create a **gradle.properties** file in the root of the **common** module with a string value pair representing your API Key. This file is not tracked in Git as it is for personal use.
1821

1922
```groovy
2023
OPENWEATHER_API_KEY = "YOUR-API-KEY"
2124
```
2225

23-
### iOS
24-
To use your API Key, modify the `apiKey` variable in `ViewController.swift`:
25-
26-
```swift
27-
// replace with your api key
28-
let apiKey = "YOUR-API-KEY"
29-
```
3026
## Blog Series
3127
- [Getting Started](http://gh.jdoneill.com/2019/12/06/kotlin-mulitplatform/)
3228
- [Multiplatform Persistence with SQLDelight](http://gh.jdoneill.com/2020/06/28/sqldelight/)

app/build.gradle.kts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,6 @@ android {
1616
versionCode = AppCoordinates.APP_VERSION_CODE
1717
versionName = AppCoordinates.APP_VERSION_NAME
1818
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
19-
20-
if (project.hasProperty("OPENWEATHER_API_KEY")) {
21-
buildConfigField("String", "OPENWEATHER_API_KEY", project.property("OPENWEATHER_API_KEY") as String)
22-
} else {
23-
buildConfigField("String", "OPENWEATHER_API_KEY", "\"\"")
24-
}
2519
}
2620

2721
buildTypes {

app/src/main/java/com/jdoneill/ktmultiplatform/ui/MainActivity.kt

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import android.widget.TextView
88
import com.jdoneill.api.RestApi
99
import com.jdoneill.common.KmpDriverFactory
1010
import com.jdoneill.common.createDb
11-
import com.jdoneill.ktmultiplatform.BuildConfig
1211
import com.jdoneill.ktmultiplatform.R
1312
import com.jdoneill.common.getDate
1413
import com.jdoneill.db.KmpModelQueries
@@ -19,9 +18,6 @@ import kotlinx.coroutines.Dispatchers.Main
1918
import kotlinx.coroutines.Job
2019
import kotlinx.coroutines.launch
2120
import kotlin.coroutines.CoroutineContext
22-
import kotlin.random.Random
23-
24-
const val APIKEY = BuildConfig.OPENWEATHER_API_KEY
2521

2622
const val DEGREE: String = "\u00B0"
2723

@@ -46,18 +42,13 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
4642
job = Job()
4743
api = RestApi()
4844

49-
val location = randomWaCoords()
50-
Log.d("Random", location.toString())
51-
getWeather(location, APIKEY)
45+
getWeather()
5246

5347
findViewById<TextView>(R.id.date_view).text = getDate()
5448
}
5549

56-
private fun getWeather(latLng: Pair<Double, Double>, apiKey: String) {
50+
private fun getWeather() {
5751
api.getWeather(
58-
lat = latLng.first.toString(),
59-
lng = latLng.second.toString(),
60-
apiKey = apiKey,
6152
success = ::parseResponse,
6253
failure = ::handleError
6354
)
@@ -95,20 +86,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
9586
}
9687
}
9788

98-
private fun randomWaCoords():Pair<Double, Double> {
99-
val random = Random.Default
100-
101-
val lat = (45..49).shuffled().first()
102-
val latDecimal = random.nextDouble()
103-
val lng = (-124..-116).shuffled().first()
104-
val lngDecimal = random.nextDouble()
105-
106-
val y = lat + latDecimal
107-
val x = lng + lngDecimal
108-
109-
return Pair(y, x)
110-
}
111-
11289
private fun handleError(ex: Throwable) {
11390
launch(Main) {
11491
val msg = ex.message

build.gradle.kts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ plugins {
1414
kotlin("android") version BuildPluginsVersion.KOTLIN apply false
1515
kotlin("plugin.serialization") version BuildPluginsVersion.KOTLIN
1616
id("org.jlleitschuh.gradle.ktlint") version BuildPluginsVersion.KTLINT
17+
id("com.github.gmazzo.buildconfig") version BuildPluginsVersion.BUILD_CONFIG
1718
}
1819

1920
allprojects {
@@ -22,10 +23,7 @@ allprojects {
2223
google()
2324
mavenCentral()
2425
jcenter()
25-
maven(url = "https://kotlin.bintray.com/kotlinx")
26+
maven("https://kotlin.bintray.com/kotlinx")
27+
maven("https://kotlin.bintray.com/kotlin-js-wrappers/")
2628
}
2729
}
28-
29-
tasks.register("clean", Delete::class.java) {
30-
delete(rootProject.buildDir)
31-
}

buildSrc/src/main/java/Dependencies.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ object BuildPluginsVersion {
2323
const val GRADLE = "4.0.0"
2424
const val KOTLIN = "1.3.72"
2525
const val KTLINT = "9.2.1"
26+
const val BUILD_CONFIG = "2.0.2"
2627
}
2728

2829
object Plugins {
@@ -34,24 +35,29 @@ object Kotlin {
3435
const val SERIALIZATION = "org.jetbrains.kotlinx:kotlinx-serialization-runtime:${Versions.SERIALIZER}"
3536
const val SERIALIZATION_COMMON = "org.jetbrains.kotlinx:kotlinx-serialization-runtime:${Versions.SERIALIZER}"
3637
const val SERIALIZATION_IOS = "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:${Versions.SERIALIZER}"
38+
const val SERIALIZATION_WEB = "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:${Versions.SERIALIZER}"
3739
}
3840

3941
object Coroutines {
4042
const val COMMON = "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:${Versions.COROUTINES}"
4143
const val JDK = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.COROUTINES}"
4244
const val NATIVE = "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:${Versions.COROUTINES}"
4345
const val ANDROID = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.COROUTINES}"
46+
const val WEB = "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:${Versions.COROUTINES}"
4447
const val TEST = "org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.COROUTINES}"
4548
}
4649

4750
object Ktor {
4851
const val COMMON_CORE = "io.ktor:ktor-client-core:${Versions.KTOR}"
4952
const val ANDROID = "io.ktor:ktor-client-android:${Versions.KTOR}"
5053
const val IOS = "io.ktor:ktor-client-ios:${Versions.KTOR}"
54+
const val WEB = "io.ktor:ktor-client-js:${Versions.KTOR}"
55+
const val WEB_LOGGING = "io.ktor:ktor-client-logging-js:${Versions.KTOR}"
5156
const val IOS_CORE = "io.ktor:ktor-client-core-native:${Versions.KTOR}"
5257
const val COMMON_SERIALIZATION ="io.ktor:ktor-client-serialization:${Versions.KTOR}"
5358
const val ANDROID_SERIALZATION ="io.ktor:ktor-client-serialization-jvm:${Versions.KTOR}"
5459
const val IOS_SERIALIZATION ="io.ktor:ktor-client-serialization-native:${Versions.KTOR}"
60+
const val WEB_SERIALIZATION = "io.ktor:ktor-client-serialization-js:${Versions.KTOR}"
5561
}
5662

5763
object SqlDelight{
@@ -61,6 +67,7 @@ object SqlDelight{
6167
const val RUNTIME_DRIVER_COMMON = "com.squareup.sqldelight:sqlite-driver:${Versions.SQLDELIGHT}"
6268
const val RUNTIME_DRIVER_IOS = "com.squareup.sqldelight:native-driver:${Versions.SQLDELIGHT}"
6369
const val RUNTIME_DRIVER_ANDROID = "com.squareup.sqldelight:android-driver:${Versions.SQLDELIGHT}"
70+
const val RUNTIME_DRIVER_JS = "com.squareup.sqldelight:runtime-js:${Versions.SQLDELIGHT}"
6471
}
6572

6673
object SupportLibs {

common/build.gradle.kts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
2+
import com.github.gmazzo.gradle.plugins.BuildConfigSourceSet
3+
24

35
plugins {
46
kotlin("multiplatform")
57
kotlin("plugin.serialization")
68
id("com.android.library")
79
id("com.squareup.sqldelight")
10+
id("com.github.gmazzo.buildconfig")
811
}
912

1013
android {
@@ -20,6 +23,7 @@ android {
2023

2124
kotlin {
2225
android()
26+
jvm()
2327

2428
// select iOS target platform depending on the Xcode environment variables
2529
// iPhone simulator : presets.iosX64 | real iDevice 64 bit : presets.iosArm64
@@ -37,6 +41,10 @@ kotlin {
3741
}
3842
}
3943

44+
js {
45+
browser { }
46+
}
47+
4048
sourceSets["commonMain"].dependencies {
4149
// kotlin
4250
implementation(kotlin("stdlib-common", BuildPluginsVersion.KOTLIN))
@@ -76,6 +84,18 @@ kotlin {
7684
// SQL Delight
7785
implementation(SqlDelight.RUNTIME_DRIVER_IOS)
7886
}
87+
88+
sourceSets["jsMain"].dependencies {
89+
implementation(kotlin("stdlib-js"))
90+
// Coroutines
91+
implementation(Coroutines.WEB)
92+
// Ktor
93+
implementation(Ktor.WEB)
94+
// Serialize
95+
implementation(Kotlin.SERIALIZATION_WEB)
96+
// SQL Delight
97+
implementation(SqlDelight.RUNTIME_DRIVER_JS)
98+
}
7999
}
80100

81101
val packForXcode by tasks.creating(Sync::class) {
@@ -105,11 +125,18 @@ val packForXcode by tasks.creating(Sync::class) {
105125
}
106126
}
107127

128+
buildConfig {
129+
if (project.hasProperty("OPENWEATHER_API_KEY")) {
130+
buildConfigField("String", "OPENWEATHER_API_KEY", project.property("OPENWEATHER_API_KEY") as String)
131+
} else {
132+
buildConfigField("String", "OPENWEATHER_API_KEY", "\"\"")
133+
}
134+
}
135+
108136
sqldelight {
109137
database("KmpDb") {
110138
packageName = "com.jdoneill.db"
111139
sourceFolders = listOf("sqldelight")
112140
}
113141
}
114-
115142
tasks.getByName("build").dependsOn(packForXcode)

common/src/commonMain/kotlin/com/jdoneill/api/RestApi.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.jdoneill.api
22

3+
import com.jdoneill.common.BuildConfig
34
import com.jdoneill.common.ApplicationDispatcher
45
import com.jdoneill.model.WeatherResponse
6+
import com.jdoneill.util.RandomLocation
57

68
import io.ktor.client.HttpClient
79
import io.ktor.client.request.get
@@ -16,22 +18,29 @@ import kotlinx.serialization.json.Json
1618
class RestApi {
1719

1820
companion object {
21+
private const val API_KEY = BuildConfig.OPENWEATHER_API_KEY
22+
1923
private const val API_V2 = "/data/2.5/"
2024
private const val API_V2_WEATHER = "$API_V2/weather"
2125
private const val BASE_URL = "https://api.openweathermap.org"
2226
}
2327

2428
private val client = HttpClient()
2529

26-
fun getWeather(lat: String, lng: String, apiKey: String, success: (WeatherResponse) -> Unit, failure: (Throwable) -> Unit) {
30+
fun getWeather(success: (WeatherResponse) -> Unit, failure: (Throwable) -> Unit) {
31+
32+
val location = RandomLocation.washingtonStateCoords()
33+
val lat = location.first.toString()
34+
val lng = location.second.toString()
35+
2736
GlobalScope.launch(ApplicationDispatcher) {
2837
try {
2938
val response = client.get<HttpStatement> {
3039
url("$BASE_URL$API_V2_WEATHER")
3140
parameter("units", "imperial")
3241
parameter("lat", lat)
3342
parameter("lon", lng)
34-
parameter("appid", apiKey)
43+
parameter("appid", API_KEY)
3544
}.execute()
3645

3746
Json.nonstrict.parse(WeatherResponse.serializer(), response.readText())
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.jdoneill.util
2+
3+
import kotlin.jvm.JvmStatic
4+
import kotlin.random.Random
5+
6+
class RandomLocation {
7+
companion object {
8+
@JvmStatic
9+
fun washingtonStateCoords():Pair<Double, Double> {
10+
val random = Random.Default
11+
12+
val lat = (45..49).shuffled().first()
13+
val latDecimal = random.nextDouble()
14+
val lng = (-124..-116).shuffled().first()
15+
val lngDecimal = random.nextDouble()
16+
17+
val y = lat + latDecimal
18+
val x = lng + lngDecimal
19+
20+
return Pair(y, x)
21+
}
22+
}
23+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.jdoneill.common
2+
3+
import kotlin.js.Date
4+
5+
actual fun getCurrentDate() = Date().toString()
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.jdoneill.common
2+
3+
import kotlinx.coroutines.CoroutineDispatcher
4+
import kotlinx.coroutines.Dispatchers
5+
6+
internal actual val ApplicationDispatcher: CoroutineDispatcher = Dispatchers.Default

0 commit comments

Comments
 (0)