Skip to content

Commit

Permalink
Migrate to Kotlin Multiplatform Architecture (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
ErickSumargo authored Apr 30, 2021
1 parent 0f1ed28 commit e2884b1
Show file tree
Hide file tree
Showing 398 changed files with 2,400 additions and 2,103 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup GraphQL
run: ./gradlew downloadApolloSchema --endpoint="https://dads-engine.herokuapp.com" --schema="libs/lib_remote/src/main/graphql/com/bael/dads/lib/remote/schema.json"
run: ./gradlew downloadApolloSchema --endpoint="https://dads-engine.herokuapp.com" --schema="data/remote/src/commonMain/graphql/com/bael/dads/data/remote/schema.json"
- name: Cache GraphQL setup
uses: actions/cache@v2
with:
path: libs/lib_remote/src/main/graphql/com/bael/dads/lib/remote
path: data/remote/src/commonMain/graphql/com/bael/dads/data/remote
key: ${{ github.sha }}
build:
runs-on: macos-latest
Expand All @@ -26,7 +26,7 @@ jobs:
- name: Cache GraphQL setup
uses: actions/cache@v2
with:
path: libs/lib_remote/src/main/graphql/com/bael/dads/lib/remote
path: data/remote/src/commonMain/graphql/com/bael/dads/data/remote
key: ${{ github.sha }}
- name: Build application
run: ./gradlew assembleDebug --stacktrace
Expand All @@ -39,7 +39,7 @@ jobs:
- name: Cache GraphQL setup
uses: actions/cache@v2
with:
path: libs/lib_remote/src/main/graphql/com/bael/dads/lib/remote
path: data/remote/src/commonMain/graphql/com/bael/dads/data/remote
key: ${{ github.sha }}
- name: Test application
uses: reactivecircus/android-emulator-runner@v2
Expand Down
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
- [JavaPoet](https://github.com/square/javapoet) - Personal diff-state engine printer.
- [Lifecycle](https://developer.android.com/topic/libraries/architecture/coroutines) - Coroutines teams up with Android's component lifecycle.
- [Navigation component](https://developer.android.com/guide/navigation/navigation-getting-started) - The key player for adopting single-activity architecture with ease.
- [Room](https://developer.android.com/training/data-storage/room) - ORM for SQLite database. Also, try out its integration with [Database Inspector](https://developer.android.com/studio/inspect/database).
- [SQLDelight](https://github.com/cashapp/sqldelight) - ORM for SQLite database, Multiplatform. Also, try out its integration with [Database Inspector](https://developer.android.com/studio/inspect/database).
- [View Binding](https://developer.android.com/topic/libraries/view-binding) - Providing safe access to view.
- [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel) - Presenter with its semi data persistence behavior.
- [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) - Background job scheduler. You should also try out its integration with [WorkManager Inspector](https://developer.android.com/studio/preview/features#workmanager-inspector).
Expand All @@ -42,9 +42,9 @@
- [ViewPager2](https://developer.android.com/reference/androidx/viewpager2/widget/ViewPager2) - Personal option over `RecyclerView` when dealing view snapping experience.

#### Internal
- [LiveListAdapter](libs/lib_presentation/src/main/kotlin/com/bael/dads/lib/presentation/widget/recyclerview/adapter/LiveListAdapter.kt) <br/>
- [LiveListAdapter](android/library/presentation/src/main/kotlin/com/bael/dads/library/presentation/widget/recyclerview/adapter/LiveListAdapter.kt) <br/>
ListAdapter w/ every visible cell (`ViewHolder`) is reactive. Cell acts like observer of data they hold, so it will auto-refresh if their related data is updated.<br/>
- [RenderExecutor Processor](processor/src/main/kotlin/com/bael/dads/processor) <br/>
- [RenderExecutor Processor](android/processor/src/main/kotlin/com/bael/dads/processor) <br/>
Processor for `RenderWith` annotation. Diff-state engine generator for rendering view component.

#### Remote
Expand All @@ -61,12 +61,23 @@ Processor for `RenderWith` annotation. Diff-state engine generator for rendering
* [LeakCanary](https://square.github.io/leakcanary) (Debug) - Memory leak detector.
* [StrictMode](https://developer.android.com/reference/android/os/StrictMode) (Debug) - Tool for checking if any *should-be-background* operation is done on main thread.
<br/>
<br/>

## MAD Scorecard
[<img src="assets/mad_scorecard.png">](https://madscorecard.withgoogle.com/scorecards/966921635/)
<br/>
<br/>

[<img align="left" width=50% src="assets/kmm.png">]()
# Supports
Dads is ready to support multiplatform with the Clean Architecture concept for project structure (`data`-`domain`-`presentation`).
<br/>
<br/>

JVM, JS, or Native, He is just waiting your implementation of presentation part!
<br/>
<br/>

## Architecture
Dads adopts [MVVM](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel) with [Unidirectional flow (UDF)](https://en.wikipedia.org/wiki/Unidirectional_Data_Flow_(computer_science)) pattern.
<br/>
Expand Down Expand Up @@ -97,10 +108,10 @@ Jokes are requested from proprietary GraphQL service, the [Dads-Engine](https://
* Since this project employs GraphQL stack, you need to download the [schema](https://www.apollographql.com/docs/tutorial/schema/) first:
* Go to hosted [GraphQL Playground](https://dads-engine.herokuapp.com/graphql),
* Open tab `SCHEMA` at the right side. `DOWNLOAD` it,
* Put the `schema.json` in directory: `libs/lib_remote/src/main/graphql/com/bael/dads/lib/remote/`,
* Put the `schema.json` in directory: `data/remote/src/commonMain/graphql/com/bael/dads/data/remote/`,
* Or you can run this command as alternative.
```
./gradlew downloadApolloSchema --endpoint="https://dads-engine.herokuapp.com" --schema="libs/lib_remote/src/main/graphql/com/bael/dads/lib/remote/schema.json"
./gradlew downloadApolloSchema --endpoint="https://dads-engine.herokuapp.com" --schema="data/remote/src/commonMain/graphql/com/bael/dads/data/remote/schema.json"
```
* Set `JWT` key in `keys.properties` file (located in project root folder):
```
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
69 changes: 69 additions & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import Library.AndroidX.hiltCompiler
import Library.AndroidX.hiltNavigation
import Library.AndroidX.hiltWork
import Library.AndroidX.navigationFragment
import Library.AndroidX.navigationUi
import Library.AndroidX.work
import Library.Apollo.apolloKotlin
import Library.Google.dagger
import Library.Google.daggerCompiler
import Library.Google.firebaseBom
import Library.KotlinX.serialization
import Library.Square.leakCanary
import Library.Google.firebaseAnalytics as analytics
import Library.Google.firebaseCrashlytics as crashlytics

plugins {
id("androidApp")
}

dependencies {
// AndroidX
implementation(hiltNavigation)
implementation(hiltWork)
kapt(hiltCompiler)

implementation(navigationFragment)
implementation(navigationUi)

implementation(work)

// Apollo
implementation(apolloKotlin)

// Google
implementation(dagger)
kapt(daggerCompiler)

implementation(platform(firebaseBom))
implementation(analytics)
implementation(crashlytics)

// KotlinX
implementation(serialization)

// Square
debugImplementation(leakCanary)
}

dependencies {
// Shared
implementation(project(":shared"))

// Data
implementation(project(":data:database"))
implementation(project(":data:remote"))

// Domain
implementation(project(":domain:home"))
implementation(project(":shared"))

// Feature
implementation(project(":android:feature:home"))

// Library
implementation(project(":android:library:preference"))
implementation(project(":android:library:presentation"))
implementation(project(":android:library:threading"))
implementation(project(":android:library:worker"))
}
30 changes: 30 additions & 0 deletions android/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# ProGuard Configuration file
#
# See http://proguard.sourceforge.net/index.html#manual/usage.html

# Firebase Crashlytics
-keepattributes SourceFile,LineNumberTable
-keep public class * extends java.lang.Exception
-keep class com.google.firebase.crashlytics.** { *; }
-dontwarn com.google.firebase.crashlytics.**

# Jetpack - Navigation Component
-keep class androidx.navigation.fragment.NavHostFragment

# KotlinX - Serialization
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations

-keepclassmembers class kotlinx.serialization.json.** {
*** Companion;
}
-keepclasseswithmembers class kotlinx.serialization.json.** {
kotlinx.serialization.KSerializer serializer(...);
}
-keep,includedescriptorclasses class com.bael.dads.**$$serializer { *; }
-keepclassmembers class com.bael.dads.** {
*** Companion;
}
-keepclasseswithmembers class com.bael.dads.** {
kotlinx.serialization.KSerializer serializer(...);
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import android.app.Application
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
import com.bael.dads.lib.preference.Preference
import com.bael.dads.library.preference.Preference
import kotlinx.coroutines.runBlocking
import javax.inject.Inject

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.bael.dads.di.module

import com.bael.dads.activity.MainActivity
import com.bael.dads.lib.presentation.di.ActivityNameQualifier
import com.bael.dads.lib.presentation.di.ActivityNameQualifier.Companion.ACTIVITY_MAIN
import com.bael.dads.library.presentation.di.qualifier.ActivityNameQualifier
import com.bael.dads.library.presentation.di.qualifier.ActivityNameQualifier.Companion.ACTIVITY_MAIN
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -15,7 +15,7 @@ import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
class MainActivityModule {
internal class MainActivityModule {

@Provides
@Singleton
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
39 changes: 39 additions & 0 deletions android/feature/home/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Library.Airbnb.lottie
import Library.AndroidX.constraintLayout
import Library.AndroidX.swipeRefreshLayout
import Library.AndroidX.viewPager2
import Library.AndroidX.work

plugins {
id("androidFeature")
}

dependencies {
// AndroidX
implementation(constraintLayout)
implementation(swipeRefreshLayout)
implementation(viewPager2)
implementation(work)

// Airbnb
implementation(lottie)
}

dependencies {
// Shared
implementation(project(":shared"))

// Data
androidTestImplementation(project(":data:database"))
androidTestImplementation(project(":data:database_test"))

androidTestImplementation(project(":data:remote"))
androidTestImplementation(project(":data:remote_test"))

// Domain
implementation(project(":domain:home"))

// Library
implementation(project(":android:library:preference"))
implementation(project(":android:library:worker"))
}
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.bael.dads.feature.home.screen.home

import com.bael.dads.feature.home.R
import com.bael.dads.lib.instrumentation.fragment.BaseFragmentTest
import com.bael.dads.lib.presentation.ext.readText
import com.bael.dads.library.instrumentation.fragment.BaseFragmentTest
import com.bael.dads.library.presentation.ext.readText
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Test

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.bael.dads.feature.home.screen.seen

import com.bael.dads.data.database.entity.DadJoke
import com.bael.dads.data.database.repository.DadJokeRepository
import com.bael.dads.feature.home.R
import com.bael.dads.lib.database.DadsDatabase
import com.bael.dads.lib.database.entity.DadJoke
import com.bael.dads.lib.instrumentation.fragment.BaseFragmentTest
import com.bael.dads.lib.presentation.ext.readText
import com.bael.dads.library.instrumentation.fragment.BaseFragmentTest
import com.bael.dads.library.presentation.ext.readText
import dagger.hilt.android.testing.HiltAndroidTest
import kotlinx.coroutines.runBlocking
import org.junit.Test
import javax.inject.Inject

Expand All @@ -16,18 +17,13 @@ import javax.inject.Inject
@HiltAndroidTest
internal class UITest : BaseFragmentTest() {
@Inject
lateinit var database: DadsDatabase
lateinit var dadJokeRepository: DadJokeRepository

override fun setupTest() {}

@Test
fun givenEmptySeenDadJokes_thenEmptyStateShouldShow() {
runTest {
// given
database.dadJoke.insertDadJokes(
dadJokes = listOf()
)

// when
launch<UI>(graphResId = R.navigation.nav_graph)

Expand All @@ -40,7 +36,7 @@ internal class UITest : BaseFragmentTest() {
fun givenSeenDadJokes_dadJokesShouldShow() {
runTest {
// given
database.dadJoke.insertDadJokes(
dadJokeRepository.insertDadJokes(
dadJokes = listOf(
DadJoke(
id = 1,
Expand Down Expand Up @@ -76,6 +72,8 @@ internal class UITest : BaseFragmentTest() {
}

override fun clearTest() {
database.closeConnection()
runBlocking {
dadJokeRepository.deleteAllDadJokes()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package com.bael.dads.feature.home.sheet.detail

import androidx.core.os.bundleOf
import com.bael.dads.domain.home.model.DadJoke
import com.bael.dads.lib.instrumentation.sheet.BaseSheetTest
import com.bael.dads.library.instrumentation.sheet.BaseSheetTest
import com.bael.dads.shared.time.DateTime.now
import dagger.hilt.android.testing.HiltAndroidTest
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.junit.Test

/**
Expand All @@ -25,11 +28,11 @@ internal class UITest : BaseSheetTest() {
punchline = "Punchline 1",
favored = false,
seen = false,
updatedAt = 0L
updatedAt = now
)

// when
launch<UI>(args = bundleOf("dadJoke" to dadJoke))
launch<UI>(args = bundleOf("dadJoke" to Json.encodeToString(dadJoke)))

// then
isDisplayed(text = "Setup 1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.bael.dads.feature.home.adapter.cell.DadJokeFeedCell
import com.bael.dads.feature.home.adapter.diffcallback.DadJokeDiffCallback
import com.bael.dads.feature.home.databinding.CellFeedBinding.inflate
import com.bael.dads.domain.home.model.DadJoke
import com.bael.dads.lib.presentation.widget.recyclerview.adapter.LiveListAdapter
import com.bael.dads.library.presentation.widget.recyclerview.adapter.LiveListAdapter

/**
* Created by ErickSumargo on 01/01/21.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.bael.dads.feature.home.adapter.cell.SeenDadJokeCell
import com.bael.dads.feature.home.adapter.diffcallback.DadJokeDiffCallback
import com.bael.dads.feature.home.databinding.CellSeenBinding.inflate
import com.bael.dads.domain.home.model.DadJoke
import com.bael.dads.lib.presentation.widget.recyclerview.adapter.LiveListAdapter
import com.bael.dads.library.presentation.widget.recyclerview.adapter.LiveListAdapter

/**
* Created by ErickSumargo on 01/01/21.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package com.bael.dads.feature.home.adapter.cell

import com.bael.dads.feature.home.databinding.CellFeedBinding
import com.bael.dads.domain.home.model.DadJoke
import com.bael.dads.lib.presentation.ext.toRichText
import com.bael.dads.lib.presentation.widget.recyclerview.adapter.cell.BaseCell
import com.bael.dads.library.presentation.ext.toRichText
import com.bael.dads.library.presentation.widget.recyclerview.adapter.cell.BaseCell

/**
* Created by ErickSumargo on 01/01/21.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package com.bael.dads.feature.home.adapter.cell

import com.bael.dads.feature.home.databinding.CellSeenBinding
import com.bael.dads.domain.home.model.DadJoke
import com.bael.dads.lib.presentation.ext.toRichText
import com.bael.dads.lib.presentation.widget.recyclerview.adapter.cell.BaseCell
import com.bael.dads.library.presentation.ext.toRichText
import com.bael.dads.library.presentation.widget.recyclerview.adapter.cell.BaseCell

/**
* Created by ErickSumargo on 01/01/21.
Expand Down
Loading

0 comments on commit e2884b1

Please sign in to comment.