Skip to content

Commit

Permalink
Merge pull request #4 from zsmb13/kmp
Browse files Browse the repository at this point in the history
KMP
  • Loading branch information
zsmb13 authored May 18, 2024
2 parents 168bcd4 + c9079b4 commit 2bf49aa
Show file tree
Hide file tree
Showing 87 changed files with 3,410 additions and 329 deletions.
3 changes: 3 additions & 0 deletions .fleet/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"backend.maxHeapSizeMb": 4096
}
11 changes: 10 additions & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,14 @@ jobs:
java-version: 21
- name: Debug build
run: ./gradlew assembleDebug --stacktrace
- name: API check
run: ./gradlew apiCheck
- name: Unit test
run: ./gradlew testDebugUnitTest --stacktrace
run: ./gradlew allTests test --stacktrace
env:
IS_CI_TEST: yup
- name: Upload test results
uses: actions/upload-artifact@v4
with:
name: test-results
path: '*/build/reports/tests'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
*.iml
.gradle
.kotlin
/local.properties
/.idea
.DS_Store
Expand Down
114 changes: 77 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,92 @@
# requireKTX

**requireKTX** is a collection of small utility functions to make it easier to deal with some otherwise nullable APIs on Android, using the same idea as [`requireContext`](https://developer.android.com/reference/androidx/fragment/app/Fragment.html#requireContext()), [`requireArguments`](https://developer.android.com/reference/androidx/fragment/app/Fragment.html#requireArguments()), and other similar Android SDK methods.
**requireKTX** is a collection of small utility functions to make it easier to work with **values that should always exist** on Android and Kotlin Multiplatform, in the style of [`requireContext`](https://developer.android.com/reference/androidx/fragment/app/Fragment.html#requireContext()), [`requireArguments`](https://developer.android.com/reference/androidx/fragment/app/Fragment.html#requireArguments()), and other similar Android SDK methods.

Types that requireKTX provides extensions for:

- [Bundle](#bundle)
- [Intent](#intent)
- [NavBackStackEntry](#navbackstackentry)
- [WorkManager Data](#workmanager-data)
- [Bundle](#bundle) (Kotlin Multiplatform)
- [NavBackStackEntry](#navbackstackentry) (Kotlin Multiplatform)
- [Intent](#intent) (Android)
- [WorkManager Data](#workmanager-data) (Android)

## Why?

Take the example of grabbing a Fragment argument bundle and reading a String ID from it that should always be there: you have two choices, and none of them are great:
Take the example of grabbing a Bundle and reading a String ID from it that should always be there: the Bundle APIs give you a nullable result, which means you'll have to do some kind of null handling.

```kotlin
// Providing no default value
// Results in a nullable return type, caller has to do null handling
val id: String = requireArguments().getString("user_id")!!

// Providing a (meaningless) default value
// Results in a platform return type, caller has to explicitly type as non-null
val id: String = requireArguments().getString("user_id", "")
if (id == "") { ... } // ... and check to avoid accidentally using the default value
// Without requireKTX 😥
val id: String = argumentBundle.getString("user_id")!!
```

requireKTX provides methods such as `requireString` so that you can demand that a value be there, or otherwise get an exception:
The exception potentially thrown by this code also won't be too helpful in tracking down the problem, as it won't tell you details such as whether the value was missing, or if it was the wrong type for the request.

Another problem with Bundles is accessing primitive values, as they're always returned as non-nullable, defaulting to `0` (or even worse, `false` for Booleans) if the key is not found or its associated value has the wrong type:

```kotlin
val bundle = Bundle()
bundle.putDouble("count", 123.0)

// These both pass 😱
assertEquals(0, bundle.getInt("count"))
assertEquals(0, bundle.getInt("score"))
```

This makes it difficult to know if what you received was a real `0` value, or if something silently went wrong.

---

Instead of using these methods, requireKTX provides extensions such as `requireString`, which you can use to *require* a value that must always be there:

```kotlin
val id: String = requireArguments().requireString("user_id")
// With requireKTX 🥳
val id: String = argumentBundle.requireString("user_id")
val count: Int = argumentBundle.requireInt("count")
```

These methods in the library will throw meaningful exceptions based on the error that occurred - see the method docs for details.
These methods give you non-nullable return types. If the key isn't set or the value doesn't have the expected type, they throw meaningful exceptions based on the error that occurred. This is true for accessing primitive values as well.

### getOrNull

To make the nullable case more obvious and explicit, requireKTX **also includes `getOrNull` style methods for everything that it covers with `require` style methods**. These match the conventions of the Kotlin Standard Library, and can make it clearer that `null` is returned if a value for a key couldn't be fetched.
requireKTX **also includes `getOrNull` style methods for everything that it covers with `require` style methods**,to make the nullable case more obvious and explicit. These match the conventions of the Kotlin Standard Library, and can make it clearer that `null` is returned if a value for a key couldn't be fetched.

```kotlin
val userId: String? = requireArguments().getStringOrNull("user_id")
```
val count: Int? = requireArguments().getIntOrNull("count")
```

## Dependencies

requireKTX is available from MavenCentral.
requireKTX is published on [Maven Central](https://repo1.maven.org/maven2/co/zsmb/).

```groovy
```kotlin
repositories {
mavenCentral()
}
```

It's available in several artifacts which you can import depending on which types you want to get extensions for - see the module descriptions below for more info:
There are several artifacts you can import depending on which types you want to get extensions for - see the module descriptions below to learn more.

```groovy
```kotlin
dependencies {
implementation "co.zsmb:requirektx-bundle:1.2.0"
implementation "co.zsmb:requirektx-intent:1.2.0"
implementation "co.zsmb:requirektx-navigation:1.2.0"
implementation "co.zsmb:requirektx-work:1.2.0"
// commonMain or Android
implementation("co.zsmb:requirektx-bundle:2.0.0-alpha01")
implementation("co.zsmb:requirektx-navigation:2.0.0-alpha01")

// Android only
implementation("co.zsmb:requirektx-intent:2.0.0-alpha01")
implementation("co.zsmb:requirektx-work:2.0.0-alpha01")
}
```

## Available modules and extensions

### Bundle

*The `requirektx-bundle` artifact works with the `androidx.core.bundle.Bundle` type, available on Android and other Kotlin Multiplatform targets from `org.jetbrains.androidx.core:core-bundle`.*

Given a `Bundle`, you can require the following types of values:

```
```kotlin
// Primitives (examples)
bundle.requireBoolean()
bundle.requireByte()
Expand All @@ -92,9 +111,36 @@ bundle.requireFloatArray()

... and many more!

### NavBackStackEntry

*The `requirektx-navigation` artifact works with the `androidx.navigation.NavBackStackEntry` type, available on Android and other Kotlin Multiplatform targets from `org.jetbrains.androidx.navigation:navigation-runtime`.*

*This is compatible with both the [Jetpack Navigation component on Android](https://developer.android.com/guide/navigation) (with or without Compose) and the [Compose Multiplatform navigation library](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-navigation-routing.html).*

To get the bundle of arguments from an entry, use `requireArguments`:

```kotlin
val args: Bundle = navBackStackEntry.requireArguments()
```

Here's an example of using this with Compose Navigation, in combination with the [Bundle extensions](#bundle):

```kotlin
composable(
"detail/{objectId}",
arguments = listOf(navArgument("objectId") { type = NavType.IntType }),
) { backStackEntry ->
val args = backStackEntry.requireArguments()
val objectId = args.requireInt("objectId")
// UI implementation
}
```

### Intent

Given a `Intent`, you can require its extras `Bundle` (and then require values from it as seen above):
*The `requirektx-intent` artifact works with the [`android.content.Intent`](https://developer.android.com/reference/android/content/Intent) type, available on Android only.*

Given an `Intent`, you can require its extras `Bundle` (and then require values from it as seen above):

```kotlin
val extras: Bundle = intent.requireExtras()
Expand Down Expand Up @@ -127,16 +173,10 @@ intent.requireFloatArrayExtra()

... and many more!

### NavBackStackEntry

The navigation module provides an extension to require the arguments of a `NavBackStackEntry`, same as you could do for a `Fragment`:

```kotlin
val args: Bundle = navBackStackEntry.requireArguments()
```

### WorkManager Data

*The `requirektx-work` artifact provides extensions for the [`androidx.work.Data`](https://developer.android.com/reference/androidx/work/Data) type, available on Android only.*

Given a WorkManager `Data` object (such as `inputData` inside a worker), you can require the following types of values:

```kotlin
Expand Down
11 changes: 0 additions & 11 deletions app/src/androidMain/kotlin/co/zsmb/requirektx/MainActivity.kt

This file was deleted.

18 changes: 0 additions & 18 deletions app/src/androidMain/res/layout/activity_main.xml

This file was deleted.

16 changes: 0 additions & 16 deletions app/src/androidMain/res/values-night/themes.xml

This file was deleted.

10 changes: 0 additions & 10 deletions app/src/androidMain/res/values/colors.xml

This file was deleted.

3 changes: 0 additions & 3 deletions app/src/androidMain/res/values/strings.xml

This file was deleted.

16 changes: 0 additions & 16 deletions app/src/androidMain/res/values/themes.xml

This file was deleted.

8 changes: 6 additions & 2 deletions build-logic/convention/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ dependencies {

gradlePlugin {
plugins {
register("androidLibrary") {
register("library") {
id = "co.zsmb.requirektx.library"
implementationClass = "AndroidLibraryConventionPlugin"
implementationClass = "KmpLibraryConventionPlugin"
}
register("publishing") {
id = "co.zsmb.requirektx.publishing"
implementationClass = "PublishingConventionPlugin"
}
}
}
Loading

0 comments on commit 2bf49aa

Please sign in to comment.