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

[WIP] Converting to Swift 3 #45

Merged
merged 3 commits into from
Sep 18, 2016
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
65 changes: 28 additions & 37 deletions Action.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import RxCocoa
public typealias CocoaAction = Action<Void, Void>

/// Possible errors from invoking execute()
public enum ActionError: ErrorType {
case NotEnabled
case UnderlyingError(ErrorType)
public enum ActionError: Error {
case notEnabled
case underlyingError(Error)
}

/// TODO: Add some documentation.
public final class Action<Input, Element> {
public typealias WorkFactory = Input -> Observable<Element>
public typealias WorkFactory = (Input) -> Observable<Element>

public let _enabledIf: Observable<Bool>
public let workFactory: WorkFactory
Expand All @@ -23,83 +23,73 @@ public final class Action<Input, Element> {
/// All inputs are always appear in this subject even if the action is not enabled.
/// Thus, inputs count equals elements count + errors count.
public let inputs = PublishSubject<Input>()
private let _completed = PublishSubject<Void>()
fileprivate let _completed = PublishSubject<Void>()

/// Errors aggrevated from invocations of execute().
/// Delivered on whatever scheduler they were sent from.
public var errors: Observable<ActionError> {
return self._errors.asObservable()
}
private let _errors = PublishSubject<ActionError>()
fileprivate let _errors = PublishSubject<ActionError>()

/// Whether or not we're currently executing.
/// Delivered on whatever scheduler they were sent from.
public var elements: Observable<Element> {
return self._elements.asObservable()
}
private let _elements = PublishSubject<Element>()
fileprivate let _elements = PublishSubject<Element>()

/// Whether or not we're currently executing.
/// Always observed on MainScheduler.
public var executing: Observable<Bool> {
return self._executing.asObservable().observeOn(MainScheduler.instance)
}
private let _executing = Variable(false)
fileprivate let _executing = Variable(false)

/// Observables returned by the workFactory.
/// Useful for sending results back from work being completed
/// e.g. response from a network call.
public var executionObservables: Observable<Observable<Element>> {
return self._executionObservables.asObservable().observeOn(MainScheduler.instance)
}
private let _executionObservables = PublishSubject<Observable<Element>>()
fileprivate let _executionObservables = PublishSubject<Observable<Element>>()

/// Whether or not we're enabled. Note that this is a *computed* sequence
/// property based on enabledIf initializer and if we're currently executing.
/// Always observed on MainScheduler.
public var enabled: Observable<Bool> {
return _enabled.asObservable().observeOn(MainScheduler.instance)
}
public private(set) var _enabled = BehaviorSubject(value: true)
public fileprivate(set) var _enabled = BehaviorSubject(value: true)

private let executingQueue = dispatch_queue_create("com.ashfurrow.Action.executingQueue", DISPATCH_QUEUE_SERIAL)
private let disposeBag = DisposeBag()
fileprivate let executingQueue = DispatchQueue(label: "com.ashfurrow.Action.executingQueue", attributes: [])
fileprivate let disposeBag = DisposeBag()

public init<B: BooleanType>(enabledIf: Observable<B>, workFactory: WorkFactory) {
self._enabledIf = enabledIf.map { booleanType in
return booleanType.boolValue
}
public init(enabledIf: Observable<Bool> = Observable.just(true), workFactory: @escaping WorkFactory) {
self._enabledIf = enabledIf

self.workFactory = workFactory

Observable.combineLatest(self._enabledIf, self.executing) { (enabled, executing) -> Bool in
return enabled && !executing
}.bindTo(_enabled).addDisposableTo(disposeBag)

self.inputs
.subscribeNext { [weak self] input in
self?._execute(input)
}
.addDisposableTo(disposeBag)
self.inputs.subscribe(onNext: { [weak self] (input) in
self?._execute(input)
}).addDisposableTo(disposeBag)
}
}

// MARK: Convenience initializers.
public extension Action {

/// Always enabled.
public convenience init(workFactory: WorkFactory) {
self.init(enabledIf: .just(true), workFactory: workFactory)
}
}

// MARK: Execution!
public extension Action {

public func execute(input: Input) -> Observable<Element> {
@discardableResult
public func execute(_ input: Input) -> Observable<Element> {
let buffer = ReplaySubject<Element>.createUnbounded()
let error = errors
.flatMap { error -> Observable<Element> in
if case .UnderlyingError(let error) = error {
if case .underlyingError(let error) = error {
throw error
} else {
return Observable.empty()
Expand All @@ -118,7 +108,8 @@ public extension Action {
return buffer.asObservable()
}

private func _execute(input: Input) -> Observable<Element> {
@discardableResult
fileprivate func _execute(_ input: Input) -> Observable<Element> {

// Buffer from the work to a replay subject.
let buffer = ReplaySubject<Element>.createUnbounded()
Expand All @@ -134,7 +125,7 @@ public extension Action {

// Make sure we started executing and we're accidentally disabled.
guard startedExecuting else {
let error = ActionError.NotEnabled
let error = ActionError.notEnabled
self._errors.onNext(error)
buffer.onError(error)

Expand All @@ -153,7 +144,7 @@ public extension Action {
self?._elements.onNext(element)
},
onError: {[weak self] error in
self?._errors.onNext(ActionError.UnderlyingError(error))
self?._errors.onNext(ActionError.underlyingError(error))
},
onCompleted: {[weak self] in
self?._completed.onNext()
Expand All @@ -169,12 +160,12 @@ public extension Action {
}

private extension Action {
private func doLocked(closure: () -> Void) {
dispatch_sync(executingQueue, closure)
func doLocked(_ closure: () -> Void) {
executingQueue.sync(execute: closure)
}
}

internal extension BehaviorSubject where Element: BooleanLiteralConvertible {
internal extension BehaviorSubject where Element: ExpressibleByBooleanLiteral {
var valueOrFalse: Element {
guard let value = try? value() else { return false }

Expand Down
4 changes: 2 additions & 2 deletions Action.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 2.3;
SWIFT_VERSION = 3.0;
};
name = Debug;
};
Expand All @@ -312,7 +312,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.ashfurrow.Action;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 2.3;
SWIFT_VERSION = 3.0;
};
name = Release;
};
Expand Down
10 changes: 5 additions & 5 deletions AlertAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import RxCocoa

public extension UIAlertAction {

public static func Action(title: String?, style: UIAlertActionStyle) -> UIAlertAction {
public static func Action(_ title: String?, style: UIAlertActionStyle) -> UIAlertAction {
return UIAlertAction(title: title, style: style, handler: { action in
action.rx_action?.execute()
})
Expand Down Expand Up @@ -48,17 +48,17 @@ extension UIAlertAction {
MainScheduler.ensureExecutingOnScheduler()

switch event {
case .Next(let value):
self?.enabled = value
case .Error(let error):
case .next(let value):
self?.isEnabled = value
case .error(let error):
let error = "Binding error to UI: \(error)"
#if DEBUG
rxFatalError(error)
#else
print(error)
#endif
break
case .Completed:
case .completed:
break
}
}
Expand Down
2 changes: 1 addition & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "ReactiveX/RxSwift" ~> 2.0
github "ReactiveX/RxSwift" "develop"
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "ReactiveX/RxSwift" "2.6.0"
github "ReactiveX/RxSwift" "c99a49aa66ea830780bd3be1f11b3bac2b690c90"
Loading