Skip to content
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

feat: first pass at conforming to Sendable #40

Merged
merged 18 commits into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 42 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
version: 2.1

## Orbs
orbs:
macos: circleci/macos@2

## Jobs
jobs:
unit_test:
macos:
xcode: 14.0.1
environment:
HOMEBREW_NO_AUTO_UPDATE: 1
steps:
- macos/preboot-simulator:
version: "16.0"
platform: "iOS"
device: "iPhone 14 Pro"
- macos/preboot-simulator:
version: "15.5"
platform: "iOS"
device: "iPhone 13 Pro"
- checkout
- run:
name: Install xcpretty
command: gem install xcpretty
- run:
name: Run iOS16 unit tests
command: Scripts/test -d "OS=16.0,name=iPhone 14 Pro" | xcpretty --color --report junit --output ~/test-results/ios16-results.xml
- run:
name: Run iOS15.5 unit tests
command: Scripts/test -d "OS=15.5,name=iPhone 13 Pro" | xcpretty --color --report junit --output ~/test-results/ios15-results.xml
- store_test_results:
path: ~/test-results
- store_artifacts:
path: ~/Library/Logs/DiagnosticReports

## Workflows
workflows:
on_push:
jobs:
- unit_test
29 changes: 0 additions & 29 deletions .github/workflows/ci.yml

This file was deleted.

16 changes: 11 additions & 5 deletions Asynchrone.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1310;
LastUpgradeCheck = 1310;
LastUpgradeCheck = 1400;
TargetAttributes = {
A4C361A9276A5EF200511525 = {
CreatedOnToolsVersion = 13.1;
Expand Down Expand Up @@ -599,6 +599,7 @@
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_STRICT_CONCURRENCY = minimal;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
Expand Down Expand Up @@ -656,6 +657,7 @@
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_STRICT_CONCURRENCY = minimal;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
Expand All @@ -664,11 +666,11 @@
A4C361BF276A5EF200511525 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
Expand All @@ -692,18 +694,19 @@
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "macosx iphoneos appletvos watchos iphonesimulator appletvsimulator watchsimulator appletvos";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
A4C361C0276A5EF200511525 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
Expand All @@ -727,6 +730,7 @@
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "macosx iphoneos appletvos watchos iphonesimulator appletvsimulator watchsimulator appletvos";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
};
name = Release;
Expand All @@ -740,6 +744,7 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
Expand Down Expand Up @@ -770,6 +775,7 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1310"
LastUpgradeVersion = "1400"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
13 changes: 6 additions & 7 deletions Asynchrone/Source/Extensions/AsyncSequence+Extension.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
extension AsyncSequence {

/// Assigns each element from an async sequence to a property on an object.
///
/// ```swift
Expand Down Expand Up @@ -35,7 +34,7 @@ extension AsyncSequence {
public func assign<Root>(
to keyPath: ReferenceWritableKeyPath<Root, Element>,
on object: Root
) rethrows -> Task<Void, Error> {
) rethrows -> Task<Void, Error> where Self: Sendable, Root: Sendable {
Task {
for try await element in self {
object[keyPath: keyPath] = element
Expand Down Expand Up @@ -121,8 +120,8 @@ extension AsyncSequence {
@discardableResult
public func sink(
priority: TaskPriority? = nil,
receiveValue: @escaping (Element) async -> Void
) -> Task<Void, Error> {
receiveValue: @Sendable @escaping (Element) async -> Void
) -> Task<Void, Error> where Self: Sendable {
Task(priority: priority) {
for try await element in self {
await receiveValue(element)
Expand Down Expand Up @@ -160,9 +159,9 @@ extension AsyncSequence {
@discardableResult
public func sink(
priority: TaskPriority? = nil,
receiveValue: @escaping (Element) async -> Void,
receiveCompletion: @escaping (AsyncSequenceCompletion<Error>) async -> Void
) -> Task<Void, Never> {
receiveValue: @Sendable @escaping (Element) async -> Void,
receiveCompletion: @Sendable @escaping (AsyncSequenceCompletion<Error>) async -> Void
) -> Task<Void, Never> where Self: Sendable {
Task(priority: priority) {
do {
for try await element in self {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ extension AsyncThrowingStream {
public init(
_ elementType: Element.Type = Element.self,
bufferingPolicy limit: AsyncThrowingStream<Element, Failure>.Continuation.BufferingPolicy = .unbounded,
_ build: @escaping (AsyncThrowingStream<Element, Failure>.Continuation) async -> Void
_ build: @Sendable @escaping (AsyncThrowingStream<Element, Failure>.Continuation) async -> Void
) where Failure == Error {
self = AsyncThrowingStream(elementType, bufferingPolicy: limit) { continuation in
Task {
Expand Down
46 changes: 27 additions & 19 deletions Asynchrone/Source/Sequences/AnyAsyncSequenceable.swift
Original file line number Diff line number Diff line change
@@ -1,45 +1,53 @@
/// An async sequence that performs type erasure by wrapping another async sequence.
///
/// If the async sequence that you wish to type erase can throw, then use `AnyThrowingAsyncSequenceable`.
public struct AnyAsyncSequenceable<Element>: AsyncSequence {
private var _next: () async -> Element?
private var _makeAsyncIterator: () -> Self
public struct AnyAsyncSequenceable<Element>: AsyncSequence, Sendable {
private var _makeAsyncIterator: @Sendable () -> Iterator

// MARK: Initialization

/// Creates a type erasing async sequence.
/// - Parameters:
/// - sequence: The async sequence to type erase.
public init<T>(_ sequence: T) where T: AsyncSequence, T.Element == Element {
var iterator = sequence.makeAsyncIterator()
self._next = { try? await iterator.next() }
self._makeAsyncIterator = { .init(sequence) }
public init<T>(_ sequence: T) where T: AsyncSequence, T.Element == Element, T: Sendable {
self._makeAsyncIterator = { Iterator(sequence.makeAsyncIterator()) }
}

/// Creates an optional type erasing async sequence.
/// - Parameters:
/// - sequence: An optional async sequence to type erase.
public init?<T>(_ asyncSequence: T?) where T: AsyncSequence, T.Element == Element {
public init?<T>(_ asyncSequence: T?) where T: AsyncSequence, T.Element == Element, T: Sendable {
guard let asyncSequence = asyncSequence else { return nil }
self = .init(asyncSequence)
}

// MARK: AsyncSequence

/// Creates an async iterator that emits elements of this async sequence.
/// - Returns: An instance that conforms to `AsyncIteratorProtocol`.
public func makeAsyncIterator() -> Self {

public func makeAsyncIterator() -> Iterator {
self._makeAsyncIterator()
}
}

// MARK: AsyncIteratorProtocol
// MARK: Iterator

extension AnyAsyncSequenceable: AsyncIteratorProtocol {
/// Produces the next element in the sequence.
/// - Returns: The next element or `nil` if the end of the sequence is reached.
public mutating func next() async -> Element? {
await self._next()
extension AnyAsyncSequenceable {
public struct Iterator: AsyncIteratorProtocol {
private var iterator: any AsyncIteratorProtocol

// MARK: Initialization

init<T>(_ iterator: T) where T: AsyncIteratorProtocol, T.Element == Element {
self.iterator = iterator
}

// MARK: AsyncIteratorProtocol

public mutating func next() async -> Element? {
// NOTE: When `AsyncSequence`, `AsyncIteratorProtocol` get their Element as
// their primary associated type we won't need the casting.
// https://github.com/apple/swift-evolution/blob/main/proposals/0358-primary-associated-types-in-stdlib.md#alternatives-considered
try? await self.iterator.next() as? Element
}
}
}

Expand All @@ -51,7 +59,7 @@ extension AsyncSequence {
/// If the async sequence that you wish to type erase can throw,
/// then use `eraseToAnyThrowingAsyncSequenceable()`.
/// - Returns: A typed erased async sequence.
public func eraseToAnyAsyncSequenceable() -> AnyAsyncSequenceable<Element> {
public func eraseToAnyAsyncSequenceable() -> AnyAsyncSequenceable<Element> where Self: Sendable {
.init(self)
}
}
48 changes: 28 additions & 20 deletions Asynchrone/Source/Sequences/AnyThrowingAsyncSequenceable.swift
Original file line number Diff line number Diff line change
@@ -1,45 +1,53 @@
/// A throwing async sequence that performs type erasure by wrapping another throwing async sequence.
///
/// If the async sequence that you wish to type erase doesn't throw, then use `AnyAsyncSequenceable`.
public struct AnyThrowingAsyncSequenceable<Element>: AsyncSequence {
private var _next: () async throws -> Element?
private var _makeAsyncIterator: () -> Self
public struct AnyThrowingAsyncSequenceable<Element>: AsyncSequence, Sendable {
private var _makeAsyncIterator: @Sendable () -> Iterator

// MARK: Initialization

/// Creates a type erasing async sequence.
/// - Parameters:
/// - sequence: The async sequence to type erase.
public init<T>(_ asyncSequence: T) where T: AsyncSequence, T.Element == Element {
var iterator = asyncSequence.makeAsyncIterator()
self._next = { try await iterator.next() }
self._makeAsyncIterator = { .init(asyncSequence) }
public init<T>(_ sequence: T) where T: AsyncSequence, T: Sendable, T.Element == Element {
self._makeAsyncIterator = { Iterator(sequence.makeAsyncIterator()) }
}

/// Creates an optional type erasing async sequence.
/// - Parameters:
/// - sequence: An optional async sequence to type erase.
public init?<T>(_ asyncSequence: T?) where T: AsyncSequence, T.Element == Element {
public init?<T>(_ asyncSequence: T?) where T: AsyncSequence, T: Sendable, T.Element == Element {
guard let asyncSequence = asyncSequence else { return nil }
self = .init(asyncSequence)
}

// MARK: AsyncSequence

/// Creates an async iterator that emits elements of this async sequence.
/// - Returns: An instance that conforms to `AsyncIteratorProtocol`.
public func makeAsyncIterator() -> Self {

public func makeAsyncIterator() -> Iterator {
self._makeAsyncIterator()
}
}

// MARK: AsyncIteratorProtocol
// MARK: Iterator

extension AnyThrowingAsyncSequenceable: AsyncIteratorProtocol {
/// Produces the next element in the sequence.
/// - Returns: The next element or `nil` if the end of the sequence is reached.
public mutating func next() async throws -> Element? {
try await self._next()
extension AnyThrowingAsyncSequenceable {
public struct Iterator: AsyncIteratorProtocol {
private var iterator: any AsyncIteratorProtocol

// MARK: Initialization

init<T>(_ iterator: T) where T: AsyncIteratorProtocol, T.Element == Element {
self.iterator = iterator
}

// MARK: AsyncIteratorProtocol

public mutating func next() async throws -> Element? {
// NOTE: When `AsyncSequence`, `AsyncIteratorProtocol` get their Element as
// their primary associated type we won't need the casting.
// https://github.com/apple/swift-evolution/blob/main/proposals/0358-primary-associated-types-in-stdlib.md#alternatives-considered
try await self.iterator.next() as? Element
}
}
}

Expand All @@ -52,7 +60,7 @@ extension AsyncSequence {
/// If the async sequence that you wish to type erase deson't throw,
/// then use `eraseToAnyAsyncSequenceable()`.
/// - Returns: A typed erased async sequence.
public func eraseToAnyThrowingAsyncSequenceable() -> AnyThrowingAsyncSequenceable<Element> {
public func eraseToAnyThrowingAsyncSequenceable() -> AnyThrowingAsyncSequenceable<Element> where Self: Sendable {
.init(self)
}
}
Loading