Skip to content

Add support for coroutine parameters #152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 62 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
de16b4d
Create inverse Kotlin wrapper functions
rickclephas Feb 16, 2022
f1c47a1
Create inverse Combine wrapper functions
rickclephas Feb 16, 2022
f113550
Merge branch 'master' into feature/GH-42-swift-to-kotlin-wrappers
rickclephas Feb 19, 2022
615ee3e
Merge branch 'master' into feature/GH-42-swift-to-kotlin-wrappers
rickclephas Feb 20, 2022
0e9126a
Revert to NativeError after merge
rickclephas Feb 20, 2022
50bf5e8
Merge branch 'master' into feature/GH-42-swift-to-kotlin-wrappers
rickclephas Aug 20, 2023
3f0a58f
Update asFlow and await after merge
rickclephas Aug 20, 2023
a976664
Update asNativeSuspend and asNativeFlow after merge
rickclephas Aug 20, 2023
9089f8d
Create asNativeSuspend and asNativeFlow Swift functions
rickclephas Sep 2, 2023
914a61b
Update integration tests with nil return type
rickclephas Sep 2, 2023
01eeaea
Make return type constants internal
rickclephas Sep 2, 2023
22dc2e4
Update documentation
rickclephas Sep 2, 2023
8d5f840
Add back pressure and cancellation support to Combine functions
rickclephas Sep 4, 2023
a1bf495
Add back pressure and cancellation support to RxSwift functions
rickclephas Sep 4, 2023
89b376c
Split async files
rickclephas Sep 4, 2023
7ab096e
Sort files
rickclephas Sep 4, 2023
6384253
Don't block thread while sending value
rickclephas Sep 4, 2023
30f28cf
Merge branch 'master' into feature/GH-42-swift-to-kotlin-wrappers
rickclephas Nov 18, 2023
879022c
Add logic to convert NativeError to Throwable and CancellationException
rickclephas Nov 18, 2023
d85a9e9
Add tests for NativeError conversion functions
rickclephas Nov 19, 2023
d0ae00b
Add tests for asFlow function
rickclephas Nov 19, 2023
ccf68a8
Add tests for await function
rickclephas Nov 19, 2023
d3ee660
Add NativeFlow and NativeSuspend tests for Swift concurrency
rickclephas Nov 26, 2023
036060d
Add NativeFlow and NativeSuspend tests for Combine
rickclephas Nov 26, 2023
6b974c8
Assert no result and no error
rickclephas Nov 26, 2023
bf2bd44
Add NativeFlow and NativeSuspend tests for RxSwift
rickclephas Nov 26, 2023
c09bc5b
Yield an item before completion
rickclephas Nov 26, 2023
8d59e39
Fix backpressure and concurrency issues for Combine
rickclephas Nov 26, 2023
50807f8
Convert CoroutinesReturnType.CoroutineScope to isCoroutineScopeProperty
rickclephas Dec 3, 2023
9c27e4a
Update checkers with extension and value parameter support
rickclephas Dec 3, 2023
c1b4b2f
Add UNSUPPORTED_INPUT_FLOW check
rickclephas Dec 3, 2023
9688dd1
Rename CoroutinesReturnType to CoroutinesType
rickclephas Dec 3, 2023
422021e
Add tests for extension and value parameters
rickclephas Dec 10, 2023
d692089
Fix some issues with extension and value parameter positioning strate…
rickclephas Dec 10, 2023
7239dc6
Fix issue where custom Flows weren't reported as unsupported
rickclephas Dec 10, 2023
f60e906
Use DeferSymbolException to defer symbol processing
rickclephas Jan 4, 2024
6d13b46
Detect function and custom Flow types in KSP plugin
rickclephas Jan 4, 2024
c20c64a
Merge branch 'master' into feature/GH-42-swift-to-kotlin-wrappers
rickclephas Aug 3, 2024
636cbbc
Remove extra Unit type parameter
rickclephas Aug 3, 2024
4d69a8d
Fix merge issues in checker classes
rickclephas Aug 3, 2024
0569d56
Replace CoroutinesClassIds with ClassIds
rickclephas Aug 3, 2024
a549a25
Update BoxTest helper functions with returnType parameter
rickclephas Aug 3, 2024
4debf8f
Update NativeError implementations for JVM
rickclephas Aug 3, 2024
62af668
Improve NativeUnit typealias to allow detection of Unit/Void return t…
rickclephas Aug 3, 2024
936d54a
Add more util functions for Unit/Void return types
rickclephas Aug 3, 2024
508fbb4
Specify JvmName for Unit util functions
rickclephas Aug 3, 2024
14a4f3e
Update IR dumps with new signatures
rickclephas Aug 4, 2024
3252ebe
Support unit functions in K2 codegen
rickclephas Aug 4, 2024
a008235
Support unit functions in KSP codegen
rickclephas Aug 4, 2024
ee99acd
Map new embeddable intellij imports
rickclephas Aug 4, 2024
28107b6
Run compiler tests in K2 mode
rickclephas Aug 4, 2024
386a0ab
Update FIR checker to use CallableSignature
rickclephas Aug 4, 2024
d475ff3
Move CallableSignature utils
rickclephas Aug 4, 2024
649aa9d
Create common CallableSignatureBuilder
rickclephas Aug 4, 2024
a7fe8c3
Update classic checker to use CallableSignature
rickclephas Aug 4, 2024
4f29096
Remove unused CoroutinesType
rickclephas Aug 4, 2024
1e5553d
Remove UNSUPPORTED_INPUT_FLOW check and just ignore unsupported input…
rickclephas Aug 5, 2024
d14b7d5
Fix DiagnosticFactory0 generic in idea-plugin
rickclephas Aug 5, 2024
5941817
Extract isGenerated check into extension property
rickclephas Aug 5, 2024
3f2ae54
Merge branch 'master' into feature/GH-42-swift-to-kotlin-wrappers
rickclephas Aug 10, 2024
8f80a19
Update FIR logic to support suspend types and input parameters
rickclephas Aug 11, 2024
b7f01c5
Merge branch 'master' into feature/GH-42-swift-to-kotlin-wrappers
rickclephas Sep 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion KMPNativeCoroutines.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions KMPNativeCoroutinesAsync/AnyAsyncSequence.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// AnyAsyncSequence.swift
// KMPNativeCoroutinesAsync
//
// Created by Rick Clephas on 02/09/2023.
//

/// An `AsyncSequence` that performs type erasure by wrapping another `AsyncSequence`.
public struct AnyAsyncSequence<Element>: AsyncSequence {
public typealias Element = Element
public typealias AsyncIterator = Iterator

public struct Iterator: AsyncIteratorProtocol {

private var base: any AsyncIteratorProtocol

internal init<I: AsyncIteratorProtocol>(_ base: I) where I.Element == Element {
self.base = base
}

public mutating func next() async throws -> Element? {
return try await base.next() as! Element?
}
}

private let _makeAsyncIterator: () -> Iterator

internal init<S: AsyncSequence>(_ base: S) where S.Element == Element {
_makeAsyncIterator = { Iterator(base.makeAsyncIterator()) }
}

public func makeAsyncIterator() -> Iterator {
return _makeAsyncIterator()
}
}
4 changes: 2 additions & 2 deletions KMPNativeCoroutinesAsync/AsyncError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import KMPNativeCoroutinesCore
/// Awaits the `NativeSuspend` and returns the optional error.
/// - Parameter nativeSuspend: The native suspend function to await.
/// - Returns: The `Error` from the `nativeSuspend`, or `nil`.
public func asyncError<Unit, Failure: Error>(
for nativeSuspend: @escaping NativeSuspend<Unit, Failure, Unit>
public func asyncError<Failure: Error>(
for nativeSuspend: @escaping NativeSuspend<NativeUnit?, Failure>
) async -> Error? {
do {
try await asyncFunction(for: nativeSuspend)
Expand Down
33 changes: 19 additions & 14 deletions KMPNativeCoroutinesAsync/AsyncFunction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,41 @@
import Dispatch
import KMPNativeCoroutinesCore

internal let RETURN_TYPE_SWIFT_ASYNC = "swift-async"

/// Wraps the `NativeSuspend` in an async function.
/// - Parameter nativeSuspend: The native suspend function to await.
/// - Returns: The result from the `nativeSuspend`.
/// - Throws: Errors thrown by the `nativeSuspend`.
public func asyncFunction<Result, Failure: Error, Unit>(
for nativeSuspend: @escaping NativeSuspend<Result, Failure, Unit>
public func asyncFunction<Result, Failure: Error>(
for nativeSuspend: @escaping NativeSuspend<Result, Failure>
) async throws -> Result {
try await AsyncFunctionTask(nativeSuspend: nativeSuspend).awaitResult()
if let function = nativeSuspend(RETURN_TYPE_SWIFT_ASYNC, EmptyNativeCallback1, EmptyNativeCallback1, EmptyNativeCallback1)() {
return try await (function as! (@Sendable () async throws -> Result))()
}
return try await AsyncFunctionTask(nativeSuspend: nativeSuspend).awaitResult()
}

/// Wraps the `NativeSuspend` in an async function.
/// - Parameter nativeSuspend: The native suspend function to await.
/// - Throws: Errors thrown by the `nativeSuspend`.
public func asyncFunction<Unit, Failure: Error>(
for nativeSuspend: @escaping NativeSuspend<Unit, Failure, Unit>
public func asyncFunction<Failure: Error>(
for nativeSuspend: @escaping NativeSuspend<NativeUnit?, Failure>
) async throws -> Void {
_ = try await AsyncFunctionTask(nativeSuspend: nativeSuspend).awaitResult()
}

private class AsyncFunctionTask<Result, Failure: Error, Unit>: @unchecked Sendable {
private class AsyncFunctionTask<Result, Failure: Error>: @unchecked Sendable {

private let semaphore = DispatchSemaphore(value: 1)
private var nativeCancellable: NativeCancellable<Unit>?
private var nativeCancellable: NativeCancellable?
private var result: Result? = nil
private var error: Failure? = nil
private var cancellationError: Failure? = nil
private var continuation: UnsafeContinuation<Result, Error>? = nil

init(nativeSuspend: NativeSuspend<Result, Failure, Unit>) {
nativeCancellable = nativeSuspend({ result, unit in
init(nativeSuspend: NativeSuspend<Result, Failure>) {
nativeCancellable = nativeSuspend(nil, { result in
self.semaphore.wait()
defer { self.semaphore.signal() }
self.result = result
Expand All @@ -46,8 +51,8 @@ private class AsyncFunctionTask<Result, Failure: Error, Unit>: @unchecked Sendab
self.continuation = nil
}
self.nativeCancellable = nil
return unit
}, { error, unit in
return nil
}, { error in
self.semaphore.wait()
defer { self.semaphore.signal() }
self.error = error
Expand All @@ -56,8 +61,8 @@ private class AsyncFunctionTask<Result, Failure: Error, Unit>: @unchecked Sendab
self.continuation = nil
}
self.nativeCancellable = nil
return unit
}, { cancellationError, unit in
return nil
}, { cancellationError in
self.semaphore.wait()
defer { self.semaphore.signal() }
self.cancellationError = cancellationError
Expand All @@ -66,7 +71,7 @@ private class AsyncFunctionTask<Result, Failure: Error, Unit>: @unchecked Sendab
self.continuation = nil
}
self.nativeCancellable = nil
return unit
return nil
})
}

Expand Down
8 changes: 4 additions & 4 deletions KMPNativeCoroutinesAsync/AsyncResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import KMPNativeCoroutinesCore
/// Awaits the `NativeSuspend` and returns the result.
/// - Parameter nativeSuspend: The native suspend function to await.
/// - Returns: The `Result` from the `nativeSuspend`.
public func asyncResult<Output, Failure: Error, Unit>(
for nativeSuspend: @escaping NativeSuspend<Output, Failure, Unit>
public func asyncResult<Output, Failure: Error>(
for nativeSuspend: @escaping NativeSuspend<Output, Failure>
) async -> Result<Output, Error> {
do {
return .success(try await asyncFunction(for: nativeSuspend))
Expand All @@ -23,8 +23,8 @@ public func asyncResult<Output, Failure: Error, Unit>(
/// Awaits the `NativeSuspend` and returns the result.
/// - Parameter nativeSuspend: The native suspend function to await.
/// - Returns: The `Result` from the `nativeSuspend`.
public func asyncResult<Unit, Failure: Error>(
for nativeSuspend: @escaping NativeSuspend<Unit, Failure, Unit>
public func asyncResult<Failure: Error>(
for nativeSuspend: @escaping NativeSuspend<NativeUnit?, Failure>
) async -> Result<Void, Error> {
do {
return .success(try await asyncFunction(for: nativeSuspend))
Expand Down
42 changes: 24 additions & 18 deletions KMPNativeCoroutinesAsync/AsyncSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,38 @@
import Dispatch
import KMPNativeCoroutinesCore

/// Wraps the `NativeFlow` in a `NativeFlowAsyncSequence`.
internal let RETURN_TYPE_SWIFT_ASYNC_SEQUENCE = "swift-async-sequence"

/// Wraps the `NativeFlow` in an `AsyncSequence`.
/// - Parameter nativeFlow: The native flow to collect.
/// - Returns: A `NativeFlowAsyncSequence` that yields the collected values.
public func asyncSequence<Output, Failure: Error, Unit>(
for nativeFlow: @escaping NativeFlow<Output, Failure, Unit>
) -> NativeFlowAsyncSequence<Output, Error, Unit> {
return NativeFlowAsyncSequence(nativeFlow: nativeFlow)
/// - Returns: An `AsyncSequence` that yields the collected values.
public func asyncSequence<Output, Failure: Error>(
for nativeFlow: @escaping NativeFlow<Output, Failure>
) -> AnyAsyncSequence<Output> {
if let sequence = nativeFlow(RETURN_TYPE_SWIFT_ASYNC_SEQUENCE, EmptyNativeCallback2, EmptyNativeCallback1, EmptyNativeCallback1)() {
return sequence as! AnyAsyncSequence<Output>
}
return AnyAsyncSequence(NativeFlowAsyncSequence(nativeFlow: nativeFlow))
}

public struct NativeFlowAsyncSequence<Output, Failure: Error, Unit>: AsyncSequence {
private struct NativeFlowAsyncSequence<Output, Failure: Error>: AsyncSequence {
typealias AsyncIterator = Iterator

public typealias Element = Output

var nativeFlow: NativeFlow<Output, Failure, Unit>
var nativeFlow: NativeFlow<Output, Failure>

public class Iterator: AsyncIteratorProtocol, @unchecked Sendable {

private let semaphore = DispatchSemaphore(value: 1)
private var nativeCancellable: NativeCancellable<Unit>?
private var item: (Output, () -> Unit)? = nil
private var nativeCancellable: NativeCancellable?
private var item: (Output, NativeCallback)? = nil
private var result: Failure?? = Optional.none
private var cancellationError: Failure? = nil
private var continuation: UnsafeContinuation<Output?, Error>? = nil

init(nativeFlow: NativeFlow<Output, Failure, Unit>) {
nativeCancellable = nativeFlow({ item, next, unit in
init(nativeFlow: NativeFlow<Output, Failure>) {
nativeCancellable = nativeFlow(nil, { item, next in
self.semaphore.wait()
defer { self.semaphore.signal() }
if let continuation = self.continuation {
Expand All @@ -41,9 +48,9 @@ public struct NativeFlowAsyncSequence<Output, Failure: Error, Unit>: AsyncSequen
return next()
} else {
self.item = (item, next)
return unit
return nil
}
}, { error, unit in
}, { error in
self.semaphore.wait()
defer { self.semaphore.signal() }
self.result = Optional.some(error)
Expand All @@ -56,8 +63,8 @@ public struct NativeFlowAsyncSequence<Output, Failure: Error, Unit>: AsyncSequen
self.continuation = nil
}
self.nativeCancellable = nil
return unit
}, { cancellationError, unit in
return nil
}, { cancellationError in
self.semaphore.wait()
defer { self.semaphore.signal() }
self.cancellationError = cancellationError
Expand All @@ -66,7 +73,7 @@ public struct NativeFlowAsyncSequence<Output, Failure: Error, Unit>: AsyncSequen
self.continuation = nil
}
self.nativeCancellable = nil
return unit
return nil
})
}

Expand Down Expand Up @@ -105,4 +112,3 @@ public struct NativeFlowAsyncSequence<Output, Failure: Error, Unit>: AsyncSequen
return Iterator(nativeFlow: nativeFlow)
}
}

51 changes: 51 additions & 0 deletions KMPNativeCoroutinesAsync/NativeFlow.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// NativeFlow.swift
// KMPNativeCoroutinesAsync
//
// Created by Rick Clephas on 04/09/2023.
//

import KMPNativeCoroutinesCore

public extension AsyncSequence {
/// Creates a `NativeFlow` for this `AsyncSequence`.
/// - Parameter priority: The priority of the task collecting this `AsyncSequence`.
func asNativeFlow(priority: TaskPriority? = nil) -> NativeFlow<Element, Error> {
return { returnType, onItem, onComplete, onCancelled in
if returnType == RETURN_TYPE_SWIFT_ASYNC_SEQUENCE {
return { AnyAsyncSequence(self) }
} else if returnType != nil {
return { nil }
}
let task = Task(priority: priority) {
do {
for try await value in self {
await withUnsafeContinuation { continuation in
_ = onItem(value, {
continuation.resume()
return nil
})
}
}
try Task.checkCancellation()
_ = onComplete(nil)
} catch {
if error is CancellationError {
_ = onCancelled(error)
} else {
_ = onComplete(error)
}
}
}
return task.asNativeCancellable()
}
}
}

public extension AsyncSequence where Element == Void {
/// Creates a `NativeFlow` for this `AsyncSequence`.
/// - Parameter priority: The priority of the task collecting this `AsyncSequence`.
func asNativeFlow(priority: TaskPriority? = nil) -> NativeFlow<NativeUnit?, Error> {
map { nil }.asNativeFlow(priority: priority)
}
}
53 changes: 53 additions & 0 deletions KMPNativeCoroutinesAsync/NativeSuspend.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// NativeSuspend.swift
// KMPNativeCoroutinesAsync
//
// Created by Rick Clephas on 04/09/2023.
//

import KMPNativeCoroutinesCore

/// Creates a `NativeSuspend` for the provided async operation.
/// - Parameters:
/// - priority: The priority of the task executing the operation.
/// - operation: The async operation to execute.
public func nativeSuspend<Result>(
priority: TaskPriority? = nil,
operation: @escaping @Sendable () async throws -> Result
) -> NativeSuspend<Result, Error> {
return { returnType, onResult, onError, onCancelled in
if returnType == RETURN_TYPE_SWIFT_ASYNC {
return { operation }
} else if returnType != nil {
return { nil }
}
let task = Task(priority: priority) {
do {
let result = try await operation()
try Task.checkCancellation()
_ = onResult(result)
} catch {
if error is CancellationError {
_ = onCancelled(error)
} else {
_ = onError(error)
}
}
}
return task.asNativeCancellable()
}
}

/// Creates a `NativeSuspend` for the provided async operation.
/// - Parameters:
/// - priority: The priority of the task executing the operation.
/// - operation: The async operation to execute.
public func nativeSuspend(
priority: TaskPriority? = nil,
operation: @escaping @Sendable () async throws -> Void
) -> NativeSuspend<NativeUnit?, Error> {
nativeSuspend(priority: priority) {
try await operation()
return nil
}
}
18 changes: 18 additions & 0 deletions KMPNativeCoroutinesAsync/Task.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// Task.swift
// KMPNativeCoroutinesAsync
//
// Created by Rick Clephas on 02/09/2023.
//

import KMPNativeCoroutinesCore

internal extension Task {
/// Creates a `NativeCancellable` for this `Task`.
func asNativeCancellable() -> NativeCancellable {
return {
self.cancel()
return nil
}
}
}
Loading