Code Monkey home page Code Monkey logo

reactiveswift's Introduction

ReactiveSwift

Streams of values over time. Tailored for Swift.

Latest ReactiveSwift Documentation Join the ReactiveSwift Slack community.


Carthage compatible CocoaPods compatible SwiftPM compatible GitHub release Swift 5.1 platforms

🚄 Release Roadmap

Getting Started

Learn about the Core Reactive Primitives in ReactiveSwift, and Basic Operators available offered by these primitives.

Extended modules

Module Repository Description
ReactiveCocoa ReactiveCocoa/ReactiveCocoa

Extend Cocoa frameworks and Objective-C runtime APIs with ReactiveSwift bindings and extensions.

Loop ReactiveCocoa/Loop

Composable unidirectional data flow with ReactiveSwift.

ReactiveSwift Composable Architecture trading-point/reactiveswift-composable-architecture

The Pointfree Composable Architecture using ReactiveSwift instead of Combine.

What is ReactiveSwift in a nutshell?

ReactiveSwift offers composable, declarative and flexible primitives that are built around the grand concept of streams of values over time.

These primitives can be used to uniformly represent common Cocoa and generic programming patterns that are fundamentally an act of observation, e.g. delegate pattern, callback closures, notifications, control actions, responder chain events, futures/promises and key-value observing (KVO).

Because all of these different mechanisms can be represented in the same way, it’s easy to declaratively compose them together, with less spaghetti code and state to bridge the gap.

References

  1. API Reference

  2. API Contracts

    Contracts of the ReactiveSwift primitives, Best Practices with ReactiveSwift, and Guidelines on implementing custom operators.

  3. Debugging Techniques

  4. RxSwift Migration Cheatsheet

Installation

ReactiveSwift supports macOS 10.13+, iOS 11.0+, watchOS 4.0+, tvOS 11.0+ and Linux.

Carthage

If you use Carthage to manage your dependencies, simply add ReactiveSwift to your Cartfile:

github "ReactiveCocoa/ReactiveSwift" ~> 6.1

If you use Carthage to build your dependencies, make sure you have added ReactiveSwift.framework to the "Linked Frameworks and Libraries" section of your target, and have included them in your Carthage framework copying build phase.

CocoaPods

If you use CocoaPods to manage your dependencies, simply add ReactiveSwift to your Podfile:

pod 'ReactiveSwift', '~> 6.1'

Swift Package Manager

If you use Swift Package Manager, simply add ReactiveSwift as a dependency of your package in Package.swift:

.package(url: "https://github.com/ReactiveCocoa/ReactiveSwift.git", from: "6.1.0")

Git submodule

  1. Add the ReactiveSwift repository as a submodule of your application’s repository.
  2. Run git submodule update --init --recursive from within the ReactiveCocoa folder.
  3. Drag and drop ReactiveSwift.xcodeproj into your application’s Xcode project or workspace.
  4. On the “General” tab of your application target’s settings, add ReactiveSwift.framework to the “Embedded Binaries” section.
  5. If your application target does not contain Swift code at all, you should also set the EMBEDDED_CONTENT_CONTAINS_SWIFT build setting to “Yes”.

Playground

We also provide a Playground, so you can get used to ReactiveCocoa's operators. In order to start using it:

  1. Clone the ReactiveSwift repository.
  2. Retrieve the project dependencies using one of the following terminal commands from the ReactiveSwift project root directory:
    • git submodule update --init --recursive OR, if you have Carthage installed
    • carthage checkout
  3. Open ReactiveSwift.xcworkspace
  4. Build ReactiveSwift-macOS scheme
  5. Finally open the ReactiveSwift.playground
  6. Choose View > Show Debug Area

Have a question?

If you need any help, please visit our GitHub issues or Stack Overflow. Feel free to file an issue if you do not manage to find any solution from the archives.

Release Roadmap

Current Stable Release:
GitHub release

Plan of Record

ABI stability release

ReactiveSwift has no plan to declare ABI and module stability at the moment. It will continue to be offered as a source only dependency for the foreseeable future.

reactiveswift's People

Contributors

335g avatar adlai-holler avatar alanjrogers avatar almassapargali avatar andersio avatar bigboybad avatar brow avatar ikesyo avatar javisoto avatar jlawton avatar jonsterling avatar joshaber avatar joshvera avatar jspahrsummers avatar kastiglione avatar kkazuo avatar lawrencelomax avatar lbrndnr avatar liscio avatar mdiep avatar mluisbrown avatar nachosoto avatar neilpa avatar olejnjak avatar qata avatar ra1028 avatar robrix avatar ruiaaperes avatar sharplet avatar tomj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reactiveswift's Issues

Would you consider using warnings instead of errors for deprecated usages?

Hi,
Currently working on a swift 3 migration - when you go to version 1 of RAC5, would you please consider using warnings instead of compile-time errors for deprecated usage such as startWithNext ?
At least until, say, version 5.1 ?
It would make the migration of large codebases to swift 3 much easier.
Thanks a lot.

Un-genericize ScopedDisposable

I'm migrating a project from RAC to ReactiveSwift and one of the changes that boggles me a bit is why ScopedDisposable was turned into a generic, I'm just wondering the real world use case where anyone actually needs to explicitly ensure a ScopedDisposable only contains a disposable of X type?

My use case is maybe the outlier but I use ScopeDisposable to make for easy interrupts:

var requestDisposable: ScopedDisposable? = nil
...
func run() {
   self.requestDisposable = producer.start()
}

It's frustrating to have to now double wrap these ScopedDisposable(AnyDisposable(producer.start())) to get the same effect and I'm just struggling to see the use case where anyone is going to grab the innerDisposable and care what type it is?

Question about Signal leak / memory managment

I'm seeing some leaks with ReactiveSwift. I'm using the alpha so wondering if this is known, or if I'm missing something.

Using ReactiveCocoa (5.0.0-alpha.3)
Using ReactiveSwift (1.0.0-alpha.3)
Using Result (3.0.0)

Why does this code leak a Signal?

screen shot 2016-11-10 at 11 45 50 am

 var disposable = CompositeDisposable()  
  deinit {
       disposable.dispose()
   }

   @IBAction func load(_ sender: Any) {
       disposable += SignalProducer<Void, NSError> { sink, disposable in
           disposable.add {
               print("dispose")
           }
           
           sink.send(value: ())
           sink.sendCompleted()
       }.startWithCompleted {
           print("completed")
       }
   }

Also a similar example with a Disposable

       var disposable: Disposable?
       deinit {
            disposable?.dispose()
        }

       disposable = SignalProducer<Void, NSError> { sink, disposable in
            sink.send(value: ())
            sink.sendCompleted()
        }.startWithCompleted {
            print("completed")
        }

can't find the methods

I use Carthage,but only has two files.

ReactiveSwift-Swift.h
@import Foundation;
@interface NSNotificationCenter (SWIFT_EXTENSION(ReactiveSwift))
@EnD
@interface NSURLSession (SWIFT_EXTENSION(ReactiveSwift))
@EnD

ReactiveSwift.h

//! Project version number for ReactiveSwift.
FOUNDATION_EXPORT double ReactiveSwiftVersionNumber;

//! Project version string for ReactiveSwift.
FOUNDATION_EXPORT const unsigned char ReactiveSwiftVersionString[];

send Action.events before sending events on the applied producer?

Currently, a producer returned from Action.apply() first sends an event on its observer, then it sends the same event on the action's .events signal. I wonder if this is by design and if there are any arguments against reversing the order in which the events are sent.

Consider the following example:

class RegisterViewModel {
    lazy private(set) var isRegistered: Property<Bool> = {
          Property(initial: false, then: self.register.events
                 .filter { $0 == .completed }
                 .take(first: 1)
                 .map { _ in true })
    }()
    lazy private(set) var login: Action<(),(),NoError> = {
        Action(enabledIf: self.isRegistered) {
            .empty //login request
        }
    }()
    lazy private(set) var registerAndLogin: Action<(),(),NoError> = Action { [weak self] in
         guard let `self` = self else { assertionFailure(); return .empty }
         return SignalProducer<(),NoError>.empty //register request
             .then(self.login.apply()
                   .flatMapError { _ in
                      assertionFailure("we are registered by now, so login should be enabled")
                      return  SignalProducer { observer, _ in  observer.sendInterrupted() } 
                   }
             )
    }
}

//...
//clientCode
viewModel.registerAndLogin.apply().start()

The above code fails on the assertion, because login is still disabled by the time we start the producer from login.apply(). This can be remedied by making isRegistered a MutableProperty and setting its value from .on(completed:) of the register request. However, this approach includes sideeffects and reduces the declarativeness of the code. Reversing the order in which events are sent would fix this code.

Let me know what you think about my example and also any counter examples you can think of. I wonder if it would cause trouble for other people and the way they use Actions. Thanks.

NOTE: I just want to start a discussion, I will gladly create a PR in the future.

Strange build warnings in Xcode 8

I'm probably doing something wrong, but I keep getting build warnings such as "/Path/To/ReactiveCocoa-master/Carthage/Checkouts/ReactiveSwift/ReactiveSwift.xcodeproj The file “iOS-Framework.xcconfig” couldn’t be opened. (/Path/To/ReactiveCocoa-master/Carthage/Checkouts/ReactiveSwift/Carthage/Checkouts/xcconfigs/xcconfigs/iOS/iOS-Framework.xcconfig)"

Before, it was saying that it couldn't be opened because it couldn't resolve the path. I've since resolved those paths in the navigator, and now it just says it doesn't want to open them. I'm getting these in iOS-Framework.xcconfig, Debug.xcconfig, and Release.xcconfig.

Anyone know more than me about these?

Question regarding memory/resource management

I'm reading through the source code, and it looks like the only way to break the retain cycle for a Signal is to remove all of it's observers. So it seems strange to me that the method observe would be decorated with @discardableResult. It seems as though if any one of the returned ActionDisposable results was discarded, a retain cycle would be guaranteed (since there would be no way to remove it from the Bag of observers). I just wanted to ask if someone might be able to help me understand what's going on here.

Synthetic benchmark on a few pending PRs combined.

Benchmark Source
#106 + #112 + #107 + #113

Seven scenarios

  1. Send 10000 events to 1 observer of a plain signal.
  2. Send 10000 events to 8 observers of a plain signal.
  3. Send 10000 events to 1 observer of a produced signal from a 5-times-transformed producer.
  4. Send 10000 events to 2 observer of a produced signal from a 5-times-transformed producer.
  5. Start, send one event to and dispose a 5-times-transformed producer for 10000 times.
  6. Start a 5-times-transformed producer for 10000 times.
  7. Start and dispose a 5-times-transformed producer for 10000 times.

TL;DR

  1. A non-trivial regression in sending events. (as a result of the interruption handling fix in #112). Transformed producer would amplify the effect.
  2. But if there is enough work done for each event (e.g. invoking eight empty observers), the regression would be outweighed.
  3. Single-digit-percentage improvements in the SignalProducer starting & disposing performance.
  4. When it comes to a common pattern for a scrolling collection views (rebinding reusable cells to different producers, represented by scenario 5), there is a minor net improvement.

Further optimization

  1. Synchronise Signal.observers using read-copy-update. This removes a lock from the critical path of a Signal, at a cost of more expensive observer insertion.
// -Owholemodule
// `signal-state-patch` + `disposable-lockfree` + `lift-fix` + `minor-signal-refactor`
One Observer:    0.026 12%
Eight Observers: 0.042 8%
Producer:        0.326 3%
Producer 2 obv:  0.632 2%
Producer Mixed:  2.038 2%
Start Producer:  1.582 2%
Start & Dispose: 1.581 2%

// `master`
One Observer:    0.018 10%
Eight Observers: 0.053 5%
Producer:        0.223 3%
Producer 2 obv:  0.422 2%
Producer Mixed:  2.090 2%
Start Producer:  1.663 2%
Start & Dispose: 1.683 2%

// Speedup
One Observer:    0.692x ***
Eight Observers: 1.262x
Producer:        0.684x ***
Producer 2 obv:  0.668x ***
Producer Mixed:  1.025x
Start Producer:  1.051x
Start & Dispose: 1.065x

NotificationCenter extension.

With the new signal lifetime semantics, we can safely express it as a Signal<Notification, NoError>. Any thoughts on changing it?

Replaying success but not errors

In my current project I have a 3-step process where the output of each signal producer becomes the input of the next. This process should externally behave like a single atomic operation, essentially a state machine. If any step fails, then retrying the process should start again from the failed step, but use the cached output from previously successful steps.

I'd really like to use replayLazily() for this, but can't because it will also replay failures. I haven't been able to work out a way to compose existing operators to get the semantics I'm looking for.

Is this something that could use a new operator? Is there a way I can get this with existing operators?

I kind of want replayLazily, only failure events should invalidate the cache instead of being replayed.

Inner signal not "interrupted" on outer signal disposal

(Ref: ReactiveCocoa/ReactiveCocoa#2959)

Below gist (SignalProducerSpec's "disposal") passed the test in ReactiveCocoa v4.2.2 but not in current ReactiveSwift 1.0.0-alpha.3.

https://gist.github.com/inamiy/925644779adbfca621ea716770d3865e

It seems innerSignal.on().observe() is disposed first before on() receives .interrupted.
This could be a NEW correct behavior as discussed in ReactiveCocoa/ReactiveCocoa#3075, but if I want to know the interruption of innerSignal, what should I do?

Modeling `private(set) var` behavior using properties

Hi, I'm trying to figure out the best way to model behavior similar to

class Object {
  private(set) var property: Type

  func sideEffects() {
    property = newValue()
  }
}

when a function has side effects changing the value of the property.
The main goal of this exercise is to expose a readonly property that is modified from within the object using ReactiveSwift. My current setup looks like this:

class Object {
  let property: MutableProperty<Type>
  func sideEffects() {
    property.value = newValue()
  }
}

but that also allows changing property value from the outside of the object, e.g. object.property.value = newUnexpectedValue() which is what I want to avoid.

Thank you!

How to program UI now, without unsafeCocoaAction?

It seems that unsafeCocoaAction was removed for the obvious reasons, but now it is unclear what to do with the programming of UI. Is there other sub-project with an extension of Action with unsafeCocoaAction that should be used for this purpose?

Getting infinite loop on ScopedDisposable.init

Hi, i'm getting an infinite loop when doing the following:

let disposable = someProducer.startWithNext ...
let scoped = ScopedDisposable(disposable)

It loops on ScopedDisposable's extensions' init(_ disposable: Disposable), which is here: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/Disposable.swift#L230

This init has the same signature as ScopedDisposable's non-extension init (i'm not sure how this compiles without error), and when the extension calls self.init it calls the extension init instead of the non-extension init, which causes infinite recursion.

Recommend changing the signature of the extension init to have a named argument, or even remove it? I'm not sure what the need for the extension init is, given that the normal init is fully generic already.

For reference for anyone else having this issue, you can workaround for now with: let scoped = ScopedDisposable(AnyDisposable(disposable))

Thanks

Make Reactive.init(_ base:) public.

It would be nice to be able to add custom reactive extensions to types that are outside the scope of ReactiveSwift. The only way to achieve that currently is to declare an accessible initializer in the consuming module that can assign base since the current initializer is marked as fileprivate.

Best way to expire SignalProducer that is lazily replayed?

I have a very simple object that performs network requests and lazily replays them. What I would love to do is expire the underlying cache/buffer after a certain time so it will perform the work again on next start. Is there a RACy way of doing this? Currently I manage the expiry using state in my object by comparing to a stored date and recreating the lazily replayed SignalProducer again, but would love to do away with this state entirely if possible!

Thanks

Kill Actions as BindingTargets?

@andersio and I (with some @sharplet thrown in) had a discussion recently about the ability to bind to an Action in ReactiveSwift.

My distaste for this feature started when I saw the following code in a test case:

presented.rac.dismissAnimated <~ Property(value: false)

Frankly I don't feel that it's a good idea to enable this sort of use through the API, and I think that the ability to bind an Action to an arbitrary signal seems like a solution looking for a problem.

I'm not against the pattern of an <~ operator showing up in the UI code, but rather I think that it should be limited to binding only "action outlets" to "action inlets", so to speak. To paraphrase a comment from @andersio: "it should work more like IB".

It's possible that I'm in need of some education and examples of where this is useful in practice. I don't use actions all that much in practice outside of the UI case, where controls are being bridged to actions. And in all these cases, I think that my uses may be a bit of a stretch.

So, I say we either pull it, or come with some compelling real-world cases outside of UI code where this holds value.

Use cases / Examples

Would it be possible to provide some sort of guidance on when/how to use various aspects of the framework?

Examples on Action, Lifetime etc would be helpful a lot for someone who is at the process of learning the framework

Replace TimeInterval in APIs with DispatchTimeInterval

Reasoning:

The current TimeInterval APIs are fragile and dangerous for many reasons:

  • We always end up converting to milliseconds, but if the input value is too high, it will overflow (#31 fixed this to some extent by reducing the likelihood, but it's still possible).
  • The API is not strongly typed: there is no guarantee or way to enforce users sending the right magnitude (seconds versus milliseconds, etc), other than a weak comment.

But with Swift 3 we can do better: using DispatchTimeInterval solves all these problems :)

Process:

So I'm proposing deprecating current APIs that use TimeInterval to point users to use new DispatchTimeInterval versions.

Odd behaviour on `on`

I have the following snippet of code:

	SignalProducer<Int, NSError>(value: 15)
			.on(starting: {
				print("starting")
			})
			.on(started: {
				print("started")
			})
			.on(value: { _ in
				print("value")
			})
			.on(failed: { _ in
				print("failed")
			})
			.on(completed: {
				print("completed")
			})
			.start()

That prints the following results on the console

starting
value
completed
started

Shouldn't the output be

starting
started
value
completed

instead?

The same thing happens when the producer fails.

rac_textSignal() does not seem to work

I'm using Xcode version 8.1, Reactive Swift pre-release version 1.0.0 alpha 3, Carthage version 0.11.0, and I don't think this matters but macOS Sierra version 10.12.1.

I have successfully added ReactiveSwift.framework and Result.framework to my project as specified in the Getting Started section. However, when following the first code example on the README of this project, that is:

let searchStrings = textField.rac_textSignal()
    .toSignalProducer()
    .map { text in text as! String }

I am getting an error on the first line. The error reads:

Value of type 'UITextField' has no member 'rac_textSignal'

When right-clicking on rac_textSignal(), I am not taken anywhere and thus, I assume that this function has been removed. My implementation is as follows:

let textField = UITextField()

let textSignal = textField.rac_textSignal() {
  .toSignalProducer()
  .map { text in text as! String }
}

It concerns me that the first example in the README of this project doesn't work so I am hoping that I am just doing something wrong here. Any help would be greatly appreciated, thanks!

Checking out ReactiveSwift takes longer than expected

I'm dependent on ReactiveSwift through Moya, and I've noticed that carthage updates hang on Checking out ReactiveSwift for much longer than expected (1 minute+), it seems as if it's doing something with git-remote-https? Maybe checking out submodules or something?

I wanted to bring this to the maintainer's attention at the very least, and I was wondering if the checkout process for ReactiveSwift could be optimized for the next release?

Thanks!

Deadlock

Hi,

I am running into a deadlock issue. I’m not entirely sure why it is happening yet. Here is my setup (dumbed down as this is the only part breaking, can provide more if needed).

class ActionController {
	var delay: NSTimeInterval = 0.0
	let undoable = MutableProperty<Bool>(false)

	func start() {
		if delay > 0.0 {
			undoable.value = true
		}
		
		// More stuff
		// Eventually will commit once delay is hit
		// Can provide more if needed.
	}
	
	func undo() {
		guard undoable.value else {
			return
		}
		
		// Do some stuff
		
		/***** DEADLOCK HAPPENS ON LINE BELOW *******/
		undoable.value = false
		/********************************************/
	}
}
class MyViewModel {

	private let currentAction = MutableProperty<ActionController?>(nil)
	
	let undoEnabled = MutableProperty<Bool>(false)
	
	init() {
		undoEnabled <~ currentAction.producer
			.flatMap(.Concat) { action in
				guard let action = action else {
					return SignalProducer<Bool, NoError>(value: false)
				}
				
				return action.undoable.producer
			}
			.skipRepeats()
	}
	
	func commitAction() {
		let action = ActionController()
		action.delay = 10
		action.start()
	}
	
	func undoCurrentAction() {
		currentAction.value?.undo()
	}

	 func onActionCompleted() {
		// Method gets called when ActionController completes    
		currentAction.value = nil
	}

}

Both commitAction() and undoCurrentAction() get called from a UIViewController that the MyViewModel is powering and has subscribers to undoEnabled.

Example for testing:

        viewModel.undoEnabled.producer.startWithNext { [unowned self] enabled in
            if enabled {
                self.viewModel.undoCurrentAction()
            }
        }

The deadlock seems to be happening when calling undo and setting undoable’s value to false.

Simple example of UI bindings

Hey there!

I've been struggling for the past few hours to figure this out. I have 2 UITextFields(username and password) and 1 UIButton to login. How do I make it so that if textfields are empty, the button is disabled. And if either 1 of the textfields has some text in it, the button is enabled.

This is as far as I've got:

let textFieldSignals = Signal.merge(
    textFieldUserName.reactive.continuousTextValues,
    textFieldPassword.reactive.continuousTextValues
)

SignalProducer(signal: textFieldSignals).start() { [weak self] result in
    guard let this = self else { return }
    let isEnabled = this.textFieldUserName.text!.characters.count > 0 && this.textFieldPassword.text!.characters.count > 0
    this.navigationItem.rightBarButtonItem?.isEnabled = isEnabled
}

However, I don't get a signal right away in the block. It only happens when textfields values are changed.

I'm trying to work with the latest 5.0.x version of the ReactiveSwift.

Bring back the Subject?

A while ago, @andersio shared a gist with me that implemented a replacement for RACSubject. In essence, it just wraps the tuple returned by Signal<T,E>.pipe(), but works much nicer in practice.

Here's the gist containing his implementation: https://gist.github.com/andersio/3b4889176d6632bbb637ffd177795815/

Is there a reason that we don't offer this useful construct that we have available in ReactiveObjC? I've never loved the name, personally—calling it a Pipe would make far more sense—but since I integrated this code into a project I'm working on, life got a lot easier.

Thoughts?

Deprecated combineLatest not converted to Swift 3

@available(*, unavailable, renamed:"SignalProducer.combineLatest")
public func combineLatest<S: Sequence, Value, Error>(producers: S) -> SignalProducer<[Value], Error> where S.Iterator.Element == SignalProducer<Value, Error> { fatalError() }

Should have (_ producers: S) instead of (producers: S), otherwise instead of 'combineLatest' has been renamed to 'SignalProducer.combineLatest' Xcode shows confusing Argument labels '(_:)' do not match any available overloads.

Probably there are other cases like this.

Best practice to replace `CocoaAction`?

I noticed that CocoaAction was removed from ReactiveSwift. I'm trying to find a way to replace this functionality:

signUpButton.addTarget(viewModel.signUpButtonAction, action: CocoaAction.selector, for: .touchUpInside)

I'm thinking it'd be best to just handle in an @IBAction and call apply(_:).start() on the action, but wanted to ask to see if there was a prescribed way to achieve it.

Array of Signal<Any?, NoError>. Array of different Value types

I have some signals. Some from a textfield, and others I have coerced from valuesForKeyPath.

let sig1: Signal<Any?, NoError> = specialBtn.reactive.values(forKeyPath: "isSelected").startSignal()
let sig2: Signal<String, NoError> = textField.reactive.continuousTextValues

wherestartSignal is really just the internal func startAndRetrieveSignal():

extension SignalProducer {
  func startSignal() -> Signal<Value, Error> {
    var result: Signal<Value, Error>!
    startWithSignal { signal, _ in
      result = signal
    }
    
    return result
  }
}

I try to put the above 2 signals into an array, but it complains about the types matching. I know String conforms to Any though.

let sigs: [Signal<Any?, NoError>] = [sig1, sig2]

Cannot convert value of type 'Signal<String, NoError>' to expected element type 'Signal<Optional<Any>, NoError>'

How should I aggregate signals that have different types?

Rename `Event.failed` to `Event.error`

This is just a matter of taste, but observer.sendFailed() has been renamed to observer.send(error:), so I think Event.failed should be renamed to Event.error as well (and also other methods like on(failed:)).

Or, renaming observer.send(error:) to observer.send(failed:) improves consistency too.
(I prefer send(error:) though)

[Pitch, Take 2] Revisiting `observe*` and `start*`

» Sandbox

TL;DR

"Extensible enum" based API.

producer.start(.all) { event in }
producer.start(.values) { value in }
producer.start(.results) { result in }
producer.start(.completed) {}
producer.start(.interrupted) {}
producer.start(.failed) { error in }

// default value: .all
producer.start { event in }

Caveats

  1. No autocompletion for the extensible enum.
  2. Three more characters to observe a Signal for With being wiped from SignalProducer.start.

Well probably keeping things as is would be better, heh. But similar tricks using static members can be considered too, e.g.

// Static method on `Observer`.
producer.start(.values { value in })

How would existing codes be affected?

Unless the default value is anything but .all, the existing observe* and start* can be marked as deprecated and gracefully fade out without name clashes.

Is QueueScheduler still necessary?

Just wonder if we can conform DispatchQueue (or even OperationQueue) to the scheduler protocols directly.

These loosen the requirement of serial execution in the Scheduler contract. But then DispatchQueue is by default serial, and the users should have been aware of any issue should they own the queue.

UIScheduler.

Should it be made a singleton instead? Say UIScheduler.shared.

Error in switching event

searchResults.observe { event in
    switch event {
    case let .value(results):
        print("Search results: \(results)")

    case let .error(error):
        print("Search error: \(error)")

    case .completed, .interrupted:
        break
    }
}

Notice the case let .error have been change to case let .failed, please update the README.md.

Action: What is it good for

This has all spawned from @sharplet's #22.

Action is based on RAC 2's RACCommand, which is itself based on ReactiveUI's ReactiveCommand. The model was originally created to model UI actions—which are serial and occur on the main thread. But Action was meant to be decoupled from this dependency. The RAC 3 changelog says:

Unlike commands, actions are not bound to or dependent upon the main thread, making it easier to reason about when they can be executed and when they will generate notifications.

I think this is incomplete: Actions aren't truly thread safe.

The enabledIf property is one of the key features of actions. However, that property is inherently racey with the body of the action itself. If the property is altered on a different thread/scheduler, it can switch to false after Action checks the value but before the body of the action executes. You can't trust that the world hasn't changed in the execute closure.

RACCommand and ReactiveUI's ReactiveCommand don't suffer from this issue because they're tied to the main thread. Altering the world on a background thread is prohibited.

I think we should do something to address this: either explicitly document this limitation or modify Action so that it's truly thread safe and reactive.

One we could achieve the later would be to base Action's inputs on signals. Currently, Actions often read external properties (i.e. state), rather than relying solely on their input. In the typical login example, the login action would read the username and password properties directly from the view model:

class LoginViewModel {
    let username = Property<String>()
    let password = Property<String>()
    let action: Action<(), (), LoginError>

    init() {
        let enabled = Property
            .combineLatest(username, password)
            .map { !$0.isEmpty && !$1.isEmpty }
        action = Action(enabledIf: enabled) { _ in
          let username = self.username // These could be empty, even though they should be disabled
          let password = self.password // The perils of reading state
          
        }
    }
}

// elsewhere
loginViewModel.action.apply(())

But actions could be restructured like so:


class LoginViewModel {
    let username = Property<String>()
    let password = Property<String>()
    let action: Action<(), LoginError> // input type isn't part of the type

    init() {
        let input = Property.combineLatest(username, password)
        action = Action(input, enabledIf: { !$0.isEmpty && !$1.isEmpty }) { (username, password) in
          …
        }
    }
}


// elsewhere
loginViewModel.action.apply()

This would have a few benefits that I see:

  1. Value consistency between enabledIf and execute
  2. apply() can easily be bound to a trigger signal
  3. Actions become usable only in the reactive model

But this does require (1) removing the input type from Action and (2) removing the input parameter from apply(). (2) could be especially problematic, depending on how people are currently using Action. But in the standard bind-this-action-to-a-button case, this model seems like it would be simpler.

I'm curious what people think. @ReactiveCocoa/reactiveswift

Discussion: Swift 3 function names/prepositions + trailing closure syntax

Pre-Swift 3 a func like skipWhile(_ condition: () -> Bool) would be, well.. that. And thus usage would be .skipWhile { ... } – terse yet clear at call site.

Now it's skip(while condition: () -> Bool) per Swift 3 naming practices, rendering usage either .skip(while: { .. }) (losing crispness of trailing closure) or .skip { .. } (losing info). The latter wouldn't even be usable if a skip(until condition: () -> Bool) func were introduced because of the ambiguity.

Is anyone else concerned?

Replace `<~` operator with `bind(with:)`

Would it make sense to replace the <~ operator with bind(with:), then the api would become

mutableProperty.bind(with: someProducer)

That way the functionality will be discoverable from within Xcode and the user won't have to keep in mind that an operator exists.

CocoaPods Support?

Good morning! Lovely library, I see that ReactiveCocoa's new Swift 3 work depends on this project, neat. Would you consider adding CocoaPods support to ReactiveSwift? I would be happy to send a pull request, but wanted to check first. Thanks!

DynamicProperty not exist

Hi,

Does the DynamicProperty be removed? My Xcode shows Use of unresolved identifier 'DynamicProperty' for all lines with DynamicProperty.

I wonder how to observe the value change and bind with view model now? Thanks

Deadlock in binding to a MutableProperty

I'm seeing a deadlock in a binding from a signal to a mutable property. Here's the relevant part of the stack trace:

image

(2) and (19) are the same signal, hitting the recursive send. Here's the current implementation of <~:

	public static func <~ <Source: SignalProtocol>(target: Self, signal: Source) -> Disposable? where Source.Value == Value, Source.Error == NoError {
		return signal
			.take(during: target.lifetime)
			.observeValues { [weak target] value in
				target?.consume(value)
			}
	}

I think what's happening is:

  1. Signal at (19) sends a value
  2. The observeValues block is called with the value
  3. Upon reading target, which has gone out of scope, the target's deinit gets called
  4. This triggers lifetime.ended to complete
  5. Because of take(during:), the signal immediately tries to complete (lifetime has ended)
  6. Deadlock, because the same signal is in the middle of sending a value

Unfortunately I haven't been able to write a test case that reproduces this.

The reason and use cases behind special treatment for .interrupted event.

Hi, recently I'm digging around the sources and found these comments in Signal.swift

// Normally we disallow recursive events, but `interrupted` is
// kind of a special snowflake, since it can inadvertently be
// sent by downstream consumers.
//
// So we'll flag Interrupted events specially, and if it
// happened to occur while we're sending something else,  we'll
// wait to deliver it.

I understood that .interrupted is special and doing this prevents undesired deadlocks under certain conditions.

However, these certain conditions, aka when it can inadvertently be sent by downstream consumers, still remains vague to me.

Can you please provide some actual scenes or explain these comments a little further to help clarifying?

Sorry if the question is naive or the answer to it is obvious.

Thanks.

Release?

Super excited about this project! I was wondering when you're thinking of tagging a release, even an prerelease version. Cheers!

(SWIFT_VERSION) to Swift 3

“Use Legacy Swift Language Version” (SWIFT_VERSION) is required to be configured correctly for targets which use Swift. Use the [Edit > Convert > To Current Swift Syntax…] menu to choose a Swift version or use the Build Settings editor to configure the build setting directly.

After converting to Swift 3 Build Failed and additional 58 errors are coming see below image.
screen shot 2016-11-29 at 9 53 43 am

Also mention is there any thing to import after pod install to use this library.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.