
Sync and data stack for Quran apps: OIDC authentication, SQL-backed local persistence, orchestration of sync engine via DI, unified SyncService API, mutation/observe primitives and demos.
Kotlin Multiplatform sync/data stack for Quran mobile apps.
This repository contains shared modules used by Android and iOS apps for:
SyncService)auth + persistence + syncengine are composed in sync-pipelines, exposed through a DI graph (AppGraph / SharedDependencyGraph), and exported to iOS through umbrella.
flowchart LR
UI[Android/iOS UI] --> S[SyncService]
S --> A[Auth Module]
S --> P[Persistence Module]
S --> E[SyncEngine Module]
U[Umbrella XCFramework] --> UI| Module | Purpose |
|---|---|
:auth |
OIDC login/logout, auth state, token handling |
:persistence |
SQLDelight DB, repositories for bookmarks/collections/notes/recent pages |
:syncengine |
Core sync engine and scheduling |
:sync-pipelines |
DI graph and SyncService orchestration API |
:umbrella |
iOS XCFramework export (Shared.xcframework) |
:demo:android |
Android sample app (Compose) |
:demo:common |
Shared demo helpers/models |
:mutations-definitions |
Shared mutation/domain definitions |
Optional but recommended:
local.properties with OAuth credentials:OAUTH_CLIENT_ID=your_client_id
OAUTH_CLIENT_SECRET=your_client_secretgit clone https://github.com/quran/mobile-sync.git
cd mobile-sync./gradlew help./gradlew allTests --stacktrace --continueimport com.quran.shared.auth.di.AuthFlowFactoryProvider
import com.quran.shared.persistence.DriverFactory
import com.quran.shared.pipeline.AppEnvironment
import com.quran.shared.pipeline.di.SharedDependencyGraph
import org.publicvalue.multiplatform.oidc.appsupport.AndroidCodeAuthFlowFactory
val authFactory = AndroidCodeAuthFlowFactory(useWebView = false)
authFactory.registerActivity(activity)
AuthFlowFactoryProvider.initialize(authFactory)
val graph = SharedDependencyGraph.init(
driverFactory = DriverFactory(context = applicationContext),
appEnvironment = AppEnvironment.PRELIVE
)
val authService = graph.authService
val syncService = graph.syncServiceimport Shared
final class AppContainer {
static let shared = AppContainer()
static var graph: AppGraph { shared.graph }
let graph: AppGraph
private init() {
Shared.AuthFlowFactoryProvider.shared.doInitialize()
let driverFactory = DriverFactory()
graph = SharedDependencyGraph.shared.doInit(
driverFactory: driverFactory,
appEnvironment: AppEnvironment.prelive
)
}
}Advanced override for custom endpoints remains available:
import com.quran.shared.auth.model.AuthEnvironment
import com.quran.shared.syncengine.SynchronizationEnvironment
val graph = SharedDependencyGraph.init(
driverFactory = DriverFactory(context = applicationContext),
environment = SynchronizationEnvironment(
endPointURL = "https://custom-sync.example.com/auth"
),
authEnvironment = AuthEnvironment.PRELIVE
)Core API examples:
authState, bookmarks, collectionsWithBookmarks, notes
addBookmark, deleteBookmark, addCollection, deleteCollection, addNote, deleteNote
triggerSync()
Lifecycle note:
SyncService is app-scoped. Initialize once via SharedDependencyGraph.init(...).The published artifact can target either environment at runtime:
AppEnvironment.PRELIVE / AppEnvironment.prelive
AppEnvironment.PRODUCTION / AppEnvironment.production
AppEnvironment keeps auth and sync aligned by default:
PRELIVE -> https://prelive-oauth2.quran.foundation and https://apis-prelive.quran.foundation/auth
PRODUCTION -> https://oauth2.quran.foundation and https://apis.quran.foundation/auth
buildkonfig.flavor now only controls the default fallback used when the app does not pass an explicit app environment.
Default fallback (gradle.properties):
buildkonfig.flavor=debugOverride for release-like builds:
./gradlew :auth:build -Pbuildkonfig.flavor=releasedemo/android app module.CLI build example:
./gradlew :demo:android:assembleDebugdemo/apple/QuranSyncDemo/QuranSyncDemo.xcodeproj
QuranSyncDemo scheme.The Xcode project includes a Run Script phase that calls:
./gradlew :umbrella:embedAndSignAppleFrameworkForXcodeCLI build example:
xcodebuild -project demo/apple/QuranSyncDemo/QuranSyncDemo.xcodeproj \
-scheme QuranSyncDemo \
-configuration Debug \
-destination 'generic/platform=iOS Simulator' \
build CODE_SIGNING_ALLOWED=NOBuild all:
./gradlew buildRun all tests:
./gradlew allTests --stacktrace --continueBuild iOS XCFramework:
./gradlew :umbrella:assembleSharedXCFrameworkBuild Android demo compile target:
./gradlew :demo:android:compileDebugKotlinBuild iOS KMP target:
./gradlew :sync-pipelines:compileKotlinIosSimulatorArm64Kotlin Multiplatform sync/data stack for Quran mobile apps.
This repository contains shared modules used by Android and iOS apps for:
SyncService)auth + persistence + syncengine are composed in sync-pipelines, exposed through a DI graph (AppGraph / SharedDependencyGraph), and exported to iOS through umbrella.
flowchart LR
UI[Android/iOS UI] --> S[SyncService]
S --> A[Auth Module]
S --> P[Persistence Module]
S --> E[SyncEngine Module]
U[Umbrella XCFramework] --> UI| Module | Purpose |
|---|---|
:auth |
OIDC login/logout, auth state, token handling |
:persistence |
SQLDelight DB, repositories for bookmarks/collections/notes/recent pages |
:syncengine |
Core sync engine and scheduling |
:sync-pipelines |
DI graph and SyncService orchestration API |
:umbrella |
iOS XCFramework export (Shared.xcframework) |
:demo:android |
Android sample app (Compose) |
:demo:common |
Shared demo helpers/models |
:mutations-definitions |
Shared mutation/domain definitions |
Optional but recommended:
local.properties with OAuth credentials:OAUTH_CLIENT_ID=your_client_id
OAUTH_CLIENT_SECRET=your_client_secretgit clone https://github.com/quran/mobile-sync.git
cd mobile-sync./gradlew help./gradlew allTests --stacktrace --continueimport com.quran.shared.auth.di.AuthFlowFactoryProvider
import com.quran.shared.persistence.DriverFactory
import com.quran.shared.pipeline.AppEnvironment
import com.quran.shared.pipeline.di.SharedDependencyGraph
import org.publicvalue.multiplatform.oidc.appsupport.AndroidCodeAuthFlowFactory
val authFactory = AndroidCodeAuthFlowFactory(useWebView = false)
authFactory.registerActivity(activity)
AuthFlowFactoryProvider.initialize(authFactory)
val graph = SharedDependencyGraph.init(
driverFactory = DriverFactory(context = applicationContext),
appEnvironment = AppEnvironment.PRELIVE
)
val authService = graph.authService
val syncService = graph.syncServiceimport Shared
final class AppContainer {
static let shared = AppContainer()
static var graph: AppGraph { shared.graph }
let graph: AppGraph
private init() {
Shared.AuthFlowFactoryProvider.shared.doInitialize()
let driverFactory = DriverFactory()
graph = SharedDependencyGraph.shared.doInit(
driverFactory: driverFactory,
appEnvironment: AppEnvironment.prelive
)
}
}Advanced override for custom endpoints remains available:
import com.quran.shared.auth.model.AuthEnvironment
import com.quran.shared.syncengine.SynchronizationEnvironment
val graph = SharedDependencyGraph.init(
driverFactory = DriverFactory(context = applicationContext),
environment = SynchronizationEnvironment(
endPointURL = "https://custom-sync.example.com/auth"
),
authEnvironment = AuthEnvironment.PRELIVE
)Core API examples:
authState, bookmarks, collectionsWithBookmarks, notes
addBookmark, deleteBookmark, addCollection, deleteCollection, addNote, deleteNote
triggerSync()
Lifecycle note:
SyncService is app-scoped. Initialize once via SharedDependencyGraph.init(...).The published artifact can target either environment at runtime:
AppEnvironment.PRELIVE / AppEnvironment.prelive
AppEnvironment.PRODUCTION / AppEnvironment.production
AppEnvironment keeps auth and sync aligned by default:
PRELIVE -> https://prelive-oauth2.quran.foundation and https://apis-prelive.quran.foundation/auth
PRODUCTION -> https://oauth2.quran.foundation and https://apis.quran.foundation/auth
buildkonfig.flavor now only controls the default fallback used when the app does not pass an explicit app environment.
Default fallback (gradle.properties):
buildkonfig.flavor=debugOverride for release-like builds:
./gradlew :auth:build -Pbuildkonfig.flavor=releasedemo/android app module.CLI build example:
./gradlew :demo:android:assembleDebugdemo/apple/QuranSyncDemo/QuranSyncDemo.xcodeproj
QuranSyncDemo scheme.The Xcode project includes a Run Script phase that calls:
./gradlew :umbrella:embedAndSignAppleFrameworkForXcodeCLI build example:
xcodebuild -project demo/apple/QuranSyncDemo/QuranSyncDemo.xcodeproj \
-scheme QuranSyncDemo \
-configuration Debug \
-destination 'generic/platform=iOS Simulator' \
build CODE_SIGNING_ALLOWED=NOBuild all:
./gradlew buildRun all tests:
./gradlew allTests --stacktrace --continueBuild iOS XCFramework:
./gradlew :umbrella:assembleSharedXCFrameworkBuild Android demo compile target:
./gradlew :demo:android:compileDebugKotlinBuild iOS KMP target:
./gradlew :sync-pipelines:compileKotlinIosSimulatorArm64