Skip to content

Commit 592859d

Browse files
Sync Library Interface & SyncAPIs (#12)
* Configure the gradle for the syncengine * Define a first draft of the SynchronizationClient * Create mutations-definitions module * Use Local and Remote mutation types * Add needed details to SynchronizationClient's interface * Crreate SynchronizationClientImpl * Create GetMutationsRequest and PostMutationsRequest * Add ktor * Fill in the main details of SynchronizationClientImpl * Extract MutationsResponse to a separate file * Fill in GetMutationsRequest's details * Fill in the details of PostMutationsRequest * Cteate HttpClientFactory * Call API requests in SynchronizationClientImpl * Provide a temporary SynchronizationClientIntegrationTest * Add ketmit to syncengine * Add exhastive loggings * Log errors in mutations APIs requests * Fix an issue in preparing local mutations to push * Add a test for posting mutations to the integration test as well * Ignore all SynchronizationClientIntegrationTest tests * Refactor some stuff in the requests * Deliver explicit failure responses * Add HttpClientFactory.ios.kt
1 parent 7f31073 commit 592859d

File tree

18 files changed

+987
-7
lines changed

18 files changed

+987
-7
lines changed

gradle/libs.versions.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
[versions]
22
agp = "8.11.0"
33
kotlin = "2.2.0"
4+
kotlinx-serialization = "1.6.3"
45
kermit = "2.0.0"
56

67
maven-publish = "0.33.0"
78

89
compose = "2025.06.01"
910
coroutines = "1.10.2"
1011
sqldelight = "2.1.0"
12+
ktor = "2.3.12"
1113

1214
# Android configuration
1315
android-compile-sdk = "35"
@@ -35,6 +37,17 @@ sqldelight-android-driver = { module = "app.cash.sqldelight:android-driver", ver
3537
sqldelight-native-driver = { module = "app.cash.sqldelight:native-driver", version.ref = "sqldelight" }
3638
sqldelight-sqlite-driver = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqldelight" }
3739

40+
# Ktor dependencies
41+
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
42+
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
43+
ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
44+
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
45+
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
46+
ktor-serialization-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
47+
48+
# Kotlin Serialization
49+
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
50+
3851
compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose" }
3952
compose-foundation = { module = "androidx.compose.foundation:foundation" }
4053
compose-animation = { module = "androidx.compose.animation:animation" }
@@ -50,5 +63,6 @@ android-application = { id = "com.android.application", version.ref = "agp" }
5063
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
5164
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
5265
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
66+
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
5367
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
5468
vanniktech-maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "maven-publish" }
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2+
3+
plugins {
4+
alias(libs.plugins.kotlin.multiplatform)
5+
alias(libs.plugins.android.library)
6+
alias(libs.plugins.vanniktech.maven.publish)
7+
}
8+
9+
kotlin {
10+
iosX64()
11+
iosArm64()
12+
iosSimulatorArm64()
13+
14+
androidTarget {
15+
publishLibraryVariants("release")
16+
compilerOptions {
17+
jvmTarget.set(JvmTarget.JVM_17)
18+
}
19+
}
20+
21+
sourceSets {
22+
val commonMain by getting {
23+
dependencies {
24+
implementation(libs.kotlinx.coroutines.core)
25+
}
26+
}
27+
28+
val commonTest by getting {
29+
dependencies {
30+
implementation(libs.kotlin.test)
31+
}
32+
}
33+
}
34+
// don't show warnings for expect/actual classes
35+
targets.configureEach {
36+
compilations.configureEach {
37+
compileTaskProvider.get().compilerOptions {
38+
freeCompilerArgs.add("-Xexpect-actual-classes")
39+
}
40+
}
41+
}
42+
}
43+
44+
android {
45+
namespace = "com.quran.shared.mutations"
46+
compileSdk = libs.versions.android.compile.sdk.get().toInt()
47+
48+
defaultConfig {
49+
minSdk = libs.versions.android.min.sdk.get().toInt()
50+
}
51+
52+
compileOptions {
53+
sourceCompatibility = JavaVersion.valueOf("VERSION_${libs.versions.android.java.version.get()}")
54+
targetCompatibility = JavaVersion.valueOf("VERSION_${libs.versions.android.java.version.get()}")
55+
}
56+
57+
testOptions {
58+
unitTests {
59+
isIncludeAndroidResources = true
60+
}
61+
}
62+
}
63+
64+
mavenPublishing {
65+
signAllPublications()
66+
coordinates(libs.versions.project.group.get(), "syncengine", libs.versions.project.version.get())
67+
68+
pom {
69+
name = "Quran.com Sync Engine"
70+
description = "A library for synchronizing data with Quran.com"
71+
inceptionYear = "2025"
72+
url = "https://github.com/quran/syncengine"
73+
}
74+
}
75+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.quran.shared.mutations
2+
3+
enum class Mutation {
4+
CREATED,
5+
DELETED,
6+
MODIFIED
7+
}
8+
9+
class LocalModelMutation<Model>(val model: Model, val remoteID: String?, val localID: String, val mutation: Mutation)
10+
11+
class RemoteModelMutation<Model>(val model: Model, val remoteID: String, val mutation: Mutation)

persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/PageBookmarksRepositoryImpl.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import com.quran.shared.persistence.QuranDatabase
66
import com.quran.shared.persistence.model.PageBookmark
77
import com.quran.shared.persistence.model.PageBookmarkMutation
88
import com.quran.shared.persistence.model.PageBookmarkMutationType
9-
import com.quran.shared.persistence.repository.toBookmarkMutation
109
import kotlinx.coroutines.flow.Flow
1110
import kotlinx.coroutines.flow.map
1211
import kotlinx.coroutines.withContext

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ include(":syncengine")
2121
include(":persistence")
2222
include(":umbrella")
2323
include(":demo:android")
24+
include(":mutations-definitions")

syncengine/build.gradle.kts

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,102 @@
11
import com.vanniktech.maven.publish.SonatypeHost
2+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
23

34
plugins {
45
alias(libs.plugins.kotlin.multiplatform)
6+
alias(libs.plugins.kotlin.serialization)
7+
alias(libs.plugins.android.library)
58
alias(libs.plugins.vanniktech.maven.publish)
69
}
710

811
kotlin {
9-
jvm()
1012
iosX64()
1113
iosArm64()
1214
iosSimulatorArm64()
1315

16+
androidTarget {
17+
publishLibraryVariants("release")
18+
compilerOptions {
19+
jvmTarget.set(JvmTarget.JVM_17)
20+
}
21+
}
22+
1423
sourceSets {
1524
val commonMain by getting {
1625
dependencies {
26+
implementation(libs.kotlinx.coroutines.core)
27+
implementation(libs.kotlinx.serialization.json)
28+
implementation(libs.ktor.client.core)
29+
implementation(libs.ktor.client.content.negotiation)
30+
implementation(libs.ktor.client.logging)
31+
implementation(libs.ktor.serialization.json)
32+
implementation(libs.kermit)
33+
}
34+
}
35+
36+
val androidMain by getting {
37+
dependencies {
38+
implementation(libs.ktor.client.okhttp)
1739
}
1840
}
1941

42+
val iosX64Main by getting
43+
val iosArm64Main by getting
44+
val iosSimulatorArm64Main by getting
45+
val iosMain by creating {
46+
dependsOn(commonMain)
47+
dependencies {
48+
implementation(libs.ktor.client.darwin)
49+
}
50+
}
51+
iosX64Main.dependsOn(iosMain)
52+
iosArm64Main.dependsOn(iosMain)
53+
iosSimulatorArm64Main.dependsOn(iosMain)
54+
2055
val commonTest by getting {
2156
dependencies {
57+
implementation(libs.kotlin.test)
58+
implementation(libs.kotlinx.coroutines.test)
2259
}
2360
}
61+
62+
commonMain.dependencies {
63+
api(projects.mutationsDefinitions)
64+
}
65+
}
66+
// don't show warnings for expect/actual classes
67+
targets.configureEach {
68+
compilations.configureEach {
69+
compileTaskProvider.get().compilerOptions {
70+
freeCompilerArgs.add("-Xexpect-actual-classes")
71+
}
72+
}
73+
}
74+
}
75+
76+
android {
77+
namespace = "com.quran.shared.syncengine"
78+
compileSdk = libs.versions.android.compile.sdk.get().toInt()
79+
80+
defaultConfig {
81+
minSdk = libs.versions.android.min.sdk.get().toInt()
82+
}
83+
84+
compileOptions {
85+
sourceCompatibility = JavaVersion.valueOf("VERSION_${libs.versions.android.java.version.get()}")
86+
targetCompatibility = JavaVersion.valueOf("VERSION_${libs.versions.android.java.version.get()}")
87+
}
88+
89+
testOptions {
90+
unitTests {
91+
isIncludeAndroidResources = true
92+
}
2493
}
2594
}
2695

2796
mavenPublishing {
2897
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
2998
signAllPublications()
30-
coordinates(group.toString(), "syncengine", version.toString())
99+
coordinates(libs.versions.project.group.get(), "syncengine", libs.versions.project.version.get())
31100

32101
pom {
33102
name = "Quran.com Sync Engine"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.quran.shared.syncengine
2+
3+
import io.ktor.client.*
4+
import io.ktor.client.engine.okhttp.*
5+
import io.ktor.client.plugins.contentnegotiation.*
6+
import io.ktor.client.plugins.logging.*
7+
import io.ktor.serialization.kotlinx.json.*
8+
9+
actual object HttpClientFactory {
10+
actual fun createHttpClient(): HttpClient {
11+
return HttpClient(OkHttp) {
12+
install(ContentNegotiation) {
13+
json()
14+
}
15+
install(Logging) {
16+
level = LogLevel.INFO
17+
}
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)