├── .editorconfig ├── .github ├── CODEOWNERS └── workflows │ ├── check.yml │ ├── pr.yml │ ├── release.yml │ └── resolve-version.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build-conventions ├── build.gradle.kts ├── gradle ├── gradle.properties ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ ├── convention.atomicfu.gradle.kts │ ├── convention.common.gradle.kts │ ├── convention.control.gradle.kts │ ├── convention.detekt.gradle.kts │ ├── convention.git-hooks.gradle.kts │ ├── convention.library-android.gradle.kts │ ├── convention.library-mpp-all.gradle.kts │ ├── convention.library-mpp-loved.gradle.kts │ ├── convention.local-properties.gradle.kts │ ├── convention.mpp-all.gradle.kts │ ├── convention.mpp-loved.gradle.kts │ ├── convention.publishing-mpp.gradle.kts │ ├── convention.publishing-nexus.gradle.kts │ ├── convention.publishing.gradle.kts │ └── util │ ├── KotlinTargetDetails.kt │ ├── _global.kt │ ├── gradle.kt │ └── targetGroup.kt ├── build.gradle.kts ├── examples ├── build.gradle.kts ├── counter │ ├── README.md │ ├── android │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── org │ │ │ │ └── reduxkotlin │ │ │ │ └── example │ │ │ │ └── counter │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ ├── ic_arrow_downward_white_24dp.xml │ │ │ ├── ic_arrow_upward_white_24dp.xml │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ └── activity_main.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ └── common │ │ ├── build.gradle.kts │ │ └── src │ │ ├── commonMain │ │ └── kotlin │ │ │ └── org │ │ │ └── reduxkotlin │ │ │ └── examples │ │ │ └── counter │ │ │ └── Reducer.kt │ │ └── commonTest │ │ └── kotlin │ │ └── org │ │ └── reduxkotlin │ │ └── examples │ │ └── counter │ │ └── ReducerTest.kt ├── gradle ├── gradle.properties ├── gradlew ├── gradlew.bat ├── kotlin-js-store │ └── yarn.lock ├── settings.gradle.kts └── todos │ ├── android │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── org │ │ │ └── reduxkotlin │ │ │ └── example │ │ │ └── todos │ │ │ ├── MainActivity.kt │ │ │ └── TodoAdapter.kt │ │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── btn_background.xml │ │ ├── ic_arrow_downward_white_24dp.xml │ │ ├── ic_arrow_upward_white_24dp.xml │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── item_todo.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── strings.xml │ │ └── styles.xml │ └── common │ ├── build.gradle.kts │ └── src │ ├── commonMain │ └── kotlin │ │ └── org │ │ └── reduxkotlin │ │ └── examples │ │ └── todos │ │ ├── Actions.kt │ │ ├── AppState.kt │ │ ├── RootReducer.kt │ │ ├── TodosReducer.kt │ │ └── VisibilityFilterReducer.kt │ └── commonTest │ └── kotlin │ └── org │ └── reduxkotlin │ └── examples │ └── todos │ └── TodosReducerTest.kt ├── gradle.properties ├── gradle ├── detekt.yml ├── versions.properties ├── versions.rules └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── kotlin-js-store └── yarn.lock ├── redux-kotlin-threadsafe ├── build.gradle.kts └── src │ ├── commonMain │ └── kotlin │ │ └── org │ │ └── reduxkotlin │ │ └── threadsafe │ │ ├── CreateThreadSafeStore.kt │ │ ├── Enhancers.kt │ │ └── ThreadSafeStore.kt │ └── jvmCommonTest │ └── kotlin │ ├── org │ └── reduxkotlin │ │ └── threadsafe │ │ └── CreateThreadSafeStoreTest.kt │ └── test │ └── TestApp.kt ├── redux-kotlin ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidNativeMain │ └── kotlin │ │ └── org │ │ └── reduxkotlin │ │ └── utils │ │ └── ThreadUtilAndroidNative.kt │ ├── appleMain │ └── kotlin │ │ └── org │ │ └── reduxkotlin │ │ └── utils │ │ └── ThreadUtilApple.kt │ ├── commonMain │ └── kotlin │ │ └── org │ │ └── reduxkotlin │ │ ├── ActionTypes.kt │ │ ├── ApplyMiddleware.kt │ │ ├── CombineReducers.kt │ │ ├── Compose.kt │ │ ├── CreateSameThreadEnforcedStore.kt │ │ ├── CreateStore.kt │ │ ├── Definitions.kt │ │ └── utils │ │ ├── IsPlainObject.kt │ │ └── ThreadUtil.kt │ ├── commonTest │ └── kotlin │ │ ├── org │ │ └── reduxkotlin │ │ │ ├── ApplyMiddlewareTest.kt │ │ │ ├── CreateStoreTest.kt │ │ │ └── TypedReducerTest.kt │ │ └── test │ │ └── TodoApp.kt │ ├── jsMain │ └── kotlin │ │ └── org │ │ └── reduxkotlin │ │ └── utils │ │ └── ThreadUtilJs.kt │ ├── jvmCommonMain │ └── kotlin │ │ └── org │ │ └── reduxkotlin │ │ └── utils │ │ └── ThreadUtilJvm.kt │ ├── jvmCommonTest │ └── kotlin │ │ └── org │ │ └── reduxkotlin │ │ └── CreateSameThreadEnforcedStoreTest.kt │ ├── linuxMain │ └── kotlin │ │ └── org │ │ └── reduxkotlin │ │ └── utils │ │ └── ThreadUtiliLinux.kt │ └── mingwMain │ └── kotlin │ └── org │ └── reduxkotlin │ └── utils │ └── ThreadUtilMingw.kt ├── renovate.json ├── settings.gradle.kts └── website ├── .gitignore ├── README.md ├── _redirects ├── core └── Footer.js ├── docs ├── FAQ.md ├── Feedback.md ├── Glossary.md ├── README.md ├── Troubleshooting.md ├── advanced │ ├── AsyncActions.md │ ├── AsyncFlow.md │ ├── Middleware.md │ └── README.md ├── api │ ├── README.md │ ├── Store.md │ ├── applyMiddleware.md │ ├── compose.md │ ├── createSameThreadEnforcedStore.md │ ├── createStore.md │ └── createThreadSafeStore.md ├── basics │ ├── Actions.md │ ├── DataFlow.md │ ├── README.md │ ├── Reducers.md │ └── Store.md ├── faq │ ├── General.md │ ├── Multiplatform.md │ ├── Reducers.md │ └── StoreSetup.md └── introduction │ ├── CoreConcepts.md │ ├── Ecosystem.md │ ├── Examples.md │ ├── GettingStarted.md │ ├── LearningResources.md │ ├── Motivation.md │ ├── README.md │ ├── Threading.md │ └── ThreePrinciples.md ├── i18n └── en.json ├── package.json ├── pages └── en │ ├── 404.js │ └── index.js ├── sidebars.json ├── siteConfig.js ├── static ├── css │ ├── 404.css │ ├── codeblock.css │ └── custom.css ├── img │ ├── cogs-solid.svg │ ├── cubes-solid.svg │ ├── external-link-square-alt-solid.svg │ ├── favicon │ │ └── favicon.ico │ ├── github-brands.svg │ ├── multiplatform-screen-512.png │ ├── noun_Check_1870817.svg │ ├── noun_debugging_1978252.svg │ ├── redux-logo-landscape.png │ ├── redux-logo-twitter.png │ ├── redux.svg │ ├── redux_white.svg │ ├── reduxkotlin.png │ └── reduxkotlin.svg └── scripts │ ├── codeblock.js │ └── sidebarScroll.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{kt,kts}] 4 | indent_size = 4 5 | ignored_rules = no-wildstar-imports 6 | insert_final_newline = true -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @patjackson52 @mpetuska 2 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: PR 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | env: 9 | GRADLE_OPTS: "-Dorg.gradle.daemon=true" 10 | 11 | concurrency: 12 | cancel-in-progress: true 13 | group: pr-${{ github.workflow }}-${{ github.head_ref || github.ref }} 14 | 15 | jobs: 16 | check: 17 | uses: ./.github/workflows/check.yml -------------------------------------------------------------------------------- /.github/workflows/resolve-version.yml: -------------------------------------------------------------------------------- 1 | name: Resolve Version 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | desired-version: 7 | type: string 8 | description: Optional desired version 9 | required: false 10 | outputs: 11 | version: 12 | value: ${{ jobs.resolve.outputs.version }} 13 | snapshot: 14 | value: ${{ jobs.resolve.outputs.snapshot }} 15 | 16 | concurrency: 17 | cancel-in-progress: true 18 | group: version-${{ github.workflow }}-${{ github.head_ref || github.ref }} 19 | 20 | jobs: 21 | resolve: 22 | name: Resolve Version 23 | runs-on: ubuntu-latest 24 | env: 25 | VERSION: ${{ inputs.desired-version }} 26 | outputs: 27 | version: ${{ steps.resolve.outputs.version }} 28 | snapshot: ${{ steps.resolve.outputs.snapshot }} 29 | steps: 30 | - uses: actions/checkout@v3 31 | - name: Resolve 32 | id: resolve 33 | run: | 34 | project_version=$(cat gradle.properties | grep -Po '^version=\K(.+)') 35 | version=${VERSION:=$project_version} 36 | VERSION=${VERSION/v} 37 | echo "PROJECT_VERSION=${project_version}" >> $GITHUB_ENV 38 | echo "VERSION=${VERSION}" >> $GITHUB_ENV 39 | echo "::set-output name=version::${VERSION}" 40 | if [[ "$VERSION" != "$project_version" ]]; then 41 | echo "DIFF_VERSION=1" >> $GITHUB_ENV 42 | fi 43 | if [[ "$VERSION" == *-SNAPSHOT ]]; then 44 | echo "::set-output name=snapshot::true" 45 | else 46 | echo "::set-output name=snapshot::false" 47 | fi 48 | - name: Report 49 | run: | 50 | echo "VERSION=${{ env.VERSION }}" 51 | echo "steps.resolve.outputs.version=${{ steps.resolve.outputs.version }}" 52 | - name: Create Snapshot 53 | if: env.DIFF_VERSION == '1' 54 | run: | 55 | main_version=$(echo $VERSION | grep -Po '^([0-9]+.){2}(?=.*)') 56 | patch_version=$(echo $VERSION | grep -Po "^$main_version\\K([0-9]+)(?=.*)") 57 | patch_version=$(expr $patch_version + 1) 58 | SNAPSHOT_VERSION="${main_version}${patch_version}-SNAPSHOT" 59 | echo "SNAPSHOT_VERSION=$SNAPSHOT_VERSION" >> $GITHUB_ENV 60 | sed -Ei "s|^(version=).*\$|\\1$SNAPSHOT_VERSION|" gradle.properties 61 | - name: Create Pull Request for new SNAPSHOT 62 | if: env.DIFF_VERSION == '1' && env.PROJECT_VERSION != env.SNAPSHOT_VERSION 63 | uses: peter-evans/create-pull-request@v5 64 | with: 65 | title: 'New SNAPSHOT - ${{ env.SNAPSHOT_VERSION }}' 66 | commit-message: '[ci skip] New SNAPSHOT - ${{ env.SNAPSHOT_VERSION }}' 67 | branch: ci/version/${{ env.SNAPSHOT_VERSION }} 68 | delete-branch: true 69 | base: master 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle/ 3 | lint.xml 4 | /local.properties 5 | .idea/ 6 | /iOS/NameGame/.idea/ 7 | .DS_Store 8 | *.hprof 9 | /captures 10 | .externalNativeBuild 11 | node_modules/ 12 | 13 | # Created by https://www.gitignore.io/api/swift,xcode 14 | 15 | ### Swift ### 16 | # Xcode 17 | # 18 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 19 | 20 | ## Build generated 21 | build/ 22 | DerivedData/ 23 | 24 | ## Various settings 25 | *.pbxuser 26 | !default.pbxuser 27 | *.mode1v3 28 | !default.mode1v3 29 | *.mode2v3 30 | !default.mode2v3 31 | *.perspectivev3 32 | !default.perspectivev3 33 | xcuserdata/ 34 | 35 | ## Other 36 | *.moved-aside 37 | *.xccheckout 38 | *.xcscmblueprint 39 | 40 | ## Obj-C/Swift specific 41 | *.hmap 42 | *.ipa 43 | *.dSYM.zip 44 | *.dSYM 45 | 46 | ## Playgrounds 47 | timeline.xctimeline 48 | playground.xcworkspace 49 | 50 | # Swift Package Manager 51 | # 52 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 53 | # Packages/ 54 | # Package.pins 55 | .build/ 56 | 57 | # CocoaPods - Refactored to standalone file 58 | 59 | # Carthage - Refactored to standalone file 60 | 61 | # fastlane 62 | # 63 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 64 | # screenshots whenever they are needed. 65 | # For more information about the recommended setup visit: 66 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 67 | 68 | fastlane/report.xml 69 | fastlane/Preview.html 70 | fastlane/screenshots 71 | fastlane/test_output 72 | 73 | ### Xcode ### 74 | # Xcode 75 | # 76 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 77 | 78 | ## Build generated 79 | 80 | ## Various settings 81 | 82 | ## Other 83 | 84 | 85 | # End of https://www.gitignore.io/api/swift,xcode 86 | 87 | # From redux 88 | dist/ 89 | es 90 | coverage/ 91 | 92 | website/translated_docs/ 93 | website/build/ 94 | website/node_modules/ 95 | website/i18n/* 96 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | --- 9 | 10 | ## [Unreleased] 11 | 12 | --- 13 | 14 | ## [0.6.0] 15 | 16 | ### Added 17 | 18 | - All missing ios, watchos, tvos and macos simulator targets added 19 | - Added `androidNativeX64` and `androidNativeX86` targets 20 | - Added proper android release and debug variants instead of piggybacking on jvm artefact 21 | - New and improved `typedReducer` and `createTypedStore` builders for those needing a simple action-typed store. 22 | Recommended to use with sealed interface hierarchies. 23 | 24 | ### Changed 25 | 26 | - Major gradle infra rework 27 | - Enabled `explicitPublicApi()` 28 | - **BREAKING**: `redux-kotlin-threadsafe` APIs moved to a new package: `org.reduxkotlin.threadsafe` 29 | 30 | ### Removed 31 | 32 | - Remove deprecated `wasm32` target 33 | 34 | --- 35 | 36 | ## [0.5.5] - 2020-08-16 37 | 38 | - update to Kotlin 1.4.0 39 | - added platforms (androidNativeArm32, androidNativeArm64, iosArm32, linuxArm64, linuxX64, 40 | mingwX86, tvosArm64, tvosX64, watchosArm32, watchosArm64, watchosX86) 41 | - remove spek & atrium deps and use plain kotlin tests & assertions. Tests run for all platforms now. 42 | 43 | --- 44 | 45 | ## [0.5.2] - 2020-07-03 46 | 47 | - publish all available platforms to maven 48 | - add CI/CD through github actions 49 | 50 | --- 51 | 52 | ## [0.5.1] - 2020-06-11 53 | 54 | - update lib dependency to api import, so core lib is included in redux-kotlin-threadsafe 55 | 56 | --- 57 | 58 | ## [0.5.0] - 2020-06-11 59 | 60 | - kotlin 1.3.72 61 | - createThreadSafeStore fun added for thread synchronized access 62 | - createEnsureSameThreadStore to provide existing same-thread-enforcement 63 | 64 | --- 65 | 66 | ## [0.4.0] - 2020-03-23 67 | 68 | - kotlin 1.3.70 69 | 70 | --- 71 | 72 | ## [0.3.2] - 2020-02-22 73 | 74 | - issue #34 - incorrect same thread enforcement behavior fixed 75 | 76 | --- 77 | 78 | ## [0.3.1] - 2019-12-16 79 | 80 | ### Changed 81 | 82 | - update same thread enforcement message to not be getState only 83 | 84 | --- 85 | 86 | ## [0.3.0] - 2019-12-16 87 | 88 | ### Added 89 | 90 | - thread enforcement 91 | 92 | --- 93 | 94 | ## [0.2.9] - 2019-11-23 95 | 96 | ### Changed 97 | 98 | - update Kotlin to 1.3.60 99 | 100 | --- 101 | 102 | [Unreleased]: https://github.com/reduxkotlin/redux-kotlin/compare/v0.6.0...HEAD 103 | [0.6.0]: https://github.com/reduxkotlin/redux-kotlin/compare/v0.5.5...0.6.0 104 | [0.5.5]: https://github.com/reduxkotlin/redux-kotlin/releases/tag/v0.5.5 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019-present ReduxKotlin contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /build-conventions/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | `kotlin-dsl` 5 | } 6 | 7 | repositories { 8 | gradlePluginPortal() 9 | mavenCentral() 10 | google() 11 | if (findProperty("project.enableSnapshots") == "true") { 12 | maven("https://oss.sonatype.org/content/repositories/snapshots") 13 | } 14 | } 15 | 16 | dependencies { 17 | implementation("com.android.tools.build:gradle:_") 18 | implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:_") 19 | implementation("com.github.jakemarsden:git-hooks-gradle-plugin:_") 20 | implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:_") 21 | implementation("io.github.gradle-nexus:publish-plugin:_") 22 | implementation("org.jetbrains.dokka:dokka-gradle-plugin:_") 23 | implementation("org.jetbrains.kotlinx:atomicfu-gradle-plugin:_") 24 | } 25 | 26 | tasks { 27 | withType { 28 | kotlinOptions { 29 | languageVersion = "1.4" // 1.9 since gradle 8 30 | } 31 | } 32 | } 33 | 34 | gradleEnterprise { 35 | buildScan { 36 | termsOfServiceUrl = "https://gradle.com/terms-of-service" 37 | termsOfServiceAgree = "yes" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /build-conventions/gradle: -------------------------------------------------------------------------------- 1 | ../gradle -------------------------------------------------------------------------------- /build-conventions/gradle.properties: -------------------------------------------------------------------------------- 1 | ../gradle.properties -------------------------------------------------------------------------------- /build-conventions/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("de.fayard.refreshVersions") version "0.51.0" 3 | id("com.gradle.enterprise") version "3.12.3" 4 | } 5 | 6 | refreshVersions { 7 | versionsPropertiesFile = rootDir.resolve("gradle/versions.properties") 8 | extraArtifactVersionKeyRules(rootDir.resolve("gradle/versions.rules")) 9 | } 10 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/convention.atomicfu.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kotlinx-atomicfu") 3 | } 4 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/convention.common.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.konan.target.HostManager 2 | 3 | plugins { 4 | id("convention.local-properties") 5 | id("convention.detekt") 6 | idea 7 | } 8 | 9 | repositories { 10 | mavenCentral() 11 | google() 12 | gradlePluginPortal() 13 | if (findProperty("project.enableSnapshots") == "true") { 14 | maven("https://oss.sonatype.org/content/repositories/snapshots") 15 | } 16 | } 17 | 18 | printlnCI( 19 | """ 20 | CI: $CI 21 | SANDBOX: $SANDBOX 22 | isMainHost: $isMainHost 23 | --- 24 | hostIsLinux: ${HostManager.hostIsLinux} 25 | hostIsMac: ${HostManager.hostIsMac} 26 | hostIsMingw: ${HostManager.hostIsMingw} 27 | """.trimIndent() 28 | ) 29 | 30 | idea { 31 | module { 32 | isDownloadSources = true 33 | isDownloadJavadoc = true 34 | } 35 | } 36 | 37 | afterEvaluate { 38 | tasks { 39 | if (findByName("compile") == null) { 40 | register("compile") { 41 | dependsOn(withType(AbstractCompile::class)) 42 | group = "build" 43 | } 44 | } 45 | if (findByName("allTests") == null) { 46 | register("allTests") { 47 | dependsOn(withType(AbstractTestTask::class)) 48 | group = "verification" 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/convention.control.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension 2 | import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension 3 | import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension 4 | import org.jetbrains.kotlin.gradle.plugin.KotlinTarget 5 | import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget 6 | import org.jetbrains.kotlin.konan.target.Family 7 | import org.jetbrains.kotlin.konan.target.HostManager 8 | import util.buildHost 9 | 10 | plugins { 11 | id("convention.common") 12 | } 13 | 14 | pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") { 15 | extensions.getByType(KotlinMultiplatformExtension::class.java).targets.let(::control) 16 | } 17 | pluginManager.withPlugin("org.jetbrains.kotlin.jvm") { 18 | objects.namedDomainObjectList(KotlinTarget::class.java).apply { 19 | add(extensions.getByType(KotlinJvmProjectExtension::class.java).target) 20 | }.let(::control) 21 | } 22 | pluginManager.withPlugin("org.jetbrains.kotlin.js") { 23 | objects.namedDomainObjectList(KotlinTarget::class.java).apply { 24 | add(extensions.getByType(KotlinJsProjectExtension::class.java).js()) 25 | }.let(::control) 26 | } 27 | 28 | fun control(targets: NamedDomainObjectCollection) { 29 | fun NamedDomainObjectCollection.onlyBuildIf(enabled: Spec) { 30 | all { 31 | if (this is KotlinNativeTarget) { 32 | binaries.all { 33 | linkTask.onlyIf(enabled) 34 | } 35 | } 36 | compilations.all { 37 | compileTaskProvider { 38 | onlyIf(enabled) 39 | } 40 | } 41 | } 42 | } 43 | 44 | val nativeTargets = targets.withType() 45 | val windowsHostTargets = nativeTargets.matching { it.konanTarget.buildHost == Family.MINGW } 46 | val linuxHostTargets = nativeTargets.matching { it.konanTarget.buildHost == Family.LINUX } 47 | val osxHostTargets = nativeTargets.matching { it.konanTarget.buildHost == Family.OSX } 48 | val mainHostTargets = targets.matching { it !in nativeTargets } 49 | val mainEnabled = !CI || isMainHost 50 | linuxHostTargets.onlyBuildIf { 51 | val enabled = mainEnabled || HostManager.hostIsLinux 52 | printlnCI("[${it.name}] ${!CI} || $SANDBOX || ${HostManager.hostIsLinux} = $enabled") 53 | enabled 54 | } 55 | osxHostTargets.onlyBuildIf { 56 | val enabled = mainEnabled || HostManager.hostIsMac 57 | printlnCI("[${it.name}] ${!CI} || $SANDBOX || ${HostManager.hostIsMac} = $enabled") 58 | enabled 59 | } 60 | windowsHostTargets.onlyBuildIf { 61 | val enabled = mainEnabled || HostManager.hostIsMingw 62 | printlnCI("[${it.name}] ${!CI} || $SANDBOX || ${HostManager.hostIsMingw} = $enabled") 63 | enabled 64 | } 65 | mainHostTargets.onlyBuildIf { 66 | val enabled = mainEnabled || SANDBOX 67 | printlnCI("[${it.name}] ${!CI} || $SANDBOX || $isMainHost = $enabled") 68 | mainEnabled 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/convention.detekt.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.gitlab.arturbosch.detekt.Detekt 2 | 3 | plugins { 4 | id("io.gitlab.arturbosch.detekt") 5 | } 6 | 7 | dependencies { 8 | detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:_") 9 | } 10 | 11 | detekt { 12 | config.from(rootDir.resolve("gradle/detekt.yml")) 13 | buildUponDefaultConfig = true 14 | source = files("src/", "*.kts") 15 | } 16 | 17 | tasks { 18 | if (project == rootProject) { 19 | register("detektAll", Detekt::class) { 20 | description = "Run Detekt for all modules" 21 | config.from(project.detekt.config) 22 | buildUponDefaultConfig = project.detekt.buildUponDefaultConfig 23 | setSource(files(projectDir)) 24 | } 25 | } 26 | afterEvaluate { 27 | withType { 28 | parallel = true 29 | reports { 30 | // observe findings in your browser with structure and code snippets 31 | html.required.set(true) 32 | // checkstyle like format mainly for integrations like Jenkins 33 | xml.required.set(true) 34 | // similar to the console output, contains issue signature to manually edit baseline files 35 | txt.required.set(true) 36 | // standardized SARIF format (https://sarifweb.azurewebsites.net/) to support integrations with Github Code Scanning 37 | sarif.required.set(true) 38 | } 39 | include("**/*.kt", "**/*.kts") 40 | exclude("**/build", "scripts/") 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/convention.git-hooks.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.github.jakemarsden.git-hooks") 3 | } 4 | 5 | gitHooks { 6 | setHooks( 7 | mapOf( 8 | "pre-commit" to "detektAll --auto-correct", 9 | "pre-push" to "detektAll" 10 | ) 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/convention.library-android.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.common") 3 | id("com.android.library") 4 | } 5 | 6 | android { 7 | compileSdk = 33 8 | defaultConfig { 9 | minSdk = 21 10 | targetSdk = 33 11 | // testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 12 | publishing { 13 | multipleVariants { 14 | withSourcesJar() 15 | withJavadocJar() 16 | allVariants() 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/convention.library-mpp-all.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.mpp-all") 3 | id("convention.library-mpp-loved") 4 | } 5 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/convention.library-mpp-loved.gradle.kts: -------------------------------------------------------------------------------- 1 | import util.jvmCommonTest 2 | 3 | plugins { 4 | id("convention.mpp-loved") 5 | id("convention.library-android") 6 | id("convention.control") 7 | } 8 | 9 | kotlin { 10 | explicitApi() 11 | android { 12 | if (!CI || SANDBOX || isMainHost) { 13 | publishLibraryVariants("release", "debug") 14 | } 15 | } 16 | 17 | sourceSets { 18 | named("commonTest") { 19 | dependencies { 20 | implementation(kotlin("test-common")) 21 | implementation(kotlin("test-annotations-common")) 22 | } 23 | } 24 | named("androidMain") { 25 | val jvmCommonMain by getting 26 | kotlin.srcDir(jvmCommonMain.kotlin) 27 | resources.srcDir(jvmCommonMain.resources) 28 | } 29 | named("androidUnitTest") { 30 | val jvmCommonTest by getting 31 | kotlin.srcDir(jvmCommonTest.kotlin) 32 | resources.srcDir(jvmCommonTest.resources) 33 | } 34 | named("jsTest") { 35 | dependencies { 36 | implementation(kotlin("test-js")) 37 | } 38 | } 39 | jvmCommonTest { 40 | dependencies { 41 | implementation(kotlin("test-junit")) 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/convention.local-properties.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.util.* 2 | 3 | rootDir.resolve("local.properties").takeIf(File::exists)?.let { 4 | Properties().apply { 5 | it.inputStream().use(::load) 6 | }.mapKeys { (k, _) -> k.toString() } 7 | }?.toList()?.forEach { (k, v) -> 8 | project.extra[k] = v 9 | } 10 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/convention.mpp-all.gradle.kts: -------------------------------------------------------------------------------- 1 | import util.targetGroup 2 | 3 | plugins { 4 | id("convention.mpp-loved") 5 | } 6 | 7 | kotlin { 8 | val nativeMain by sourceSets.getting 9 | val nativeTest by sourceSets.getting 10 | targetGroup( 11 | name = "androidNative", 12 | mainSourceSetTarget = nativeMain, 13 | testSourceSetTarget = nativeTest, 14 | androidNativeArm32(), 15 | androidNativeArm64(), 16 | androidNativeX64(), 17 | androidNativeX86(), 18 | ) 19 | targetGroup( 20 | name = "mingw", 21 | mainSourceSetTarget = nativeMain, 22 | testSourceSetTarget = nativeTest, 23 | mingwX86(), 24 | ) 25 | targetGroup( 26 | name = "linux", 27 | mainSourceSetTarget = nativeMain, 28 | testSourceSetTarget = nativeTest, 29 | linuxArm32Hfp(), 30 | linuxArm64(), 31 | linuxMips32(), 32 | linuxMipsel32(), 33 | ) 34 | targetGroup( 35 | name = "watchos", 36 | mainSourceSetTarget = "appleMain", 37 | testSourceSetTarget = "appleTest", 38 | watchosDeviceArm64(), 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/convention.mpp-loved.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget 2 | import util.targetGroup 3 | 4 | plugins { 5 | id("convention.common") 6 | kotlin("multiplatform") 7 | } 8 | 9 | kotlin { 10 | js { 11 | useCommonJs() 12 | browser { testTask { useKarma() } } 13 | nodejs() 14 | } 15 | targetGroup( 16 | name = "jvmCommon", 17 | mainSourceSetTarget = "commonMain", 18 | testSourceSetTarget = "commonTest", 19 | jvm(), 20 | ) 21 | val (nativeMain, nativeTest) = targetGroup( 22 | name = "native", 23 | mainSourceSetTarget = "commonMain", 24 | testSourceSetTarget = "commonTest", 25 | ) 26 | val (appleMain, appleTest) = targetGroup( 27 | name = "apple", 28 | mainSourceSetTarget = nativeMain, 29 | testSourceSetTarget = nativeTest, 30 | ) 31 | targetGroup( 32 | name = "ios", 33 | mainSourceSetTarget = appleMain, 34 | testSourceSetTarget = appleTest, 35 | iosArm32(), 36 | iosArm64(), 37 | iosSimulatorArm64(), 38 | iosX64(), 39 | ) 40 | targetGroup( 41 | name = "tvos", 42 | mainSourceSetTarget = appleMain, 43 | testSourceSetTarget = appleTest, 44 | tvosArm64(), 45 | tvosX64(), 46 | tvosSimulatorArm64(), 47 | ) 48 | targetGroup( 49 | name = "watchos", 50 | mainSourceSetTarget = appleMain, 51 | testSourceSetTarget = appleTest, 52 | watchosArm32(), 53 | watchosArm64(), 54 | watchosX64(), 55 | watchosX86(), 56 | watchosSimulatorArm64() 57 | ) 58 | targetGroup( 59 | name = "macos", 60 | mainSourceSetTarget = appleMain, 61 | testSourceSetTarget = appleTest, 62 | macosX64(), 63 | macosArm64(), 64 | ) 65 | targetGroup( 66 | name = "mingw", 67 | mainSourceSetTarget = nativeMain, 68 | testSourceSetTarget = nativeTest, 69 | mingwX64(), 70 | ) 71 | targetGroup( 72 | name = "linux", 73 | mainSourceSetTarget = nativeMain, 74 | testSourceSetTarget = nativeTest, 75 | linuxX64(), 76 | ) 77 | } 78 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/convention.publishing-mpp.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget 2 | import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget 3 | import org.jetbrains.kotlin.konan.target.Family 4 | import org.jetbrains.kotlin.konan.target.HostManager 5 | import util.buildHost 6 | 7 | plugins { 8 | kotlin("multiplatform") 9 | id("convention.publishing") 10 | } 11 | 12 | kotlin { 13 | fun NamedDomainObjectCollection.onlyPublishIf(enabled: Spec) { 14 | publishing { 15 | publications { 16 | matching { it.name in this@onlyPublishIf.names }.all { 17 | val targetPublication = this@all 18 | tasks { 19 | withType() 20 | .all { onlyIf { publication != targetPublication || enabled(this) } } 21 | withType() 22 | .all { onlyIf { publication.orNull != targetPublication || enabled(this) } } 23 | } 24 | } 25 | } 26 | } 27 | } 28 | 29 | val nativeTargets = targets.withType() 30 | val windowsHostTargets = nativeTargets.matching { it.konanTarget.buildHost == Family.MINGW } 31 | val linuxHostTargets = nativeTargets.matching { it.konanTarget.buildHost == Family.LINUX } 32 | val osxHostTargets = nativeTargets.matching { it.konanTarget.buildHost == Family.OSX } 33 | val mainHostTargets = targets.matching { it !in nativeTargets } 34 | val androidTargets = targets.withType() 35 | val mpp = objects.domainObjectContainer(Named::class.java) 36 | mpp.add(Named { "kotlinMultiplatform" }) 37 | 38 | androidTargets.all { 39 | if (!CI || SANDBOX || isMainHost) { 40 | publishLibraryVariants("release", "debug") 41 | } 42 | } 43 | 44 | linuxHostTargets.onlyPublishIf { 45 | val enabled = !CI || SANDBOX || HostManager.hostIsLinux 46 | printlnCI("[${it.name}] ${!CI} || $SANDBOX || ${HostManager.hostIsLinux} = $enabled") 47 | enabled 48 | } 49 | osxHostTargets.onlyPublishIf { 50 | val enabled = !CI || SANDBOX || HostManager.hostIsMac 51 | printlnCI("[${it.name}] ${!CI} || $SANDBOX || ${HostManager.hostIsMac} = $enabled") 52 | enabled 53 | } 54 | windowsHostTargets.onlyPublishIf { 55 | val enabled = !CI || SANDBOX || HostManager.hostIsMingw 56 | printlnCI("[${it.name}] ${!CI} || $SANDBOX || ${HostManager.hostIsMingw} = $enabled") 57 | enabled 58 | } 59 | mainHostTargets.onlyPublishIf { 60 | val enabled = !CI || SANDBOX || isMainHost 61 | printlnCI("[${it.name}] ${!CI} || $SANDBOX || $isMainHost = $enabled") 62 | enabled 63 | } 64 | mpp.onlyPublishIf { 65 | val enabled = !CI || SANDBOX || isMainHost 66 | println("[${it.name}] ${!CI} || $SANDBOX || $isMainHost = $enabled") 67 | enabled 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/convention.publishing-nexus.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.common") 3 | id("org.jetbrains.dokka") 4 | id("io.github.gradle-nexus.publish-plugin") 5 | } 6 | 7 | nexusPublishing { 8 | repositories { 9 | sonatype { 10 | nexusUrl by uri("https://oss.sonatype.org/service/local/") 11 | snapshotRepositoryUrl by uri("https://oss.sonatype.org/content/repositories/snapshots/") 12 | val checkProp = { pName: String -> 13 | val exists = findProperty("sonatypeUsername")?.toString()?.takeIf(String::isNotBlank) 14 | ?.let { "EXISTS" } ?: "MISSING" 15 | printlnCI("$pName: $exists") 16 | } 17 | checkProp("sonatypeUsername") 18 | checkProp("sonatypePassword") 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/util/KotlinTargetDetails.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import org.jetbrains.kotlin.konan.target.Family 4 | import org.jetbrains.kotlin.konan.target.KonanTarget 5 | 6 | val KonanTarget.buildHost: Family 7 | get() = when (family) { 8 | Family.OSX, 9 | Family.IOS, 10 | Family.TVOS, 11 | Family.WATCHOS -> Family.OSX 12 | 13 | Family.ANDROID, 14 | Family.ZEPHYR, 15 | Family.WASM, 16 | Family.LINUX -> Family.LINUX 17 | 18 | Family.MINGW -> Family.MINGW 19 | } 20 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/util/_global.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("PackageDirectoryMismatch") 2 | 3 | import org.gradle.api.NamedDomainObjectContainer 4 | import org.gradle.api.NamedDomainObjectProvider 5 | import org.gradle.api.Project 6 | import org.gradle.api.provider.Property 7 | import org.gradle.api.provider.Provider 8 | import org.gradle.kotlin.dsl.named 9 | import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet 10 | import org.jetbrains.kotlin.konan.target.HostManager 11 | 12 | val NamedDomainObjectContainer.jsMain: NamedDomainObjectProvider 13 | get() = named("jsMain") 14 | 15 | val NamedDomainObjectContainer.jsTest: NamedDomainObjectProvider 16 | get() = named("jsTest") 17 | 18 | val NamedDomainObjectContainer.jvmMain: NamedDomainObjectProvider 19 | get() = named("jvmMain") 20 | 21 | val NamedDomainObjectContainer.jvmTest: NamedDomainObjectProvider 22 | get() = named("jvmTest") 23 | 24 | infix fun Property.by(value: T) = set(value) 25 | infix fun Property.by(value: Provider) = set(value) 26 | 27 | val CI = System.getenv("CI") != null 28 | val SANDBOX = System.getenv("SANDBOX") != null 29 | 30 | val Project.isMainHost: Boolean 31 | get() = HostManager.simpleOsName().equals("${properties["project.mainOS"]}", true) 32 | 33 | fun printlnCI(text: Any?) { 34 | if (CI) println("[CI]: $text") 35 | } 36 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/util/gradle.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import java.nio.charset.Charset 4 | 5 | object Git { 6 | val headCommitHash by lazy { execAndCapture("git rev-parse --verify HEAD") } 7 | } 8 | 9 | fun execAndCapture(cmd: String): String? { 10 | val child = Runtime.getRuntime().exec(cmd) 11 | child.waitFor() 12 | return if (child.exitValue() == 0) { 13 | child.inputStream.readAllBytes().toString(Charset.defaultCharset()).trim() 14 | } else { 15 | null 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /build-conventions/src/main/kotlin/util/targetGroup.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import org.gradle.api.Action 4 | import org.gradle.api.NamedDomainObjectContainer 5 | import org.gradle.kotlin.dsl.get 6 | import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension 7 | import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet 8 | import org.jetbrains.kotlin.gradle.plugin.KotlinTarget 9 | 10 | fun KotlinMultiplatformExtension.targetGroup( 11 | name: String, 12 | mainSourceSetTarget: KotlinSourceSet, 13 | testSourceSetTarget: KotlinSourceSet, 14 | vararg targets: T 15 | ): Pair { 16 | val mainName = "${name}Main" 17 | val testName = "${name}Test" 18 | val main = sourceSets.maybeCreate(mainName).apply { dependsOn(mainSourceSetTarget) } 19 | val test = sourceSets.maybeCreate(testName).apply { dependsOn(testSourceSetTarget) } 20 | targets.forEach { target -> 21 | target.compilations["main"].defaultSourceSet { dependsOn(main) } 22 | target.compilations["test"].defaultSourceSet { dependsOn(test) } 23 | } 24 | return main to test 25 | } 26 | 27 | fun KotlinMultiplatformExtension.targetGroup( 28 | name: String, 29 | mainSourceSetTarget: String, 30 | testSourceSetTarget: String, 31 | vararg targets: T 32 | ): Pair = targetGroup( 33 | name = name, 34 | mainSourceSetTarget = sourceSets.getByName(mainSourceSetTarget), 35 | testSourceSetTarget = sourceSets.getByName(testSourceSetTarget), 36 | targets = targets, 37 | ) 38 | 39 | fun NamedDomainObjectContainer.withName(name: String, action: Action) { 40 | matching { it.name == name }.all(action) 41 | } 42 | 43 | private fun NamedDomainObjectContainer.sharedSourceSets( 44 | vararg sourceSets: String, 45 | action: Action, 46 | ) { 47 | sourceSets.forEach { withName(it, action) } 48 | } 49 | 50 | fun NamedDomainObjectContainer.jvmCommonMain(action: Action) { 51 | sharedSourceSets("jvmCommonMain", "androidMain", action = action) 52 | } 53 | 54 | fun NamedDomainObjectContainer.jvmCommonTest(action: Action) { 55 | sharedSourceSets("jvmCommonTest", "androidUnitTest", action = action) 56 | } 57 | 58 | fun NamedDomainObjectContainer.blockingMain(action: Action) { 59 | sharedSourceSets("blockingMain", "androidMain", action = action) 60 | } 61 | 62 | fun NamedDomainObjectContainer.blockingTest(action: Action) { 63 | sharedSourceSets("blockingTest", "androidUnitTest", action = action) 64 | } 65 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.common") 3 | id("convention.publishing-nexus") 4 | if (System.getenv("CI") == null) id("convention.git-hooks") 5 | } 6 | 7 | gradleEnterprise { 8 | buildScan { 9 | termsOfServiceUrl = "https://gradle.com/terms-of-service" 10 | termsOfServiceAgree = "yes" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.common") 3 | } 4 | 5 | gradleEnterprise { 6 | buildScan { 7 | termsOfServiceUrl = "https://gradle.com/terms-of-service" 8 | termsOfServiceAgree = "yes" 9 | } 10 | } 11 | 12 | // TODO(KT-52172) Remove once KMP properly supports composite builds 13 | allprojects { 14 | if (findProperty("project.localRepo") == "true") { 15 | repositories { 16 | val local = maven("file://${rootProject.projectDir.parentFile}/build/localMaven") { 17 | name = "Local" 18 | } 19 | remove(local) 20 | add(0, local) 21 | } 22 | } 23 | configurations.all { 24 | resolutionStrategy { 25 | dependencySubstitution { 26 | val reduxVersion = "+" 27 | substitute(module("org.reduxkotlin:redux-kotlin")) 28 | .using(module("org.reduxkotlin:redux-kotlin:$reduxVersion")) 29 | substitute(module("org.reduxkotlin:redux-kotlin-threadsafe")) 30 | .using(module("org.reduxkotlin:redux-kotlin-threadsafe:$reduxVersion")) 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/counter/README.md: -------------------------------------------------------------------------------- 1 | # Counter Example 2 | 3 | This the most basic example of ReduxKotlin. It is setup as a multiplatform project. Android is implemented in the Android folder and common code is in the common folder. -------------------------------------------------------------------------------- /examples/counter/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | @file:Suppress("UnstableApiUsage") 2 | 3 | plugins { 4 | id("convention.control") 5 | id("com.android.application") 6 | kotlin("android") 7 | kotlin("kapt") 8 | } 9 | 10 | android { 11 | namespace = "org.reduxkotlin.example.counter" 12 | compileSdk = 33 13 | defaultConfig { 14 | applicationId = "org.reduxkotlin.example.todos" 15 | minSdk = 26 16 | targetSdk = 33 17 | versionCode = 1 18 | versionName = "1.0" 19 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 20 | } 21 | compileOptions { 22 | sourceCompatibility = JavaVersion.VERSION_1_8 23 | targetCompatibility = JavaVersion.VERSION_1_8 24 | } 25 | buildFeatures { 26 | viewBinding = true 27 | } 28 | packagingOptions { 29 | resources.excludes.add("META-INF/*.kotlin_module") 30 | } 31 | } 32 | 33 | dependencies { 34 | implementation("androidx.appcompat:appcompat:_") 35 | implementation(project(":counter:common")) 36 | implementation("org.reduxkotlin:redux-kotlin-threadsafe") 37 | } 38 | -------------------------------------------------------------------------------- /examples/counter/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/counter/android/src/main/java/org/reduxkotlin/example/counter/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package org.reduxkotlin.example.counter 2 | 3 | import android.os.Bundle 4 | import android.os.Handler 5 | import androidx.appcompat.app.AppCompatActivity 6 | import org.reduxkotlin.StoreSubscription 7 | import org.reduxkotlin.example.counter.databinding.ActivityMainBinding 8 | import org.reduxkotlin.examples.counter.Decrement 9 | import org.reduxkotlin.examples.counter.Increment 10 | import org.reduxkotlin.examples.counter.reducer 11 | import org.reduxkotlin.threadsafe.createThreadSafeStore 12 | 13 | /** 14 | * This is a sample of basic redux behavior. 15 | * This is NOT best practice for structuring a multiplatform App. 16 | */ 17 | 18 | val store = createThreadSafeStore(reducer, 0) 19 | 20 | class MainActivity : AppCompatActivity() { 21 | private lateinit var storeSubscription: StoreSubscription 22 | private lateinit var binding: ActivityMainBinding 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | binding = ActivityMainBinding.inflate(layoutInflater) 27 | setContentView(binding.root) 28 | storeSubscription = store.subscribe { render(store.state) } 29 | binding.btnIncrement.setOnClickListener { store.dispatch(Increment()) } 30 | binding.btnDecrement.setOnClickListener { store.dispatch(Decrement()) } 31 | binding.btnAsync.setOnClickListener { incrementAsync() } 32 | binding.btnIncrementIfOdd.setOnClickListener { incrementIfOdd() } 33 | } 34 | 35 | private fun render(state: Int) { 36 | binding.txtLabel.text = "Clicked: $state times" 37 | } 38 | 39 | private fun incrementIfOdd() { 40 | if (store.state % 2 != 0) { 41 | store.dispatch(Increment()) 42 | } 43 | } 44 | 45 | private fun incrementAsync() { 46 | Handler(mainLooper).postDelayed( 47 | { 48 | store.dispatch(Increment()) 49 | }, 50 | 1000 51 | ) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/counter/android/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /examples/counter/android/src/main/res/drawable/ic_arrow_downward_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /examples/counter/android/src/main/res/drawable/ic_arrow_upward_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /examples/counter/android/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 21 | 22 | 27 | 28 | 33 | 34 | 35 | 36 | 37 |