https://github.com/mockative/kontinuity
https://github.com/mockative/kontinuity
Last synced: about 2 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/mockative/kontinuity
- Owner: mockative
- License: mit
- Created: 2022-02-17T12:01:49.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2024-11-04T21:21:59.000Z (7 months ago)
- Last Synced: 2025-04-05T22:58:56.346Z (about 2 months ago)
- Language: Kotlin
- Size: 367 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Kontinuity
[ksp]: https://github.com/google/ksp
[](https://github.com/mockative/Kontinuity/actions/workflows/build.yml)
[](https://search.maven.org/artifact/io.mockative/kontinuity-processor)Effortless use of Kotlin Multiplatform coroutines in Swift, including `suspend` functions and
`Flow` returning members.## Installation
Kontinuity includes both a core Kotlin library, a [Kotlin Symbol Processor][KSP], and various Swift
Package Manager packages, depending on how you want to consume the generated code. Please make sure
to use the same versions of each individual part.### Kotlin (Gradle)
In your Kotlin Multiplatform module, add the following to your __build.gradle.kts__ file:
```kotlin
plugins {
id("com.google.devtools.ksp")
}kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("io.mockative:kontinuity-core:2.0.1")
}
}
}
}dependencies {
add("kspIos", "io.mockative:kontinuity-processor:2.0.1")
}
```### Swift (Swift Package Manager)
The Swift libraries are available using Swift Package Manager (SPM), by adding the following to
your __Package.swift__ file, or in your Xcode Project's Package Dependencies.```swift
dependencies: [
.package(url: "https://github.com/mockative/Kontinuity.git", from: "")
]
```## Usage
For each Kotlin class or interface annotated with `@Kontinuity`, a wrapper class is generated,
referred to as the "Kontinuity Wrapper".Given a Kotlin interface like the following:
```kotlin
package com.app.sample.tasks@Kontinuity
interface TaskService {
val tasks: StateFlow>suspend fun createTask(task: Task)
fun close()
}
```A Kontinuity Wrapper will be generated:
```kotlin
package com.app.sample.tasks// The Kontinuity Wrapper class takes the prefix 'K' by default
open class KTaskService(private val wrapped: TaskService) {
// Coroutine members have their names transformed with the 'K' suffix by default
val tasksK: KontinuityStateFlow
get() = wrapped.toKontinuityStateFlow()fun refreshK(): KontinuitySuspend =
kontinuitySuspend { wrapped.refresh() }// Simple members don't require a name transformation
fun close() =
wrapped.close()
}
```### Swift
The generated Kontinuity Wrapper can be used in Swift through `KontinuityCore` and `KontinuityCombine`.
```swift
import KontinuityCore
import KontinuityCombineval kotlinTaskService: TaskService
val taskService = KTaskService(wrapped: kotlinTaskService)// Accessing the current value of a StateFlow
val tasks = getValue(of: taskService.tasksK)// Subscribing to a Flow
val subscription = createPublisher(for: taskService.tasksK)
.sink { completion in } receiveValue: { tasks in
print("tasks: \(tasks)")
}// Calling a suspend function
val subscription = createFuture(for: taskService.refreshK())
.sink { completion in
print("refresh: \(completion)")
} receiveValue: { unit in }
```### Name Transformations
Kontinuity Wrapper classes have their names transformed from the type they're wrapping, by prefixing
the class with `K`. Coroutine members of Kontinuity Wrapper classes also have their names
transformed, in order to prevent the Kotlin compiler from suffixing it with `_` when compiling for
iOS/Darwin, which it does to prevent member signature clashes when interfaces are used.See [Configuration - Name Transformations](CONFIGURATION.md#name-transformations) for more
information in how to configure Kontinuity.### Coroutine Scope
By default, Kontinuity launches all coroutines in a scope using `Dispatchers.Main.immediate`. You
can override this behaviour by annotating a top-level property with `@KontinuityScope`. This
annotation can both be applied to the entire source set (by specifying `default = true`), or to the
`@Kontinuity` annotated types within a single source file:```kotlin
// Specifies the coroutine scope used to launch Kontinuity coroutines of types within this source
// set(s), unless otherwise overwritten by a file-level @KontinuityScope.
@SharedImmutable
@KontinuityScope(default = true)
internal val defaultKontinuityScope = CoroutineScope(Dispatchers.Default + SuperviserJob())
``````kotlin
// Specifies the coroutine scope used to launch Kontinuity coroutines of types within this file.
@SharedImmutable
@KontinuityScope
internal val taskServiceScope = CoroutineScope(Dispatchers.Unconfined + SuperviserJob())interface TaskService {
// Coroutines launches within this type (and other types in this file) are launched in the
// `taskServiceScope`.
}
```## Roadmap
- [ ] Add Swift mock library and generator
- [X] Add global `@KontinuityScope` annotation to control the default scope through a
`@SharedImmutable` global variable.
- [X] Add type-local `@KontinuityScope` annotation to control the default scope on a per-type basis.
- [X] ~~Consider rewriting `SharedFlow` wrapper generation to generating 2 properties, one for the
flow, one for the value `%MValue`.~~
- Implementing this breaks usage of `suspend` functions returning `StateFlow`.## Credits
[KMP-NativeCoroutines]: https://github.com/rickclephas/KMP-NativeCoroutines
Kontinuity is heavily inspired by [rickclephas/KMP-NativeCoroutines][KMP-NativeCoroutines], and is
essentially a Kotlin Symbol Processor version of that Kotlin Compiler Plugin, born out of a desire
to have similar features while maintaining compatibility with KSP.