{"id":13407092,"url":"https://github.com/rickclephas/KMP-NativeCoroutines","last_synced_at":"2025-03-14T11:31:04.356Z","repository":{"id":37631009,"uuid":"374341212","full_name":"rickclephas/KMP-NativeCoroutines","owner":"rickclephas","description":"Library to use Kotlin Coroutines from Swift code in KMP apps","archived":false,"fork":false,"pushed_at":"2024-10-21T18:36:27.000Z","size":2098,"stargazers_count":1058,"open_issues_count":17,"forks_count":31,"subscribers_count":16,"default_branch":"master","last_synced_at":"2024-10-29T15:46:00.931Z","etag":null,"topics":["combine","ios","kmm","kmp","kotlin","kotlin-coroutines","kotlin-multiplatform","kotlin-multiplatform-mobile","macos","swift"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rickclephas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-06-06T11:20:30.000Z","updated_at":"2024-10-28T02:02:43.000Z","dependencies_parsed_at":"2024-03-23T11:32:40.676Z","dependency_job_id":null,"html_url":"https://github.com/rickclephas/KMP-NativeCoroutines","commit_stats":{"total_commits":745,"total_committers":3,"mean_commits":"248.33333333333334","dds":"0.025503355704697972","last_synced_commit":"4dfc6fa69524dec09aa34a4a410f3fac86217672"},"previous_names":[],"tags_count":386,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rickclephas%2FKMP-NativeCoroutines","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rickclephas%2FKMP-NativeCoroutines/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rickclephas%2FKMP-NativeCoroutines/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rickclephas%2FKMP-NativeCoroutines/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rickclephas","download_url":"https://codeload.github.com/rickclephas/KMP-NativeCoroutines/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243569323,"owners_count":20312402,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["combine","ios","kmm","kmp","kotlin","kotlin-coroutines","kotlin-multiplatform","kotlin-multiplatform-mobile","macos","swift"],"created_at":"2024-07-30T20:00:19.605Z","updated_at":"2025-03-14T11:31:04.348Z","avatar_url":"https://github.com/rickclephas.png","language":"Kotlin","readme":"# KMP-NativeCoroutines\n\nA library to use Kotlin Coroutines from Swift code in KMP apps.\n\n## Why this library?\n\nBoth KMP and Kotlin Coroutines are amazing, but together they have some limitations.\n\nThe most important limitation is cancellation support.  \nKotlin suspend functions are exposed to Swift as functions with a completion handler.  \nThis allows you to easily use them from your Swift code, but it doesn't support cancellation.\n\n\u003e [!NOTE]\n\u003e While Swift 5.5 brings async functions to Swift, it doesn't solve this issue.  \n\u003e For interoperability with ObjC all functions with a completion handler can be called like an async function.  \n\u003e This means starting with Swift 5.5 your Kotlin suspend functions will look like Swift async functions.  \n\u003e But that's just syntactic sugar, so there's still no cancellation support.\n\nBesides cancellation support, ObjC doesn't support generics on protocols.  \nSo all the `Flow` interfaces lose their generic value type which make them hard to use.\n\nThis library solves both of these limitations 😄.\n\n## Compatibility\n\nThe latest version of the library uses Kotlin version `2.1.10`.  \nCompatibility versions for older and/or preview Kotlin versions are also available:\n\n| Version        | Version suffix       |    Kotlin    |    KSP     | Coroutines |\n|----------------|----------------------|:------------:|:----------:|:----------:|\n| _latest_       | -kotlin-2.1.20-RC2   |  2.1.20-RC2  |   1.0.31   |   1.10.1   |\n| **_latest_**   | **_no suffix_**      |  **2.1.10**  | **1.0.31** | **1.10.1** |\n| 1.0.0-ALPHA-38 | _no suffix_          |    2.1.0     |   1.0.29   |   1.9.0    |\n| 1.0.0-ALPHA-37 | _no suffix_          |    2.0.21    |   1.0.25   |   1.9.0    |\n| 1.0.0-ALPHA-36 | _no suffix_          |    2.0.20    |   1.0.25   |   1.9.0    |\n| 1.0.0-ALPHA-35 | _no suffix_          |    2.0.20    |   1.0.24   |   1.8.1    |\n| 1.0.0-ALPHA-34 | _no suffix_          |    2.0.10    |   1.0.24   |   1.8.1    |\n| 1.0.0-ALPHA-33 | _no suffix_          |    2.0.0     |   1.0.24   |   1.8.1    |\n| 1.0.0-ALPHA-30 | _no suffix_          |    1.9.24    |   1.0.20   |   1.8.1    |\n| 1.0.0-ALPHA-28 | _no suffix_          |    1.9.23    |   1.0.20   |   1.8.0    |\n| 1.0.0-ALPHA-25 | _no suffix_          |    1.9.22    |   1.0.17   |   1.8.0    |\n| 1.0.0-ALPHA-23 | _no suffix_          |    1.9.21    |   1.0.16   |   1.7.3    |\n| 1.0.0-ALPHA-21 | _no suffix_          |    1.9.20    |   1.0.14   |   1.7.3    |\n| 1.0.0-ALPHA-18 | _no suffix_          |    1.9.10    |   1.0.13   |   1.7.3    |\n| 1.0.0-ALPHA-17 | _no suffix_          |    1.9.0     |   1.0.12   |   1.7.3    |\n| 1.0.0-ALPHA-12 | _no suffix_          |    1.8.22    |   1.0.11   |   1.7.2    |\n| 1.0.0-ALPHA-10 | _no suffix_          |    1.8.21    |   1.0.11   |   1.7.1    |\n| 1.0.0-ALPHA-7  | _no suffix_          |    1.8.20    |   1.0.10   |   1.6.4    |\n\nYou can choose from a couple of Swift implementations.  \nDepending on the implementation you can support as low as iOS 9, macOS 10.9, tvOS 9 and watchOS 3:\n\n| Implementation | Swift | iOS  | macOS | tvOS | watchOS |\n|----------------|:-----:|:----:|:-----:|:----:|:-------:|\n| Async          |  5.5  | 13.0 | 10.15 | 13.0 |   6.0   |\n| Combine        |  5.0  | 13.0 | 10.15 | 13.0 |   6.0   |\n| RxSwift        |  5.0  | 9.0  | 10.9  | 9.0  |   3.0   |\n\n## Installation\n\nThe library consists of a Kotlin and Swift part which you'll need to add to your project.  \nThe Kotlin part is available on Maven Central and the Swift part can be installed via CocoaPods \nor the Swift Package Manager.\n\nMake sure to always use the same versions for all the libraries!\n\n### Kotlin\n\nFor Kotlin just add the plugin to your `build.gradle.kts`:\n```kotlin\nplugins {\n    id(\"com.google.devtools.ksp\") version \"2.1.10-1.0.29\"\n    id(\"com.rickclephas.kmp.nativecoroutines\") version \"1.0.0-ALPHA-39\"\n}\n```\nand make sure to opt in to the experimental `@ObjCName` annotation:\n```kotlin\nkotlin.sourceSets.all {\n    languageSettings.optIn(\"kotlin.experimental.ExperimentalObjCName\")\n}\n```\n\n### Swift (Swift Package Manager)\n\nThe Swift implementations are available via the Swift Package Manager.  \nJust add it to your `Package.swift` file:\n```swift\ndependencies: [\n    .package(url: \"https://github.com/rickclephas/KMP-NativeCoroutines.git\", exact: \"1.0.0-ALPHA-39\")\n],\ntargets: [\n    .target(\n        name: \"MyTargetName\",\n        dependencies: [\n            // Swift Concurrency implementation\n            .product(name: \"KMPNativeCoroutinesAsync\", package: \"KMP-NativeCoroutines\"),\n            // Combine implementation\n            .product(name: \"KMPNativeCoroutinesCombine\", package: \"KMP-NativeCoroutines\"),\n            // RxSwift implementation\n            .product(name: \"KMPNativeCoroutinesRxSwift\", package: \"KMP-NativeCoroutines\")\n        ]\n    )\n]\n```\n\nOr add it in Xcode by going to `File` \u003e `Add Packages...` and providing the URL:\n`https://github.com/rickclephas/KMP-NativeCoroutines.git`.\n\n\u003e [!NOTE]\n\u003e The version for the Swift package should not contain the Kotlin version suffix\n\u003e (e.g. `-new-mm` or `-kotlin-1.6.0`).\n\n\u003e [!NOTE]\n\u003e If you only need a single implementation you can also use the SPM specific versions with suffixes\n\u003e `-spm-async`, `-spm-combine` and `-spm-rxswift`.\n\n### Swift (CocoaPods)\n\nIf you use CocoaPods add one or more of the following libraries to your `Podfile`:\n```ruby\npod 'KMPNativeCoroutinesAsync', '1.0.0-ALPHA-39'    # Swift Concurrency implementation\npod 'KMPNativeCoroutinesCombine', '1.0.0-ALPHA-39'  # Combine implementation\npod 'KMPNativeCoroutinesRxSwift', '1.0.0-ALPHA-39'  # RxSwift implementation\n```\n\u003e [!NOTE]\n\u003e The version for CocoaPods should not contain the Kotlin version suffix (e.g. `-new-mm` or `-kotlin-1.6.0`).\n\n### IntelliJ / Android Studio\n\nInstall the [IDE plugin](https://plugins.jetbrains.com/plugin/22481) from the JetBrains Marketplace to get:\n* Annotation usage validation\n* Exposed coroutines warnings\n* Quick fixes to add annotations\n\n## Usage\n\nUsing your Kotlin Coroutines code from Swift is almost as easy as calling the Kotlin code.   \nJust use the wrapper functions in Swift to get async functions, AsyncStreams, Publishers or Observables.\n\n### Kotlin\n\nThe plugin will automagically generate the necessary code for you! 🔮  \nJust annotate your coroutines declarations with `@NativeCoroutines` (or `@NativeCoroutinesState`).\n\n#### Flows\n\nYour `Flow` properties/functions get a native version:\n```kotlin\nimport com.rickclephas.kmp.nativecoroutines.NativeCoroutines\n\nclass Clock {\n    // Somewhere in your Kotlin code you define a Flow property\n    // and annotate it with @NativeCoroutines\n    @NativeCoroutines\n    val time: StateFlow\u003cLong\u003e // This can be any kind of Flow\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eGenerated code\u003c/summary\u003e\n\u003cp\u003e\n\nThe plugin will generate this native property for you:\n```kotlin\nimport com.rickclephas.kmp.nativecoroutines.asNativeFlow\nimport kotlin.native.ObjCName\n\n@ObjCName(name = \"time\")\nval Clock.timeNative\n    get() = time.asNativeFlow()\n```\n\nFor the `StateFlow` defined above the plugin will also generate this value property:\n```kotlin\nval Clock.timeValue\n    get() = time.value\n```\n\nIn case of a `SharedFlow` the plugin would generate a replay cache property:\n```kotlin\nval Clock.timeReplayCache\n    get() = time.replayCache\n```\n\u003c/p\u003e\n\u003c/details\u003e\n\n#### StateFlows\n\nUsing `StateFlow` properties to track state (like in a view model)?  \nUse the `@NativeCoroutinesState` annotation instead:\n```kotlin\nimport com.rickclephas.kmp.nativecoroutines.NativeCoroutinesState\n\nclass Clock {\n    // Somewhere in your Kotlin code you define a StateFlow property\n    // and annotate it with @NativeCoroutinesState\n    @NativeCoroutinesState\n    val time: StateFlow\u003cLong\u003e // This must be a StateFlow\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eGenerated code\u003c/summary\u003e\n\u003cp\u003e\n\nThe plugin will generate these native properties for you:\n```kotlin\nimport com.rickclephas.kmp.nativecoroutines.asNativeFlow\nimport kotlin.native.ObjCName\n\n@ObjCName(name = \"time\")\nval Clock.timeValue\n    get() = time.value\n\nval Clock.timeFlow\n    get() = time.asNativeFlow()\n```\n\u003c/p\u003e\n\u003c/details\u003e\n\n#### Suspend functions\n\nThe plugin also generates native versions for your annotated suspend functions:\n```kotlin\nimport com.rickclephas.kmp.nativecoroutines.NativeCoroutines\n\nclass RandomLettersGenerator {\n    // Somewhere in your Kotlin code you define a suspend function\n    // and annotate it with @NativeCoroutines\n    @NativeCoroutines\n    suspend fun getRandomLetters(): String { \n        // Code to generate some random letters\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eGenerated code\u003c/summary\u003e\n\u003cp\u003e\n\nThe plugin will generate this native function for you:\n```kotlin\nimport com.rickclephas.kmp.nativecoroutines.nativeSuspend\nimport kotlin.native.ObjCName\n\n@ObjCName(name = \"getRandomLetters\")\nfun RandomLettersGenerator.getRandomLettersNative() =\n    nativeSuspend { getRandomLetters() }\n```\n\u003c/p\u003e\n\u003c/details\u003e\n\n#### Interface limitations\n\nUnfortunately extension functions/properties aren't \n[supported](https://kotlinlang.org/docs/native-objc-interop.html#extensions-and-category-members)\non Objective-C protocols.  \n\nHowever this limitation can be \"overcome\" with some Swift magic.  \nAssuming `RandomLettersGenerator` is an `interface` instead of a `class` we can do the following:\n```swift\nimport KMPNativeCoroutinesCore\n\nextension RandomLettersGenerator {\n    func getRandomLetters() -\u003e NativeSuspend\u003cString, Error, KotlinUnit\u003e {\n        RandomLettersGeneratorNativeKt.getRandomLetters(self)\n    }\n}\n```\n\n#### Exposed coroutines checks\n\nWhen suspend functions and/or `Flow` declarations are exposed to ObjC/Swift,\nthe compiler and IDE plugin will produce a warning, reminding you to add one of the KMP-NativeCoroutines annotations.\n\nYou can customise the severity of these checks in your `build.gradle.kts` file:\n```kotlin\nnativeCoroutines {\n    exposedSeverity = ExposedSeverity.ERROR\n}\n```\n\nOr, if you are not interested in these checks, disable them:\n```kotlin\nnativeCoroutines {\n    exposedSeverity = ExposedSeverity.NONE\n}\n```\n\n### Swift Concurrency\n\nThe Async implementation provides some functions to get async Swift functions and `AsyncSequence`s.\n\n#### Async functions\n\nUse the `asyncFunction(for:)` function to get an async function that can be awaited:\n```swift\nimport KMPNativeCoroutinesAsync\n\nlet handle = Task {\n    do {\n        let letters = try await asyncFunction(for: randomLettersGenerator.getRandomLetters())\n        print(\"Got random letters: \\(letters)\")\n    } catch {\n        print(\"Failed with error: \\(error)\")\n    }\n}\n\n// To cancel the suspend function just cancel the async task\nhandle.cancel()\n```\n\nor if you don't like these do-catches you can use the `asyncResult(for:)` function:\n```swift\nimport KMPNativeCoroutinesAsync\n\nlet result = await asyncResult(for: randomLettersGenerator.getRandomLetters())\nif case let .success(letters) = result {\n    print(\"Got random letters: \\(letters)\")\n}\n```\n\nfor `Unit` returning functions there is also the `asyncError(for:)` function:\n```swift\nimport KMPNativeCoroutinesAsync\n\nif let error = await asyncError(for: integrationTests.returnUnit()) {\n    print(\"Failed with error: \\(error)\")\n}\n```\n\n#### AsyncSequence\n\nFor `Flow`s there is the `asyncSequence(for:)` function to get an `AsyncSequence`:\n```swift\nimport KMPNativeCoroutinesAsync\n\nlet handle = Task {\n    do {\n        let sequence = asyncSequence(for: randomLettersGenerator.getRandomLettersFlow())\n        for try await letters in sequence {\n            print(\"Got random letters: \\(letters)\")\n        }\n    } catch {\n        print(\"Failed with error: \\(error)\")\n    }\n}\n\n// To cancel the flow (collection) just cancel the async task\nhandle.cancel()\n```\n\n### Combine\n\nThe Combine implementation provides a couple functions to get an `AnyPublisher` for your Coroutines code.\n\n\u003e [!NOTE]\n\u003e These functions create deferred `AnyPublisher`s.  \n\u003e Meaning every subscription will trigger the collection of the `Flow` or execution of the suspend function.\n\n\u003e [!NOTE]\n\u003e You **must** keep a reference to the returned `Cancellable`s otherwise the collection will be cancelled immediately. \n\n#### Publisher\n\nFor your `Flow`s use the `createPublisher(for:)` function:\n```swift\nimport KMPNativeCoroutinesCombine\n\n// Create an AnyPublisher for your flow\nlet publisher = createPublisher(for: clock.time)\n\n// Now use this publisher as you would any other\nlet cancellable = publisher.sink { completion in\n    print(\"Received completion: \\(completion)\")\n} receiveValue: { value in\n    print(\"Received value: \\(value)\")\n}\n\n// To cancel the flow (collection) just cancel the publisher\ncancellable.cancel()\n```\n\nYou can also use the `createPublisher(for:)` function for suspend functions that return a `Flow`:\n```swift\nlet publisher = createPublisher(for: randomLettersGenerator.getRandomLettersFlow())\n```\n\n#### Future\n\nFor the suspend functions you should use the `createFuture(for:)` function:\n```swift\nimport KMPNativeCoroutinesCombine\n\n// Create a Future/AnyPublisher for the suspend function\nlet future = createFuture(for: randomLettersGenerator.getRandomLetters())\n\n// Now use this future as you would any other\nlet cancellable = future.sink { completion in\n    print(\"Received completion: \\(completion)\")\n} receiveValue: { value in\n    print(\"Received value: \\(value)\")\n}\n\n// To cancel the suspend function just cancel the future\ncancellable.cancel()\n```\n\n### RxSwift\n\nThe RxSwift implementation provides a couple functions to get an `Observable` or `Single` for your Coroutines code.\n\n\u003e [!NOTE]\n\u003e These functions create deferred `Observable`s and `Single`s.  \n\u003e Meaning every subscription will trigger the collection of the `Flow` or execution of the suspend function.\n\n#### Observable\n\nFor your `Flow`s use the `createObservable(for:)` function:\n```swift\nimport KMPNativeCoroutinesRxSwift\n\n// Create an observable for your flow\nlet observable = createObservable(for: clock.time)\n\n// Now use this observable as you would any other\nlet disposable = observable.subscribe(onNext: { value in\n    print(\"Received value: \\(value)\")\n}, onError: { error in\n    print(\"Received error: \\(error)\")\n}, onCompleted: {\n    print(\"Observable completed\")\n}, onDisposed: {\n    print(\"Observable disposed\")\n})\n\n// To cancel the flow (collection) just dispose the subscription\ndisposable.dispose()\n```\n\nYou can also use the `createObservable(for:)` function for suspend functions that return a `Flow`:\n```swift\nlet observable = createObservable(for: randomLettersGenerator.getRandomLettersFlow())\n```\n\n#### Single\n\nFor the suspend functions you should use the `createSingle(for:)` function:\n```swift\nimport KMPNativeCoroutinesRxSwift\n\n// Create a single for the suspend function\nlet single = createSingle(for: randomLettersGenerator.getRandomLetters())\n\n// Now use this single as you would any other\nlet disposable = single.subscribe(onSuccess: { value in\n    print(\"Received value: \\(value)\")\n}, onFailure: { error in\n    print(\"Received error: \\(error)\")\n}, onDisposed: {\n    print(\"Single disposed\")\n})\n\n// To cancel the suspend function just dispose the subscription\ndisposable.dispose()\n```\n\n## Customize\n\nThere are a number of ways you can customize the generated Kotlin code.\n\n### Name suffix\n\nDon't like the naming of the generated properties/functions?  \nSpecify your own custom suffixes in your `build.gradle.kts` file:\n```kotlin\nnativeCoroutines {\n    // The suffix used to generate the native coroutine function and property names.\n    suffix = \"Native\"\n    // The suffix used to generate the native coroutine file names.\n    // Note: defaults to the suffix value when `null`.\n    fileSuffix = null\n    // The suffix used to generate the StateFlow value property names,\n    // or `null` to remove the value properties.\n    flowValueSuffix = \"Value\"\n    // The suffix used to generate the SharedFlow replayCache property names,\n    // or `null` to remove the replayCache properties.\n    flowReplayCacheSuffix = \"ReplayCache\"\n    // The suffix used to generate the native state property names.\n    stateSuffix = \"Value\"\n    // The suffix used to generate the `StateFlow` flow property names,\n    // or `null` to remove the flow properties.\n    stateFlowSuffix = \"Flow\"\n}\n```\n\n### CoroutineScope\n\nFor more control you can provide a custom `CoroutineScope` with the `NativeCoroutineScope` annotation:\n```kotlin\nimport com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope\n\nclass Clock {\n    @NativeCoroutineScope\n    internal val coroutineScope = CoroutineScope(job + Dispatchers.Default)\n}\n```\n\n\u003e [!NOTE]\n\u003e Your custom coroutine scope must be either `internal` or `public`.\n\nIf you don't provide a `CoroutineScope` the default scope will be used which is defined as:\n```kotlin\ninternal val defaultCoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)\n```\n\n\u003e [!NOTE]\n\u003e KMP-NativeCoroutines has built-in support for [KMP-ObservableViewModel](https://github.com/rickclephas/KMP-ObservableViewModel).  \n\u003e Coroutines inside your `ViewModel` will (by default) use the `CoroutineScope` from the `ViewModelScope`. \n\n### Ignoring declarations\n\nUse the `NativeCoroutinesIgnore` annotation to tell the plugin to ignore a property or function:\n```kotlin\nimport com.rickclephas.kmp.nativecoroutines.NativeCoroutinesIgnore\n\n@NativeCoroutinesIgnore\nval ignoredFlowProperty: Flow\u003cInt\u003e\n\n@NativeCoroutinesIgnore\nsuspend fun ignoredSuspendFunction() { }\n```\n\n### Refining declarations in Swift\n\nIf for some reason you would like to further refine your Kotlin declarations in Swift, you can use the\n`NativeCoroutinesRefined` and `NativeCoroutinesRefinedState` annotations.  \nThese will tell the plugin to add the [`ShouldRefineInSwift`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native/-should-refine-in-swift/)\nannotation to the generated properties/function.\n\n\u003e [!NOTE]\n\u003e This currently requires a module-wide opt-in to `kotlin.experimental.ExperimentalObjCRefinement`.\n\nYou could for example refine your `Flow` property to an `AnyPublisher` property:\n```kotlin\nimport com.rickclephas.kmp.nativecoroutines.NativeCoroutinesRefined\n\nclass Clock {\n    @NativeCoroutinesRefined\n    val time: StateFlow\u003cLong\u003e\n}\n```\n\n```swift\nimport KMPNativeCoroutinesCombine\n\nextension Clock {\n    var time: AnyPublisher\u003cKotlinLong, Error\u003e {\n        createPublisher(for: __time)\n    }\n}\n```\n","funding_links":[],"categories":["Libraries","Multiplatform"],"sub_categories":["Utility","Android samples","➿ Asynchronous"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frickclephas%2FKMP-NativeCoroutines","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frickclephas%2FKMP-NativeCoroutines","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frickclephas%2FKMP-NativeCoroutines/lists"}