KMPアプリのSwift CodeのKotlin Coroutinesを使用するライブラリ。
KMPとKotlin Coroutinesの両方は驚くべきものですが、一緒にいくつかの制限があります。
最も重要な制限は、キャンセルサポートです。
Kotlin Suspend関数は、完了ハンドラーを使用した機能としてSwiftにさらされます。
これにより、Swiftコードから簡単に使用できますが、キャンセルはサポートしていません。
注記
Swift 5.5はAsync関数をSwiftにもたらしますが、この問題は解決しません。
OBJCとの相互運用性のために、完了ハンドラーを使用したすべての機能は、非同期関数のように呼ばれます。
これは、Swift 5.5から始めるKotlin Suspend関数がSwift Async関数のように見えることを意味します。
しかし、それは単なる構文砂糖なので、キャンセルサポートはまだありません。
キャンセルサポートに加えて、OBJCはプロトコル上のジェネリックをサポートしていません。
したがって、すべてのFlowインターフェイスが一般的な値タイプを失い、使用が困難になります。
このライブラリは、これらの両方の制限を解決しますか?
ライブラリの最新バージョンでは、Kotlinバージョン2.1.0使用しています。
古いおよび/またはプレビューKotlinバージョンの互換性バージョンも利用できます。
| バージョン | バージョンの接尾辞 | コトリン | ksp | コルーチン |
|---|---|---|---|---|
| 最新 | 接尾辞はありません | 2.1.0 | 1.0.29 | 1.9.0 |
| 1.0.0-Alpha-37 | 接尾辞はありません | 2.0.21 | 1.0.25 | 1.9.0 |
| 1.0.0-alpha-36 | 接尾辞はありません | 2.0.20 | 1.0.25 | 1.9.0 |
| 1.0.0-alpha-35 | 接尾辞はありません | 2.0.20 | 1.0.24 | 1.8.1 |
| 1.0.0-alpha-34 | 接尾辞はありません | 2.0.10 | 1.0.24 | 1.8.1 |
| 1.0.0-Alpha-33 | 接尾辞はありません | 2.0.0 | 1.0.24 | 1.8.1 |
| 1.0.0-alpha-30 | 接尾辞はありません | 1.9.24 | 1.0.20 | 1.8.1 |
| 1.0.0-alpha-28 | 接尾辞はありません | 1.9.23 | 1.0.20 | 1.8.0 |
| 1.0.0-alpha-25 | 接尾辞はありません | 1.9.22 | 1.0.17 | 1.8.0 |
| 1.0.0-alpha-23 | 接尾辞はありません | 1.9.21 | 1.0.16 | 1.7.3 |
| 1.0.0-alpha-21 | 接尾辞はありません | 1.9.20 | 1.0.14 | 1.7.3 |
| 1.0.0-alpha-18 | 接尾辞はありません | 1.9.10 | 1.0.13 | 1.7.3 |
| 1.0.0-alpha-17 | 接尾辞はありません | 1.9.0 | 1.0.12 | 1.7.3 |
| 1.0.0-alpha-12 | 接尾辞はありません | 1.8.22 | 1.0.11 | 1.7.2 |
| 1.0.0-alpha-10 | 接尾辞はありません | 1.8.21 | 1.0.11 | 1.7.1 |
| 1.0.0-alpha-7 | 接尾辞はありません | 1.8.20 | 1.0.10 | 1.6.4 |
いくつかの迅速な実装から選択できます。
実装に応じて、iOS 9、MacOS 10.9、TVOS 9、およびWatchos 3と同じくらい低くサポートできます。
| 実装 | 迅速 | iOS | macos | TVOS | watchos |
|---|---|---|---|---|---|
| async | 5.5 | 13.0 | 10.15 | 13.0 | 6.0 |
| 組み合わせる | 5.0 | 13.0 | 10.15 | 13.0 | 6.0 |
| rxswift | 5.0 | 9.0 | 10.9 | 9.0 | 3.0 |
図書館は、プロジェクトに追加する必要があるコトリンと迅速な部分で構成されています。
Kotlin部品はMaven Centralで利用でき、Swift部品はCocoapodsまたはSwift Package Managerを介してインストールできます。
すべてのライブラリに対して常に同じバージョンを使用してください!
Kotlinについては、 build.gradle.ktsにプラグインを追加するだけです:
plugins {
id( " com.google.devtools.ksp " ) version " 2.1.0-1.0.29 "
id( " com.rickclephas.kmp.nativecoroutines " ) version " 1.0.0-ALPHA-38 "
}そして、実験的な@ObjCNameアノテーションに必ずオプトインしてください。
kotlin.sourceSets.all {
languageSettings.optIn( " kotlin.experimental.ExperimentalObjCName " )
} Swiftの実装は、Swift Package Managerから入手できます。
Package.swiftに追加するだけです。swiftファイル:
dependencies: [
. package ( url : " https://github.com/rickclephas/KMP-NativeCoroutines.git " , exact : " 1.0.0-ALPHA-38 " )
] ,
targets: [
. target (
name : " MyTargetName " ,
dependencies : [
// Swift Concurrency implementation
. product ( name : " KMPNativeCoroutinesAsync " , package : " KMP-NativeCoroutines " ) ,
// Combine implementation
. product ( name : " KMPNativeCoroutinesCombine " , package : " KMP-NativeCoroutines " ) ,
// RxSwift implementation
. product ( name : " KMPNativeCoroutinesRxSwift " , package : " KMP-NativeCoroutines " )
]
)
]または、> File > Add Packages...に移動してxcodeに追加し、 https://github.com/rickclephas/KMP-NativeCoroutines.gitを提供します。
注記
Swiftパッケージのバージョンには、Kotlinバージョンの接尾辞( -new-mmまたは-kotlin-1.6.0など)を含めるべきではありません。
注記
単一の実装のみが必要な場合は、接尾辞-spm-async 、 -spm-combine 、および-spm-rxswiftを備えたSPM固有のバージョンを使用することもできます。
cocoapodsを使用している場合、次のライブラリの1つ以上をPodfileに追加します。
pod 'KMPNativeCoroutinesAsync' , '1.0.0-ALPHA-38' # Swift Concurrency implementation
pod 'KMPNativeCoroutinesCombine' , '1.0.0-ALPHA-38' # Combine implementation
pod 'KMPNativeCoroutinesRxSwift' , '1.0.0-ALPHA-38' # RxSwift implementation 注記
Cocoapodsのバージョンには、Kotlinバージョンの接尾辞( -new-mmまたは-kotlin-1.6.0など)を含めるべきではありません。
Jetbrains MarketplaceからIDEプラグインをインストールして、取得します。
SwiftのKotlin Coroutinesコードを使用することは、Kotlinコードを呼び出すのとほぼ同じくらい簡単です。
Swiftのラッパー関数を使用して、非同期関数、非同期ストリーム、出版社、または観測可能性を取得してください。
プラグインは、必要なコードを自動的に生成します! ?
@NativeCoroutines (または@NativeCoroutinesState )でコルーチン宣言を注入するだけです。
Flowプロパティ/関数ネイティブバージョンを取得します。
import com.rickclephas.kmp.nativecoroutines.NativeCoroutines
class Clock {
// Somewhere in your Kotlin code you define a Flow property
// and annotate it with @NativeCoroutines
@NativeCoroutines
val time : StateFlow < Long > // This can be any kind of Flow
}プラグインはあなたのためにこのネイティブプロパティを生成します:
import com.rickclephas.kmp.nativecoroutines.asNativeFlow
import kotlin.native.ObjCName
@ObjCName(name = " time " )
val Clock .timeNative
get() = time.asNativeFlow()プラグインの上に定義されているStateFlowについては、この値プロパティも生成します。
val Clock .timeValue
get() = time.value SharedFlowの場合、プラグインはリプレイキャッシュプロパティを生成します。
val Clock .timeReplayCache
get() = time.replayCache StateFlowプロパティを使用して状態を追跡します(ビューモデルのように)?
代わりに@NativeCoroutinesStateアノテーションを使用してください。
import com.rickclephas.kmp.nativecoroutines.NativeCoroutinesState
class Clock {
// Somewhere in your Kotlin code you define a StateFlow property
// and annotate it with @NativeCoroutinesState
@NativeCoroutinesState
val time : StateFlow < Long > // This must be a StateFlow
}プラグインは、これらのネイティブプロパティを生成します。
import com.rickclephas.kmp.nativecoroutines.asNativeFlow
import kotlin.native.ObjCName
@ObjCName(name = " time " )
val Clock .timeValue
get() = time.value
val Clock .timeFlow
get() = time.asNativeFlow()プラグインは、注釈付きサスペンド関数のネイティブバージョンも生成します。
import com.rickclephas.kmp.nativecoroutines.NativeCoroutines
class RandomLettersGenerator {
// Somewhere in your Kotlin code you define a suspend function
// and annotate it with @NativeCoroutines
@NativeCoroutines
suspend fun getRandomLetters (): String {
// Code to generate some random letters
}
}プラグインはあなたのためにこのネイティブ機能を生成します:
import com.rickclephas.kmp.nativecoroutines.nativeSuspend
import kotlin.native.ObjCName
@ObjCName(name = " getRandomLetters " )
fun RandomLettersGenerator. getRandomLettersNative () =
nativeSuspend { getRandomLetters() }残念ながら、Objective-Cプロトコルでは拡張機能/プロパティはサポートされていません。
ただし、この制限は、迅速な魔法で「克服」される可能性があります。
RandomLettersGeneratorがclassの代わりにinterfaceであると仮定すると、次のことができます。
import KMPNativeCoroutinesCore
extension RandomLettersGenerator {
func getRandomLetters ( ) -> NativeSuspend < String , Error , KotlinUnit > {
RandomLettersGeneratorNativeKt . getRandomLetters ( self )
}
} 一時停止関数および/またはFlow宣言がOBJC/Swiftにさらされると、コンパイラとIDEプラグインが警告を生成し、KMP-NativeCoroutinesアノテーションの1つを追加するように思い出させます。
これらのチェックの重大度をbuild.gradle.ktsファイルでカスタマイズできます。
nativeCoroutines {
exposedSeverity = ExposedSeverity . ERROR
}または、これらのチェックに興味がない場合は、それらを無効にします。
nativeCoroutines {
exposedSeverity = ExposedSeverity . NONE
}Async実装は、非同期関数とAsyncSequenceを取得するためのいくつかの関数を提供します。
asyncFunction(for:)関数を使用して、待ち望まれている非同期関数を取得します。
import KMPNativeCoroutinesAsync
let handle = Task {
do {
let letters = try await asyncFunction ( for : randomLettersGenerator . getRandomLetters ( ) )
print ( " Got random letters: ( letters ) " )
} catch {
print ( " Failed with error: ( error ) " )
}
}
// To cancel the suspend function just cancel the async task
handle . cancel ( )または、これらのdo-catchesが気に入らない場合はasyncResult(for:) function:
import KMPNativeCoroutinesAsync
let result = await asyncResult ( for : randomLettersGenerator . getRandomLetters ( ) )
if case let . success ( letters ) = result {
print ( " Got random letters: ( letters ) " )
} Unit返却機能にはasyncError(for:)関数:
import KMPNativeCoroutinesAsync
if let error = await asyncError ( for : integrationTests . returnUnit ( ) ) {
print ( " Failed with error: ( error ) " )
} Flow sの場合、非同期AsyncSequence asyncSequence(for:)機能があります。
import KMPNativeCoroutinesAsync
let handle = Task {
do {
let sequence = asyncSequence ( for : randomLettersGenerator . getRandomLettersFlow ( ) )
for try await letters in sequence {
print ( " Got random letters: ( letters ) " )
}
} catch {
print ( " Failed with error: ( error ) " )
}
}
// To cancel the flow (collection) just cancel the async task
handle . cancel ( )Combineの実装は、CoroutinesコードのAnyPublisher取得するためのいくつかの関数を提供します。
注記
これらの関数は、 AnyPublisherを延期します。
すべてのサブスクリプションを意味すると、サスペンド関数のFlowまたは実行のコレクションがトリガーされます。
注記
返されたCancellable Sへの参照を保持する必要があります。そうしないと、コレクションはすぐにキャンセルされます。
あなたのFlowには、 createPublisher(for:) function:
import KMPNativeCoroutinesCombine
// Create an AnyPublisher for your flow
let publisher = createPublisher ( for : clock . time )
// Now use this publisher as you would any other
let cancellable = publisher . sink { completion in
print ( " Received completion: ( completion ) " )
} receiveValue : { value in
print ( " Received value: ( value ) " )
}
// To cancel the flow (collection) just cancel the publisher
cancellable . cancel ( )また、 Flowを返す機能にcreatePublisher(for:) functionに使用することもできます。
let publisher = createPublisher ( for : randomLettersGenerator . getRandomLettersFlow ( ) ) サスペンド関数にはcreateFuture(for:) function:を使用する必要があります。
import KMPNativeCoroutinesCombine
// Create a Future/AnyPublisher for the suspend function
let future = createFuture ( for : randomLettersGenerator . getRandomLetters ( ) )
// Now use this future as you would any other
let cancellable = future . sink { completion in
print ( " Received completion: ( completion ) " )
} receiveValue : { value in
print ( " Received value: ( value ) " )
}
// To cancel the suspend function just cancel the future
cancellable . cancel ( ) RXSwiftの実装は、CoroutinesコードのObservableまたはSingle取得するためのいくつかの関数を提供します。
注記
これらの関数は、延期されたObservable sとSingle sを作成します。
すべてのサブスクリプションを意味すると、サスペンド関数のFlowまたは実行のコレクションがトリガーされます。
あなたのFlowにはcreateObservable(for:) function:
import KMPNativeCoroutinesRxSwift
// Create an observable for your flow
let observable = createObservable ( for : clock . time )
// Now use this observable as you would any other
let disposable = observable . subscribe ( onNext : { value in
print ( " Received value: ( value ) " )
} , onError : { error in
print ( " Received error: ( error ) " )
} , onCompleted : {
print ( " Observable completed " )
} , onDisposed : {
print ( " Observable disposed " )
} )
// To cancel the flow (collection) just dispose the subscription
disposable . dispose ( )また、 Flowを返す機能にcreateObservable(for:) function:flowを使用することもできます。
let observable = createObservable ( for : randomLettersGenerator . getRandomLettersFlow ( ) ) 一時停止関数にはcreateSingle(for:) function:を使用する必要があります。
import KMPNativeCoroutinesRxSwift
// Create a single for the suspend function
let single = createSingle ( for : randomLettersGenerator . getRandomLetters ( ) )
// Now use this single as you would any other
let disposable = single . subscribe ( onSuccess : { value in
print ( " Received value: ( value ) " )
} , onFailure : { error in
print ( " Received error: ( error ) " )
} , onDisposed : {
print ( " Single disposed " )
} )
// To cancel the suspend function just dispose the subscription
disposable . dispose ( ) 生成されたKotlinコードをカスタマイズする方法はいくつかあります。
生成されたプロパティ/関数の命名が気に入らないのですか?
build.gradle.ktsファイルで独自のカスタムサフィックスを指定します:
nativeCoroutines {
// The suffix used to generate the native coroutine function and property names.
suffix = " Native "
// The suffix used to generate the native coroutine file names.
// Note: defaults to the suffix value when `null`.
fileSuffix = null
// The suffix used to generate the StateFlow value property names,
// or `null` to remove the value properties.
flowValueSuffix = " Value "
// The suffix used to generate the SharedFlow replayCache property names,
// or `null` to remove the replayCache properties.
flowReplayCacheSuffix = " ReplayCache "
// The suffix used to generate the native state property names.
stateSuffix = " Value "
// The suffix used to generate the `StateFlow` flow property names,
// or `null` to remove the flow properties.
stateFlowSuffix = " Flow "
}さらにコントロールするには、 NativeCoroutineScopeアノテーションを使用してカスタムCoroutineScope提供できます。
import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope
class Clock {
@NativeCoroutineScope
internal val coroutineScope = CoroutineScope (job + Dispatchers . Default )
}注記
カスタムコルーチンスコープは、 internalまたはpublicいずれかでなければなりません。
CoroutineScopeを提供しない場合、デフォルトのスコープが使用されます。これは次のとおりです。
internal val defaultCoroutineScope = CoroutineScope ( SupervisorJob () + Dispatchers . Default )注記
KMP-NativeCoroutinesには、KMP-ObservableViewModelのサポートが組み込まれています。
ViewModel内のCoroutinesは(デフォルトでは) ViewModelScopeからCoroutineScopeを使用します。
NativeCoroutinesIgnoreアノテーションを使用して、プラグインにプロパティまたは機能を無視するように指示します。
import com.rickclephas.kmp.nativecoroutines.NativeCoroutinesIgnore
@NativeCoroutinesIgnore
val ignoredFlowProperty : Flow < Int >
@NativeCoroutinesIgnore
suspend fun ignoredSuspendFunction () { }何らかの理由で、Kotlin宣言をSwiftでさらに洗練したい場合は、 NativeCoroutinesRefinedおよびNativeCoroutinesRefinedState注釈を使用できます。
これらは、プラグインに、生成されたプロパティ/関数にShouldRefineInSwiftアノテーションを追加するように指示します。
注記
これには現在、 kotlin.experimental.ExperimentalObjCRefinementへのモジュール全体のオプトインが必要です。
たとえば、 FlowプロパティをAnyPublisherプロパティに洗練できます。
import com.rickclephas.kmp.nativecoroutines.NativeCoroutinesRefined
class Clock {
@NativeCoroutinesRefined
val time : StateFlow < Long >
}import KMPNativeCoroutinesCombine
extension Clock {
var time : AnyPublisher < KotlinLong , Error > {
createPublisher ( for : __time )
}
}