Giter VIP home page Giter VIP logo

hoc081098 / githubsearchkmm-compose-swiftui Goto Github PK

View Code? Open in Web Editor NEW
179.0 4.0 20.0 6.64 MB

🍭 GithubSearchKMM - Github Repos Search - Android - iOS - Kotlin Multiplatform Mobile using Jetpack Compose, SwiftUI, FlowRedux, Coroutines Flow, Dagger Hilt, Koin Dependency Injection, shared KMP ViewModel, Clean Architecture

License: MIT License

Kotlin 89.90% Ruby 0.93% Swift 9.12% Shell 0.05%
kmm kmm-library kmm-mvvm kmm-network-layer kmm-plugin kmm-sample kotlin-multiplatform kmm-flowredux kmm-mvi kotlin-flow-redux

githubsearchkmm-compose-swiftui's Introduction

GithubSearchKMM

Github Repos Search - Kotlin Multiplatform Mobile using Jetpack Compose, SwiftUI, FlowRedux, Coroutines Flow, Dagger Hilt, Koin Dependency Injection, shared KMP ViewModel, Clean Architecture

Android Build CI iOS Build CI Validate Gradle Wrapper API Kotlin Hits License: MIT codecov Platform

Minimal Kotlin Multiplatform project with SwiftUI, Jetpack Compose.

  • Android (Jetpack compose)
  • iOS (SwiftUI)

Liked some of my work? Buy me a coffee (or more likely a beer)

Buy Me A Coffee

Modern Development

  • Kotlin Multiplatform
  • Jetpack Compose
  • Kotlin Coroutines & Flows
  • Dagger Hilt
  • SwiftUI
  • Koin Dependency Injection
  • FlowRedux State Management
  • Shared KMP ViewModel
  • Clean Architecture

Tech Stacks

Screenshots

Android (Light theme)

Android (Dark theme)

iOS (Light theme)

iOS (Dark theme)

Overall Architecture

What is shared?

  • domain: Domain models, UseCases, Repositories.
  • presentation: ViewModels, ViewState, ViewSingleEvent, ViewAction.
  • data: Repository Implementations, Remote Data Source, Local Data Source.
  • utils: Utilities, Logging Library

Unidirectional data flow - FlowRedux

RxRedux In a Nutshell

public sealed interface FlowReduxStore<Action, State> {
  /**
   * The state of this store.
   */
  public val stateFlow: StateFlow<State>

  /**
   * @return false if cannot dispatch action (this store was closed).
   */
  public fun dispatch(action: Action): Boolean

  /**
   * Call this method to close this store.
   * A closed store will not accept any action anymore, thus state will not change anymore.
   * All [SideEffect]s will be cancelled.
   */
  public fun close()

  /**
   * After calling [close] method, this function will return true.
   *
   * @return true if this store was closed.
   */
  public fun isClosed(): Boolean
}

Multiplatform ViewModel

open class GithubSearchViewModel(
  searchRepoItemsUseCase: SearchRepoItemsUseCase,
  private val savedStateHandle: SavedStateHandle,
) : ViewModel() {
  private val effectsContainer = GithubSearchSideEffectsContainer(searchRepoItemsUseCase)

  private val store = viewModelScope.createFlowReduxStore(
    initialState = GithubSearchState.initial(),
    sideEffects = effectsContainer.sideEffects,
    reducer = Reducer(flip(GithubSearchAction::reduce))
      .withLogger(githubSearchFlowReduxLogger())
  )

  val termStateFlow: NonNullStateFlowWrapper<String> = savedStateHandle.getStateFlow(TERM_KEY, "").wrap()
  val stateFlow: NonNullStateFlowWrapper<GithubSearchState> = store.stateFlow.wrap()
  val eventFlow: NonNullFlowWrapper<GithubSearchSingleEvent> = effectsContainer.eventFlow.wrap()

  init {
    store.dispatch(InitialSearchAction(termStateFlow.value))
  }

  @MainThread
  fun dispatch(action: GithubSearchAction): Boolean {
    if (action is GithubSearchAction.Search) {
      savedStateHandle[TERM_KEY] = action.term
    }
    return store.dispatch(action)
  }

  companion object {
    private const val TERM_KEY = "com.hoc081098.github_search_kmm.presentation.GithubSearchViewModel.term"

    /**
     * Used by non-Android platforms.
     */
    fun create(searchRepoItemsUseCase: SearchRepoItemsUseCase): GithubSearchViewModel =
      GithubSearchViewModel(searchRepoItemsUseCase, SavedStateHandle())
  }
}

Platform ViewModel

Android

Extends GithubSearchViewModel to use Dagger Constructor Injection.

@HiltViewModel
class DaggerGithubSearchViewModel @Inject constructor(
  searchRepoItemsUseCase: SearchRepoItemsUseCase,
  savedStateHandle: SavedStateHandle,
) : GithubSearchViewModel(searchRepoItemsUseCase, savedStateHandle)

iOS

Conform to ObservableObject and use @Published property wrapper.

import Foundation
import Combine
import shared

@MainActor
class IOSGithubSearchViewModel: ObservableObject {
  private let vm: GithubSearchViewModel

  @Published private(set) var state: GithubSearchState
  @Published private(set) var term: String = ""
  let eventPublisher: AnyPublisher<GithubSearchSingleEventKs, Never>

  init(vm: GithubSearchViewModel) {
    self.vm = vm

    self.eventPublisher = vm.eventFlow.asNonNullPublisher()
      .assertNoFailure()
      .map(GithubSearchSingleEventKs.init)
      .eraseToAnyPublisher()

    self.state = vm.stateFlow.value
    vm.stateFlow.subscribe(
      scope: vm.viewModelScope,
      onValue: { [weak self] in self?.state = $0 }
    )

    self.vm
      .termStateFlow
      .asNonNullPublisher(NSString.self)
      .assertNoFailure()
      .map { $0 as String }
      .assign(to: &$term)
  }

  @discardableResult
  func dispatch(action: GithubSearchAction) -> Bool {
    self.vm.dispatch(action: action)
  }

  deinit {
    Napier.d("\(self)::deinit")
    vm.clear()
  }
}

Download APK

Building & Develop

  • Android Studio Hedgehog | 2023.1.1 (note: Java 17 is now the minimum version required).

  • Xcode 13.2.1 or later (due to use of new Swift 5.5 concurrency APIs).

  • Clone project: git clone https://github.com/hoc081098/GithubSearchKMM.git

  • Android: open project by Android Studio and run as usual.

  • iOS

    # Cd to root project directory
    cd GithubSearchKMM
    
    # Setup
    sh scripts/run_ios.sh

    There's a Build Phase script that will do the magic. 🧞
    Cmd + B to build
    Cmd + R to run.

    You can also build and run iOS app from Xcode as usual.

LOC

--------------------------------------------------------------------------------
 Language             Files        Lines        Blank      Comment         Code
--------------------------------------------------------------------------------
 Kotlin                 116         7942          996          453         6493
 JSON                     7         3938            0            0         3938
 Swift                   16          960          124          102          734
 Markdown                 1          281           53            0          228
 Bourne Shell             2          249           28          116          105
 Batch                    1           92           21            0           71
 XML                      6           69            6            0           63
--------------------------------------------------------------------------------
 Total                  149        13531         1228          671        11632
--------------------------------------------------------------------------------

githubsearchkmm-compose-swiftui's People

Contributors

dependabot[bot] avatar hoc081098 avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

githubsearchkmm-compose-swiftui's Issues

When we should use flatMapLatest or flatMapConcat

First of all thanks for the amazing reactive and coroutine setup.

I am a bit new to reactive programming and the Rx family.

In GithubSearchSideEffects.kt file can you help and tell which decisions helps in deciding if flatMapLatest or flatMapConcat need to be used.

Like in searchAPICall you have used flatMapLatest

and on next page and retry you have used flatMapConcat

Can you please explain rationale behind this?

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Rate-Limited

These updates are currently rate-limited. Click on a checkbox below to force their creation now.

  • Update Kotlin, KSP and Compose Compiler (composeOptions, serialization, org.jetbrains.kotlin:kotlin-gradle-plugin)
  • Update deps.arrow.version to v1.2.4 (io.arrow-kt:arrow-fx-coroutines, io.arrow-kt:arrow-core)
  • Update deps.ktor.version to v2.3.11 (io.ktor:ktor-serialization-kotlinx-json, io.ktor:ktor-client-content-negotiation, io.ktor:ktor-client-mock, io.ktor:ktor-client-serialization, io.ktor:ktor-client-darwin, io.ktor:ktor-client-okhttp, io.ktor:ktor-client-logging, io.ktor:ktor-client-json, io.ktor:ktor-client-core)
  • Update plugin co.touchlab.skie to v0.6.4
  • Update dependency Kingfisher to ~> 7.11.0
  • Update dependency androidx.activity:activity-compose to v1.9.0
  • Update dependency androidx.compose:compose-bom to v2024.05.00
  • Update dependency app.cash.turbine:turbine to v1.1.0
  • Update dependency com.google.android.material:material to v1.12.0
  • Update dependency com.slack.lint.compose:compose-lint-checks to v1.3.1
  • Update dependency com.squareup.leakcanary:leakcanary-android to v2.14
  • Update dependency com.squareup.retrofit2:converter-moshi to v2.11.0
  • Update dependency com.squareup.retrofit2:retrofit to v2.11.0
  • Update dependency io.coil-kt:coil-compose to v2.6.0
  • Update dependency io.github.aakira:napier to v2.7.1
  • Update dependency io.mockative:mockative to v2.2.0
  • Update dependency io.mockative:mockative-processor to v2.2.0
  • Update dependency org.jetbrains.kotlinx:atomicfu to v0.24.0
  • Update deps.dagger.version to v2.51.1 (com.google.dagger:hilt-android-gradle-plugin, com.google.dagger:hilt-android-compiler, com.google.dagger:hilt-android)
  • Update sersoft-gmbh/xcodebuild-action action to v3.2.0
  • Update actions/cache action to v4
  • Update actions/setup-java action to v4
  • Update actions/upload-artifact action to v4
  • Update codecov/codecov-action action to v4
  • Update dependency macos to v14
  • Update gradle/wrapper-validation-action action to v3
  • πŸ” Create all rate-limited PRs at once πŸ”

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

cocoapods
iosApp/Podfile
  • Kingfisher ~> 7.6.1
github-actions
.github/workflows/build.yml
  • actions/checkout v4
  • actions/setup-java v3
  • actions/cache v3
  • actions/cache v3
  • actions/upload-artifact v3
.github/workflows/gradle-versions-checker.yml
  • actions/checkout v4
  • actions/setup-java v3
  • actions/cache v3
.github/workflows/gradle-wrapper-validation.yml
  • actions/checkout v4
  • gradle/wrapper-validation-action v1
.github/workflows/ios-build.yml
  • actions/checkout v4
  • actions/setup-java v3
  • maxim-lobanov/setup-xcode v1.6.0
  • actions/cache v3
  • actions/cache v3
  • sersoft-gmbh/xcodebuild-action v3.1.0
  • actions/upload-artifact v3
  • macos 14
.github/workflows/remove-old-artifacts.yml
  • c-hive/gha-remove-artifacts v1
.github/workflows/review-suggest.yml
  • actions/checkout v4
  • actions/checkout v4
  • actions/setup-java v3
  • actions/cache v3
  • reviewdog/action-suggester v1
.github/workflows/test.yml
  • actions/checkout v4
  • actions/setup-java v3
  • actions/cache v3
  • actions/cache v3
  • codecov/codecov-action v3.1.6
  • macos 12
gradle
buildSrc/src/main/kotlin/deps.kt
  • androidx.appcompat:appcompat 1.6.1
  • androidx.core:core-ktx 1.9.0
  • com.google.android.material:material 1.10.0
  • androidx.activity:activity-compose 1.8.2
  • androidx.hilt:hilt-navigation-compose 1.1.0
  • androidx.lifecycle:lifecycle-viewmodel-ktx 2.7.0
  • androidx.lifecycle:lifecycle-runtime-ktx 2.7.0
  • androidx.lifecycle:lifecycle-runtime-compose 2.7.0
  • androidx.lifecycle:lifecycle-common-java8 2.7.0
  • com.squareup.retrofit2:retrofit 2.9.0
  • com.squareup.retrofit2:converter-moshi 2.9.0
  • com.squareup.okhttp3:logging-interceptor 5.0.0-alpha.2
  • com.squareup.moshi:moshi-kotlin 1.15.1
  • com.squareup.leakcanary:leakcanary-android 2.12
  • org.jetbrains.kotlinx:kotlinx-coroutines-core 1.8.0
  • org.jetbrains.kotlinx:kotlinx-coroutines-android 1.8.0
  • org.jetbrains.kotlinx:kotlinx-coroutines-test 1.8.0
  • org.jetbrains.kotlinx:kotlinx-serialization-json 1.6.3
  • org.jetbrains.kotlinx:kotlinx-serialization-core 1.6.3
  • io.ktor:ktor-client-core 2.3.8
  • io.ktor:ktor-client-json 2.3.8
  • io.ktor:ktor-client-logging 2.3.8
  • io.ktor:ktor-client-okhttp 2.3.8
  • io.ktor:ktor-client-darwin 2.3.8
  • io.ktor:ktor-client-serialization 2.3.8
  • io.ktor:ktor-client-mock 2.3.8
  • io.ktor:ktor-client-content-negotiation 2.3.8
  • io.ktor:ktor-serialization-kotlinx-json 2.3.8
  • androidx.compose:compose-bom 2024.03.00
  • io.insert-koin:koin-core 3.5.0
  • io.insert-koin:koin-test-junit4 3.5.0
  • io.insert-koin:koin-test 3.5.0
  • com.google.dagger:hilt-android 2.49
  • com.google.dagger:hilt-android-compiler 2.49
  • io.github.hoc081098:kmp-viewmodel 0.7.1
  • io.github.hoc081098:kmp-viewmodel-savedstate 0.7.1
  • org.jetbrains.kotlinx:kotlinx-datetime 0.5.0
  • io.coil-kt:coil-compose 2.5.0
  • io.github.hoc081098:FlowExt 0.8.0
  • org.jetbrains.kotlinx:atomicfu 0.23.2
  • org.jetbrains.kotlinx:kotlinx-collections-immutable 0.3.7
  • io.arrow-kt:arrow-core 1.2.1
  • io.arrow-kt:arrow-fx-coroutines 1.2.1
  • com.android.tools:desugar_jdk_libs 2.0.4
  • io.github.aakira:napier 2.6.1
  • com.slack.lint.compose:compose-lint-checks 1.2.0
  • junit:junit 4.13.2
  • androidx.test:core-ktx 1.4.0
  • androidx.test.ext:junit-ktx 1.1.3
  • androidx.test.espresso:espresso-core 3.4.0
  • io.mockative:mockative 2.0.1
  • io.mockative:mockative-processor 2.0.1
  • app.cash.turbine:turbine 1.0.0
  • org.jetbrains.kotlin:kotlin-gradle-plugin 1.9.23
  • serialization 1.9.23
  • com.android.tools.build:gradle 8.3.1
  • com.google.dagger:hilt-android-gradle-plugin 2.49
  • com.diffplug.spotless:spotless-plugin-gradle 6.23.3
  • com.github.ben-manes:gradle-versions-plugin 0.50.0
  • composeOptions 1.5.11
gradle.properties
settings.gradle.kts
  • org.gradle.toolchains.foojay-resolver-convention 0.8.0
build.gradle.kts
  • org.jetbrains.kotlinx:kover 0.6.1
  • com.squareup:javapoet 1.13.0
androidApp/build.gradle.kts
buildSrc/gradle.properties
buildSrc/build.gradle.kts
buildSrc/buildSrc/gradle.properties
flowredux/build.gradle.kts
shared/build.gradle.kts
  • co.touchlab.skie 0.6.3
  • co.touchlab:stately-common 2.0.7
gradle-wrapper
buildSrc/buildSrc/gradle/wrapper/gradle-wrapper.properties
  • gradle 8.7
buildSrc/gradle/wrapper/gradle-wrapper.properties
  • gradle 8.7
gradle/wrapper/gradle-wrapper.properties
  • gradle 8.7

  • Check this box to trigger a request for Renovate to run again on this repository

Can add function about User ?

Dear sir

can add

  1. search / list User (Users) : like hoc081098 ( api.github.com/users/hoc081098)
  2. list all (public) repos of user
  3. repo (repos ) fork / Transfer ownership to other user
    THX

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.