commonMain.co.touchlab.skie.plugin.configuration.SkieFeatureConfiguration.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-plugin-api-jvm Show documentation
Show all versions of gradle-plugin-api-jvm Show documentation
Public API for SKIE Gradle plugin.
The newest version!
@file:Suppress("MemberVisibilityCanBePrivate")
package co.touchlab.skie.plugin.configuration
import co.touchlab.skie.configuration.ConfigurationKey
import co.touchlab.skie.configuration.SkieConfigurationFlag
import co.touchlab.skie.plugin.util.takeIf
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import javax.inject.Inject
abstract class SkieFeatureConfiguration @Inject constructor(objects: ObjectFactory) {
val coroutinesInterop: Property = objects.property(Boolean::class.java).convention(true)
/**
* For performance reasons SKIE does not generate default arguments for functions in external libraries even if enabled via the group configuration.
* This behavior can be overridden by setting this property to true.
*
* Warning: Depending on the project this action can have a significant impact the compilation time because it turns off the Kotlin compiler caching.
*
* Note that even with this property turned on, it is still required to enable default arguments for individual functions from those libraries.
* This can be done only via the group configuration from Gradle.
* To opt in for some functions, use `group("$declarationFqNamePrefix") { DefaultArgumentInterop.Enabled(true) }`.
* To opt in for all functions globally, use `group { DefaultArgumentInterop.Enabled(true) }`.
*/
val defaultArgumentsInExternalLibraries: Property = objects.property(Boolean::class.java).convention(false)
/**
* SKIE has a **preview** support for converting `suspend` functions into `Combine.Future` types.
* When enabled,
* SKIE will extend `Combine.Future` with an initializer accepting any `async` lambda.
*
* With it, you can easily convert any `suspend` function into `Combine.Future`.
* Let's take a look at an example usage. We'll declare a suspending global function `helloWorld`.
*
* ```kotlin
* suspend fun helloWorld(): String {
* return "Hello World!"
* }
* ```
*
* Then in Swift you can use the new `Future` initializer like so:
*
* ```swift
* let future = Future(async: helloWorld)
*
* future.sink { error in
* // Handle an error throw by the function.
* // Note that this could also be `CancellationError`.
* } receiveValue: { value in
* // Value is a `String` "Hello World!"
* print(value)
* }
* ```
*
* NOTE: It's important to understand how Combine and its operators work.
* The example above doesn't store the cancellable returned by `sink`,
* so it would immediately cancel and `receiveValue` wouldn't get called.
* Additionally, Futures are hot and will invoke the provided `async` immediately.
* This means Futures don't wait for calling `.sink` on them.
* Futures also don't support cancellation.
*/
val enableFutureCombineExtensionPreview: Property = objects.property(Boolean::class.java).convention(false)
/**
* SKIE has a **preview** support for converting Flows into `Combine.Publisher` types.
* When enabled,
* each Flow bridged to Swift by SKIE will contain a `toPublisher()` method.
*
* Use this if you need to interface with *Combine* code and can't use Swift's native `AsyncSequence`.
* Let's take a look at an example usage. We'll declare a global function `helloWorld` returning a `Flow`.
*
* ```kotlin
* fun helloWorld(): Flow {
* return flow {
* emit("Hello")
* delay(1.seconds)
* emit("World")
* delay(1.seconds)
* emit("!")
* }
* }
* ```
*
* Then in Swift you can use the `toPublisher()` function.
*
* ```swift
* let publisher = helloWorld().toPublisher()
*
* publisher.sink { value in
* // Value is a `String` type
* // Receives with a 1-second delay between them:
* // - "Hello"
* // - "World"
* // - "!"
* }
* ```
*
* NOTE: It's important to understand how Combine and its operators work.
* The example above doesn't store the cancellable returned by `sink`,
* so it would immediately cancel and `receiveValue` wouldn't get called.
*/
val enableFlowCombineConvertorPreview: Property = objects.property(Boolean::class.java).convention(false)
/**
* SKIE has a **preview** support for observing Flows in SwiftUI views.
* When enabled,
* SKIE will include two APIs for observing Flows in SwiftUI.
* Each has its own uses, and we'd like your feedback on both.
*
* The first one is a view modifier `collect`,
* which you can use to collect a flow inside a view.
* Either into a `@State` property directly using a SwiftUI `Binding`,
* or through a provided `async` closure.
*
* The other one is a SwiftUI view `Observing`,
* which you can use to collect one or multiple flows and display a view based on the latest values.
* This is similar to the builtin SwiftUI view `ForEach`.
* Where `ForEach` takes a synchronous sequence (e.g. an array),
* `Observing` takes an asynchronous sequence (e.g. a Flow, a SharedFlow, or a StateFlow).
*
* Let's consider the following Kotlin view model which we'll want to interact with from SwiftUI.
*
* ```kotlin
* class SharedViewModel {
* val counter = flow {
* var counter = 0
* while (true) {
* emit(counter++)
* delay(1.seconds)
* }
* }
*
* val toggle = flow {
* var toggle = false
* while (true) {
* emit(toggle)
* toggle = !toggle
* delay(1.seconds)
* }
* }
* }
* ```
*
* Then we can use the SKIE-included helpers to interact with the `SharedViewModel` from SwiftUI.
* In the sample below, we'll use all of them as an example, not a real-world scenario.
*
* ```swift
* struct ExampleView: View {
* let viewModel = SharedViewModel()
*
* @State
* var boundCounter: KotlinInt = 0
*
* @State
* var manuallyUpdatedCounter: Int = 0
*
* var body: some View {
* Text("Bound counter using Binding: \(boundCounter)")
* .collect(flow: viewModel.counter, into: $counter)
*
* Text("Manually updated counter: \(manuallyUpdatedCounter)")
* .collect(flow: viewModel.counter) { latestValue in
* manuallyUpdatedCounter = latestValue.intValue
* }
*
* Observing(viewModel.counter, viewModel.toggle) {
* ProgressView("Waiting for counters to flows to produce a first value")
* } content: { counter, toggle in
* Text("Counter: \(counter), Toggle: \(toggle)")
* }
*
* Observing(viewModel.counter.withInitialValue(0), viewModel.toggle.withInitialValue(false)) { counter, toggle in
* Text("Counter: \(counter), Toggle: \(toggle)")
* }
* }
* }
* ```
*/
val enableSwiftUIObservingPreview: Property = objects.property(Boolean::class.java).convention(false)
internal val groupConfigurations = mutableListOf()
fun group(targetFqNamePrefix: String = "", overridesAnnotations: Boolean = false, action: GroupConfiguration.() -> Unit) {
val groupConfiguration = GroupConfiguration(targetFqNamePrefix, overridesAnnotations)
groupConfigurations.add(groupConfiguration)
groupConfiguration.action()
}
class GroupConfiguration(
internal val targetFqNamePrefix: String,
internal val overridesAnnotations: Boolean,
) {
internal val items = mutableMapOf()
operator fun ConfigurationKey.invoke(value: T) {
items[this.name] = this.serialize(value)
}
}
internal fun buildConfigurationFlags(): Set =
setOfNotNull(
SkieConfigurationFlag.Feature_CoroutinesInterop takeIf coroutinesInterop,
SkieConfigurationFlag.Feature_DefaultArgumentsInExternalLibraries takeIf defaultArgumentsInExternalLibraries,
SkieConfigurationFlag.Feature_FlowCombineConvertor takeIf enableFlowCombineConvertorPreview,
SkieConfigurationFlag.Feature_FutureCombineExtension takeIf enableFutureCombineExtensionPreview,
SkieConfigurationFlag.Feature_SwiftUIObserving takeIf enableSwiftUIObservingPreview,
)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy