Skip to content
Open
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
18 changes: 18 additions & 0 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/ReactiveX/RxSwift", "6.9.0"..<"7.0.0"),
.package(url: "https://github.com/apple/swift-async-algorithms", from: "1.0.0"),
.package(url: "https://github.com/mattgallagher/CwlPreconditionTesting.git", from: "2.2.2"), // for testTarget only
],
targets: [
.target(
name: "RIBs",
dependencies: [
.product(name: "RxSwift", package: "RxSwift"),
.product(name: "RxRelay", package: "RxSwift")
.product(name: "RxRelay", package: "RxSwift"),
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms")
],
path: "RIBs"
),
Expand Down
28 changes: 28 additions & 0 deletions RIBs.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
AFB7D4031FC81C8F00045D2B /* Foundation+ExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF82F6731FC81B5F006DF7BC /* Foundation+ExtensionsTests.swift */; };
AFB7D4051FC81D6100045D2B /* LaunchRouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFB7D4041FC81D6100045D2B /* LaunchRouterTests.swift */; };
BF5FC0F122808377004235F1 /* RxRelay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF5FC0F022808377004235F1 /* RxRelay.framework */; };
C0A100012B10000100A10001 /* Observable+AsyncSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A100032B10000100A10001 /* Observable+AsyncSequence.swift */; };
C0A100022B10000100A10001 /* Lifecycle+AsyncSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A100042B10000100A10001 /* Lifecycle+AsyncSequence.swift */; };
C0A100072B10000200A10001 /* AsyncSequence+RIBs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A100092B10000200A10001 /* AsyncSequence+RIBs.swift */; };
C0A100082B10000200A10001 /* Task+RIBs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A1000A2B10000200A10001 /* Task+RIBs.swift */; };
C0A100062B10000100A10001 /* AsyncStreamTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A100052B10000100A10001 /* AsyncStreamTests.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -75,6 +80,11 @@
AF9966B11FC40D7E00CAEAA2 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = ../Carthage/Build/iOS/RxSwift.framework; sourceTree = "<group>"; };
AFB7D4041FC81D6100045D2B /* LaunchRouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchRouterTests.swift; sourceTree = "<group>"; };
BF5FC0F022808377004235F1 /* RxRelay.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxRelay.framework; path = ../Carthage/Build/iOS/RxRelay.framework; sourceTree = "<group>"; };
C0A100032B10000100A10001 /* Observable+AsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Observable+AsyncSequence.swift"; sourceTree = "<group>"; };
C0A100042B10000100A10001 /* Lifecycle+AsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Lifecycle+AsyncSequence.swift"; sourceTree = "<group>"; };
C0A100052B10000100A10001 /* AsyncStreamTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStreamTests.swift; sourceTree = "<group>"; };
C0A100092B10000200A10001 /* AsyncSequence+RIBs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AsyncSequence+RIBs.swift"; sourceTree = "<group>"; };
C0A1000A2B10000200A10001 /* Task+RIBs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Task+RIBs.swift"; sourceTree = "<group>"; };
E8E789432378AD000043E59E /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Package.swift; path = ../Package.swift; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -110,6 +120,7 @@
413177271F8EEFEF005F08F0 /* Router.swift */,
413177241F8EEFEF005F08F0 /* ViewableRouter.swift */,
413177281F8EEFEF005F08F0 /* ViewControllable.swift */,
C0A100002B10000100A10001 /* Concurrency */,
418C17551F97DB0E003C03F7 /* DI */,
4131773B1F8EF981005F08F0 /* Extensions */,
413177361F8EF70A005F08F0 /* LeakDetector */,
Expand All @@ -119,6 +130,17 @@
path = Classes;
sourceTree = "<group>";
};
C0A100002B10000100A10001 /* Concurrency */ = {
isa = PBXGroup;
children = (
C0A100092B10000200A10001 /* AsyncSequence+RIBs.swift */,
C0A100042B10000100A10001 /* Lifecycle+AsyncSequence.swift */,
C0A100032B10000100A10001 /* Observable+AsyncSequence.swift */,
C0A1000A2B10000200A10001 /* Task+RIBs.swift */,
);
path = Concurrency;
sourceTree = "<group>";
};
413177201F8EEFEF005F08F0 /* Workflow */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -195,6 +217,7 @@
isa = PBXGroup;
children = (
8B9882F21F86E1CF00ABE009 /* Info.plist */,
C0A100052B10000100A10001 /* AsyncStreamTests.swift */,
AFB7D4041FC81D6100045D2B /* LaunchRouterTests.swift */,
AF90B40B1FBA157700920384 /* Mocks.swift */,
AF90B4091FBA14DB00920384 /* RouterTests.swift */,
Expand Down Expand Up @@ -382,6 +405,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0A100072B10000200A10001 /* AsyncSequence+RIBs.swift in Sources */,
C0A100022B10000100A10001 /* Lifecycle+AsyncSequence.swift in Sources */,
4131773A1F8EF711005F08F0 /* LeakDetector.swift in Sources */,
418C175C1F97F19F003C03F7 /* Component.swift in Sources */,
4131773D1F8EF98A005F08F0 /* Foundation+Extensions.swift in Sources */,
Expand All @@ -392,6 +417,8 @@
4131772D1F8EF5FF005F08F0 /* Interactor.swift in Sources */,
413177321F8EF5FF005F08F0 /* ViewableRouter.swift in Sources */,
413177351F8EF605005F08F0 /* Workflow.swift in Sources */,
C0A100012B10000100A10001 /* Observable+AsyncSequence.swift in Sources */,
C0A100082B10000200A10001 /* Task+RIBs.swift in Sources */,
4131772F1F8EF5FF005F08F0 /* PresentableInteractor.swift in Sources */,
4131772C1F8EF5FF005F08F0 /* Builder.swift in Sources */,
413177331F8EF5FF005F08F0 /* ViewControllable.swift in Sources */,
Expand All @@ -404,6 +431,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0A100062B10000100A10001 /* AsyncStreamTests.swift in Sources */,
AFB7D4031FC81C8F00045D2B /* Foundation+ExtensionsTests.swift in Sources */,
AF90B4111FBA185E00920384 /* ComponentTests.swift in Sources */,
AFB7D4051FC81D6100045D2B /* LaunchRouterTests.swift in Sources */,
Expand Down
73 changes: 73 additions & 0 deletions RIBs/Classes/Concurrency/AsyncSequence+RIBs.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// Copyright (c) 2017. Uber Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import AsyncAlgorithms
import RxSwift

/// A type-erased async sequence used to preserve element type information without requiring typed
/// `AsyncSequence` availability.
public struct AnyAsyncSequence<Element>: AsyncSequence {

public struct AsyncIterator: AsyncIteratorProtocol {

private var nextElement: () async throws -> Element?

fileprivate init<Iterator: AsyncIteratorProtocol>(_ iterator: Iterator) where Iterator.Element == Element {
var iterator = iterator
nextElement = {
try await iterator.next()
}
}

public mutating func next() async throws -> Element? {
return try await nextElement()
}
}

private let makeIterator: () -> AsyncIterator

public init<Sequence: AsyncSequence>(_ sequence: Sequence) where Sequence.Element == Element {
makeIterator = {
AsyncIterator(sequence.makeAsyncIterator())
}
}

public func makeAsyncIterator() -> AsyncIterator {
return makeIterator()
}
}

public extension AsyncSequence {

/// Confines the async sequence's elements to the given interactor scope.
///
/// Elements are only yielded while the interactor scope is active. Values emitted while inactive are ignored,
/// except for the latest value that can be emitted when the scope becomes active again.
///
/// - parameter interactorScope: The interactor scope whose activeness this async sequence is confined to.
/// - returns: The async sequence confined to this interactor's activeness lifecycle.
func confineTo(_ interactorScope: InteractorScope) -> AnyAsyncSequence<Element> {
return AnyAsyncSequence(
combineLatest(interactorScope.isActiveStream.values, self)
.filter { isActive, _ in
isActive
}
.map { _, element in
element
}
)
}
}
47 changes: 47 additions & 0 deletions RIBs/Classes/Concurrency/Lifecycle+AsyncSequence.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// Copyright (c) 2017. Uber Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

public extension InteractorScope {

/// The lifecycle of this interactor exposed as an async stream.
var isActiveSequence: AsyncStream<Bool> {
return isActiveStream.asAsyncStream(bufferingPolicy: .bufferingNewest(1))
}
}

public extension RouterScope {

/// The lifecycle events of this router exposed as an async stream.
var lifecycleSequence: AsyncStream<RouterLifecycle> {
return lifecycle.asAsyncStream()
}
}

public extension Working {

/// The lifecycle of this worker exposed as an async stream.
var isStartedSequence: AsyncStream<Bool> {
return isStartedStream.asAsyncStream(bufferingPolicy: .bufferingNewest(1))
}
}

public extension LeakDetector {

/// The leak detection status exposed as an async stream.
var statusSequence: AsyncStream<LeakDetectionStatus> {
return status.asAsyncStream(bufferingPolicy: .bufferingNewest(1))
}
}
70 changes: 70 additions & 0 deletions RIBs/Classes/Concurrency/Observable+AsyncSequence.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// Copyright (c) 2017. Uber Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import RxSwift

public extension ObservableType {

/// Convert this observable into an async stream.
///
/// The underlying Rx subscription is disposed when the async stream is terminated.
func asAsyncStream(
bufferingPolicy: AsyncStream<Element>.Continuation.BufferingPolicy = .unbounded
) -> AsyncStream<Element> {
return AsyncStream(Element.self, bufferingPolicy: bufferingPolicy) { continuation in
let disposable = subscribe(
onNext: { element in
continuation.yield(element)
},
onError: { _ in
continuation.finish()
},
onCompleted: {
continuation.finish()
}
)

continuation.onTermination = { _ in
disposable.dispose()
}
}
}

/// Convert this observable into an async throwing stream.
///
/// The underlying Rx subscription is disposed when the async stream is terminated.
func asAsyncThrowingStream(
bufferingPolicy: AsyncThrowingStream<Element, Error>.Continuation.BufferingPolicy = .unbounded
) -> AsyncThrowingStream<Element, Error> {
return AsyncThrowingStream(Element.self, bufferingPolicy: bufferingPolicy) { continuation in
let disposable = subscribe(
onNext: { element in
continuation.yield(element)
},
onError: { error in
continuation.finish(throwing: error)
},
onCompleted: {
continuation.finish()
}
)

continuation.onTermination = { _ in
disposable.dispose()
}
}
}
}
Loading