Git Product home page Git Product logo

reactiveviewmodel's Introduction

ReactiveCocoa

Reactive extensions to Cocoa frameworks, built on top of ReactiveSwift.

Join the ReactiveSwift Slack community.


Carthage compatible CocoaPods compatible SwiftPM compatible GitHub release Swift 5.1 platforms

⚠️ Looking for the Objective-C API?

🎉 Migrating from RAC 4.x?

🚄 Release Roadmap

What is ReactiveSwift?

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.

For more information about the core primitives, see ReactiveSwift.

What is ReactiveCocoa?

ReactiveCocoa wraps various aspects of Cocoa frameworks with the declarative ReactiveSwift primitives.

  1. UI Bindings

    UI components expose BindingTargets, which accept bindings from any kind of streams of values via the <~ operator.

    // Bind the `name` property of `person` to the text value of an `UILabel`.
    nameLabel.reactive.text <~ person.name

    Note: You'll need to import ReactiveSwift as well to make use of the <~ operator.

  2. Controls and User Interactions

    Interactive UI components expose Signals for control events and updates in the control value upon user interactions.

    A selected set of controls provide a convenience, expressive binding API for Actions.

    // Update `allowsCookies` whenever the toggle is flipped.
    preferences.allowsCookies <~ toggle.reactive.isOnValues
    
    // Compute live character counts from the continuous stream of user initiated
    // changes in the text.
    textField.reactive.continuousTextValues.map { $0.characters.count }
    
    // Trigger `commit` whenever the button is pressed.
    button.reactive.pressed = CocoaAction(viewModel.commit)
  3. Declarative Objective-C Dynamism

    Create signals that are sourced by intercepting Objective-C objects, e.g. method call interception and object deinitialization.

    // Notify after every time `viewWillAppear(_:)` is called.
    let appearing = viewController.reactive.trigger(for: #selector(UIViewController.viewWillAppear(_:)))
    
    // Observe the lifetime of `object`.
    object.reactive.lifetime.ended.observeCompleted(doCleanup)
  4. Expressive, Safe Key Path Observation

    Establish key-value observations in the form of SignalProducers and DynamicPropertys, and enjoy the inherited composability.

    // A producer that sends the current value of `keyPath`, followed by
    // subsequent changes.
    //
    // Terminate the KVO observation if the lifetime of `self` ends.
    let producer = object.reactive.producer(forKeyPath: #keyPath(key))
    	.take(during: self.reactive.lifetime)
    
    // A parameterized property that represents the supplied key path of the
    // wrapped object. It holds a weak reference to the wrapped object.
    let property = DynamicProperty<String>(object: person,
                                           keyPath: #keyPath(person.name))

But there are still more to be discovered and introduced. Read our in-code documentations and release notes to find out more.

Getting started

ReactiveCocoa supports macOS 10.9+, iOS 8.0+, watchOS 2.0+, and tvOS 9.0+.

Carthage

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

github "ReactiveCocoa/ReactiveCocoa" ~> 10.1

If you use Carthage to build your dependencies, make sure you have added ReactiveCocoa.framework and 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 ReactiveCocoa to your Podfile:

pod 'ReactiveCocoa', '~> 10.1'

Swift Package Manager

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

.package(url: "https://github.com/ReactiveCocoa/ReactiveCocoa.git", branch: "master")

Git submodule

  1. Add the ReactiveCocoa 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 ReactiveCocoa.xcodeproj and Carthage/Checkouts/ReactiveSwift/ReactiveSwift.xcodeproj into your application’s Xcode project or workspace.
  4. On the “General” tab of your application target’s settings, add ReactiveCocoa.framework and 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”.

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

In Development

Plan of Record

ABI stability release

ReactiveCocoa is expected to declare library ABI stability when Swift rolls out resilience support in Swift 5. Until then, ReactiveCocoa will incrementally adopt new language features.

reactiveviewmodel's People

Contributors

bigboybad avatar esttorhe avatar joshaber avatar joshvera avatar jspahrsummers avatar kylef avatar mdiep avatar robrix avatar vox-humana 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

reactiveviewmodel's Issues

The relationship of the view-model and data-model in MVVM, especially with regard to collections

I know this isn't necessarily a RAC question, so please close it if you don't like these types of questions here.

I was having a conversation with @ashfurrow exploring the relationship of the view model and the (data) model. Namely I was wondering if in a situation where you were on an 'edit' screen, whether the view model was always a proxy between the view and the model.

He does proxy/shadow his non-collection properties with RACChannel which makes sense for validation, etc. The more complicated situation in my mind is any collection properties on the model. Is it worth the headache of maintaining an entirely separate collection on the view model? If not, do you expose the model itself? Some of it's properties? Ash solves for this by using methods that internally access the set on the model. I'm thinking that exposing the set as an immutable read-only property on the view-model might make sense.

Trying to read about this from the .Net world. Some people seem to say you can expose the model or some of it's properties where doing otherwise would just be overhead and no transformation is necessary when accessing.

Would love to hear thoughts on this.

Feedback on MVVM-ification of an app

I've been playing around with MVVM and RAC for a little while now and have been updating an old project to MVVM (RAC will come later). I want to make sure I'm understanding how all the pieces fit together. Does anybody have any feedback on that pull?

Thanks in advance.

didBecomeInactiveSignal send value before viewDidLoad,is this right?

- (RACSignal *)didBecomeInactiveSignal {
    if (_didBecomeInactiveSignal == nil) {
        @weakify(self);

        _didBecomeInactiveSignal = [[[RACObserve(self, active)
            filter:^ BOOL (NSNumber *active) {
                NSLog(@"active:%@",active);
                return !active.boolValue;
            }]
            map:^(id _) {
                @strongify(self);
                return self;
            }]
            setNameWithFormat:@"%@ -didBecomeInactiveSignal", self];
    }

    return _didBecomeInactiveSignal;
}
  //in  myviewmodel
-(id)init{
//---
    [self.didBecomeInactiveSignal subscribeNext:^(id x){
        NSLog(@"didBecomeInactiveSignal");
    }];
//---
//in myviewcontroller that related with myviewmodel
- (void)viewDidLoad
{
   NSLog(@"did load");
}

output:
2014-11-04 23:03:18.714 [5256:60b] active:0
2014-11-04 23:03:18.793 [5256:60b] didBecomeInactiveSignal
2014-11-04 23:03:18.911 [5256:60b] active:0
2014-11-04 23:03:18.913 [5256:60b] active:0
2014-11-04 23:03:19.051 [5256:60b] active:0
2014-11-04 23:03:19.052 [5256:60b] active:0
2014-11-04 23:03:19.082 [5256:60b] view did load

Binding asynchronously loaded images from a view-model

I'm tinkering with an MVVM implementation, and I'm curious to hear others' input.

We need to present a table view UI of people, showing a single label in each cell for the person's name, and a single UIImageView for the person's avatar. We need to fetch each person's avatar from some web service.

The standard Cocoa Touch MVC solution usually involves queueing up some background requests and coordinating their returned images with the appropriate UITableViewCell subviews that are on screen. This is done asynchronously and kicked off via delegate or data source methods, usually handled within the view controller.

It seems like an MVVM solution would push the avatar image retrieval into a view-model.

So, we have a very simple model:

@interface Person : NSObject

@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, strong) NSURL *avatarURL;

@end

And I suppose we have the following view-model:

@interface PersonEntryViewModel : NSObject

/// A UI-ready combination of a `Person` `firstName` and `lastName`
@property (nonatomic, copy) NSString *name;

@property (nonatomic, strong) UIImage *avatar;

- (instancetype)initWithPerson:(Person *)person;

@end

Then we could have, say, a UITableViewCell implementation:

@interface PersonTableCell : UITableViewCell
@property (nonatomic, strong) PersonEntryViewModel *viewModel;
@end
@implementation PersonTableCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self == nil) return nil;

    RAC(self.textLabel, attributedText) = RACObserve(self, viewModel.name);
    RAC(self.imageView, image) = RACObserve(self, viewModel.avatar);

    return self;
}

@end

This seems like a good strategy, but I'm wrestling with how the PersonEntryViewModel provides a UIImage for the avatar. It seems like +[NSURLConnection rac_sendAsynchronousRequest:] could be the answer:

@implementation PersonEntryViewModel

- (instancetype)initWithPerson:(Person *)person
{
    self = [super init];
    if (!self) return nil;

    // omitted: binding self.name based on person.firstName & person.lastName

    NSURLRequest *request = [NSURLRequest requestWithURL:person.avatarURL];
    RAC(self, avatar) = [[[NSURLConnection rac_sendAsynchronousRequest:request]
        reduceEach:^id(NSURLResponse *response, NSData *data){
            return [[UIImage alloc] initWithData:data];
        }]
        deliverOn:[RACScheduler mainThreadScheduler]];

    return self;
}

@end

…but this will kick off the request on initialization of the view-model as opposed to, say, when the view-model is set on the UITableViewCell within -tableView:cellForRowAtIndexPath:. There also isn't any cancellation of the request when the cell scrolls out of view.

Should the view-model actually expose a RACSignal as opposed to a simple image property? Should the avatar binding between the cell and the view-model occur somewhere else other than the cell's initializer?

Thanks in advance for any input anyone has! If this is too generic of a problem, or poorly outlined, please close it out and I'll try to reevaluate.

forwardSignalWhileActive as signal operator

A category method on signal would increase readability a lot. If you wanna keep the API footprint small I get that, but anyone who uses this a lot will end up implementing it.

-(RACSignal *)rvm_forwardWhileViewModelActive:(RVMViewModel *)viewModel

retain cycle when using didBecomeActiveSignal

Hello,

I have a retain cycle in my code caused by the following setup in viewDidLoad

   [[[[RACSignal
    combineLatest:@[RACObserve(self.viewModel,index),
/*A*/                   self.viewModel.didBecomeActiveSignal]]
/*B                          RACObserve(self.viewModel,active)]]*/
    deliverOnMainThread]
    distinctUntilChanged]
    subscribeNext:^(RACTuple* x) {

The option A is causing a retain cycle so the view model is never released. B works fine. Do you see something wrong with the code or it might be a bug ?

Thank you,
Adrian

How-to MVVM with container controllers?

Hi. I have a question about using MVVM approach with container controllers.
For example. We have container that show ListViewController or MapViewController. Simple tab-bat approach. Does this container view controller require standalone ViewModel?
If yes, does this view model correct:

@property RACCommand* activateMapPresentation;
@property RACCommand* activateListPresentation;
@property BOOL mapIsActive;
@property BOOL listIsActive;

What about ViewModel based routing?

Hi,

Maybe it's not best suitable place for that question, sorry then, but I am just wondering is there something like ReactiveUI Routing concept implementation in ObjC? Kind of ViewModel-based routing, to be able to navigate between screens with ViewModel? And what you think about that?

Thanks

code inside [self.didBecomeActiveSignal subscribeNext:^(id x) {}] not called.

I'm learning to use ReactiveViewModel by following some examples:

but when I used didBecomeActiveSignal, code in subscribeNext not called

Here is my code:

MuseumsListViewModel.m

#import "MuseumsListViewModel.h"
#import "Museum.h"
#import "MuseumItemViewModel.h"
#import "JSONServices.h"
#import "Constants.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
#import <LinqToObjectiveC/NSArray+LinqExtensions.h>

@implementation MuseumsListViewModel

- (instancetype)init {
    self = [super init];
    if (self) {
        @weakify(self)
        [self.didBecomeActiveSignal subscribeNext:^(id x) {
            NSLog(@"didBecomeActiveSignal");
            @strongify(self);
            [self getMuseumsSignal];
        }];
    }
    return self;
}

- (RACSignal *)getMuseumsSignal {
    return
    [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [JSONServices loadJSONFromURL:MUSEUMS_LIST_URL parseToClass:[MuseumsList class] completion:^(id object) {
            MuseumsList *museumsList = (MuseumsList *)object;
            if (self.museums == nil) {
                self.museums = [[NSMutableArray alloc] init];
            }
            [self.museums addObjectsFromArray:[museumsList.data linq_select:^id(Museum *museum) {
                return [[MuseumItemViewModel alloc] initWithMuseum:museum];
            }]];
            self.meta = museumsList.meta;
        } failure:^(NSError *error) {
            NSLog(@"Error: %@", error);
        }];
        return nil;
    }];
}

@end

MuseumsListViewController.m

#import "MuseumsListViewController.h"

@interface MuseumsListViewController ()

@end

@implementation MuseumsListViewController

- (instancetype)initWithViewModel:(MuseumsListViewModel *)viewModel {
    self = [super init];
    if (self ) {
        self.viewModel = viewModel;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end

AppDelegate.m

#import "AppDelegate.h"
#import "MuseumsListViewModel.h"
#import "MuseumsListViewController.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    MuseumsListViewModel *museumsListViewModel = [[MuseumsListViewModel alloc] init];
    MuseumsListViewController *museumsListViewController = [[MuseumsListViewController alloc] initWithViewModel:museumsListViewModel];
    self.window.rootViewController = museumsListViewController;
    [self.window makeKeyAndVisible];
    return YES;
}

@end

Did I miss something? Or I did something wrong? Can you help me? Thank you.

What happened to initWithModel: and what did it do?

I'm reviewing @ashfurrow's C-41 MVVC project, and notice he's using - (instancetype)initWithModel:(id)model; which is included with his version of RVM. However the method is missing from the latest version. So:

  1. What happened to the method?
  2. What did it do (i.e., what was the model)?

run error

Hi, I'm a Chinese Programmer,after I clone the project from github,what should I do next.
11111

Data Context Interaction pattern on top of MVVM.

Hi, this rather a question or ask for advice, but definitely not an issue.

I spend some time today playing with architecture described in README.md
So at first some of my assumptions.

  1. View layer = View Controller + custom UIView + layouts and other view related stuff
  2. ViewModel = Subclass of some basic ViewModel class, with link to actual model, actual this is an adapter of model for View Controller consumption, values transforming. Other ViewModel responsibilities not clear for me. Who manages UINavigationController stack?
  3. Model = Some DTO class (like Mantle for example)

If all above assumptions are correct, then ViewModel can act as a Role in DCI pattern.
In this pattern Roles collected in Contexts, or in another words - Use cases.
Each Use case can also have some output signals or commands which will trigger start of another use cases.

And finally, Use cases can be connected with each other in some Workflow or Application entity.

This system will allow to test and isolate application not just by parts, but also by task, user stories, and other logical chunks.

Open questions.

  1. Common Shared state between ViewModels (network clients, core data contexts)
  2. Container View Controllers responsibility.

RVM Crash with take: on didBecomeActiveSignal when using OCMock

This isn't an RVM issue per se, but I wanted to document it in case anyone else had this issue and was google for an answer. Also, I'm looking for workaround (other than "don't use mocks", of course).

I have a very simple view model:

@implementation ASHViewModel

-(id)init
{
    self = [super init];
    if (self == nil) { return nil; }

    [[self.didBecomeActiveSignal take:1] subscribeNext:^(id x) {
        NSLog(@"Active");
    }];

    return self;
}

@end

Nothing special. Now, when I unit test it, I create a mock.

SpecBegin(ASHViewModel)

describe(@"in a describe block", ^{
    it(@"shouldn't crash", ^{
        ASHViewModel *viewModel = [[ASHViewModel alloc] init];
        id mockViewModel = [OCMockObject partialMockForObject:viewModel];

        [mockViewModel setActive:YES];
    });
});

SpecEnd

The problem is, due to the mocking, something funky is going on under the hood with take: being disposed of. It results in the following crash:

2014-07-30 13:26:22.644 Bug[15566:60b] Active
<unknown>:0: error: -[ASHViewModelSpec in_a_describe_block_shouldn_t_crash] : Cannot remove an observer <RACKVOTrampoline 0xd73b7f0> for the key path "active" from <ASHViewModel 0x913ee70> because it is not registered as an observer.
(
    0   CoreFoundation                      0x019081e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x016878e5 objc_exception_throw + 44
    2   CoreFoundation                      0x01907fbb +[NSException raise:format:] + 139
    3   Foundation                          0x012d546d -[NSObject(NSKeyValueObserverRegistration) _removeObserver:forProperty:] + 538
    4   Foundation                          0x012d51f4 -[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:] + 105
    5   Foundation                          0x012d5118 -[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:context:] + 172
    6   Bug                                 0x0002b7a0 -[RACKVOTrampoline dispose] + 528
    7   Bug                                 0x0001fe43 -[RACCompoundDisposable dispose] + 355
    8   Bug                                 0x0000a994 __69-[NSObject(RACKVOWrapper) rac_observeKeyPath:options:observer:block:]_block_invoke96 + 52
    9   Bug                                 0x00020c79 -[RACDisposable dispose] + 201
    10  Bug                                 0x0001ffc1 disposeEach + 81
    11  CoreFoundation                      0x018a9c69 CFArrayApplyFunction + 57
    12  Bug                                 0x0001fed3 -[RACCompoundDisposable dispose] + 499
    13  Bug                                 0x0001fe43 -[RACCompoundDisposable dispose] + 355
    14  Bug                                 0x0001ffc1 disposeEach + 81
    15  CoreFoundation                      0x018a9c69 CFArrayApplyFunction + 57
    16  Bug                                 0x0001fed3 -[RACCompoundDisposable dispose] + 499
    17  Bug                                 0x000388e4 -[RACSerialDisposable dispose] + 196
    18  Bug                                 0x0001fe43 -[RACCompoundDisposable dispose] + 355
    19  Bug                                 0x0001ffc1 disposeEach + 81
    20  CoreFoundation                      0x018a9c69 CFArrayApplyFunction + 57
    21  Bug                                 0x0001fed3 -[RACCompoundDisposable dispose] + 499
    22  Bug                                 0x000388e4 -[RACSerialDisposable dispose] + 196
    23  Bug                                 0x0001fe43 -[RACCompoundDisposable dispose] + 355
    24  Bug                                 0x0001ffc1 disposeEach + 81
    25  CoreFoundation                      0x018a9c69 CFArrayApplyFunction + 57
    26  Bug                                 0x0001fed3 -[RACCompoundDisposable dispose] + 499
    27  Bug                                 0x000388e4 -[RACSerialDisposable dispose] + 196
    28  Bug                                 0x0001fe43 -[RACCompoundDisposable dispose] + 355
    29  Bug                                 0x0001ffc1 disposeEach + 81
    30  CoreFoundation                      0x018a9c69 CFArrayApplyFunction + 57
    31  Bug                                 0x0001fed3 -[RACCompoundDisposable dispose] + 499
    32  Bug                                 0x000388e4 -[RACSerialDisposable dispose] + 196
    33  Bug                                 0x0005f1e7 __29-[RACSignal(RACStream) bind:]_block_invoke125 + 247
    34  Bug                                 0x0006f69b -[RACSubscriber sendNext:] + 251
    35  Bug                                 0x0002cf57 -[RACPassthroughSubscriber sendNext:] + 487
    36  Bug                                 0x0005ec0a __29-[RACSignal(RACStream) bind:]_block_invoke_298 + 106
    37  Bug                                 0x0006f69b -[RACSubscriber sendNext:] + 251
    38  Bug                                 0x00030b91 __29-[RACReturnSignal subscribe:]_block_invoke + 97
    39  Bug                                 0x00070898 -[RACSubscriptionScheduler schedule:] + 488
    40  Bug                                 0x00030a9f -[RACReturnSignal subscribe:] + 479
    41  Bug                                 0x00062e6b -[RACSignal(Subscription) subscribeNext:error:completed:] + 1003
    42  Bug                                 0x0005ea11 __29-[RACSignal(RACStream) bind:]_block_invoke88 + 833
    43  Bug                                 0x0005f1b0 __29-[RACSignal(RACStream) bind:]_block_invoke125 + 192
    44  Bug                                 0x0006f69b -[RACSubscriber sendNext:] + 251
    45  Bug                                 0x0002cf57 -[RACPassthroughSubscriber sendNext:] + 487
    46  Bug                                 0x0005ec0a __29-[RACSignal(RACStream) bind:]_block_invoke_298 + 106
    47  Bug                                 0x0006f69b -[RACSubscriber sendNext:] + 251
    48  Bug                                 0x00030b91 __29-[RACReturnSignal subscribe:]_block_invoke + 97
    49  Bug                                 0x00070898 -[RACSubscriptionScheduler schedule:] + 488
    50  Bug                                 0x00030a9f -[RACReturnSignal subscribe:] + 479
    51  Bug                                 0x00062e6b -[RACSignal(Subscription) subscribeNext:error:completed:] + 1003
    52  Bug                                 0x0005ea11 __29-[RACSignal(RACStream) bind:]_block_invoke88 + 833
    53  Bug                                 0x0005f1b0 __29-[RACSignal(RACStream) bind:]_block_invoke125 + 192
    54  Bug                                 0x0006f69b -[RACSubscriber sendNext:] + 251
    55  Bug                                 0x0002cf57 -[RACPassthroughSubscriber sendNext:] + 487
    56  Bug                                 0x0005ec0a __29-[RACSignal(RACStream) bind:]_block_invoke_298 + 106
    57  Bug                                 0x0006f69b -[RACSubscriber sendNext:] + 251
    58  Bug                                 0x00030b91 __29-[RACReturnSignal subscribe:]_block_invoke + 97
    59  Bug                                 0x00070898 -[RACSubscriptionScheduler schedule:] + 488
    60  Bug                                 0x00030a9f -[RACReturnSignal subscribe:] + 479
    61  Bug                                 0x00062e6b -[RACSignal(Subscription) subscribeNext:error:completed:] + 1003
    62  Bug                                 0x0005ea11 __29-[RACSignal(RACStream) bind:]_block_invoke88 + 833
    63  Bug                                 0x0005f1b0 __29-[RACSignal(RACStream) bind:]_block_invoke125 + 192
    64  Bug                                 0x0006f69b -[RACSubscriber sendNext:] + 251
    65  Bug                                 0x0002cf57 -[RACPassthroughSubscriber sendNext:] + 487
    66  Bug                                 0x0004a74a __35-[RACSignal(Operations) takeUntil:]_block_invoke576 + 106
    67  Bug                                 0x0006f69b -[RACSubscriber sendNext:] + 251
    68  Bug                                 0x0002cf57 -[RACPassthroughSubscriber sendNext:] + 487
    69  Bug                                 0x0000d99c __84-[NSObject(RACPropertySubscribing) rac_valuesAndChangesForKeyPath:options:observer:]_block_invoke48 + 476
    70  Bug                                 0x0000a591 __69-[NSObject(RACKVOWrapper) rac_observeKeyPath:options:observer:block:]_block_invoke77 + 769
    71  Bug                                 0x0002ba55 -[RACKVOTrampoline observeValueForKeyPath:ofObject:change:context:] + 581
    72  Foundation                          0x012d5d77 NSKeyValueNotifyObserver + 362
    73  Foundation                          0x012d7686 NSKeyValueDidChange + 458
    74  Foundation                          0x01293dcd -[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] + 120
    75  Bug                                 0x0007c83c -[RVMViewModel setActive:] + 332
    76  CoreFoundation                      0x018fc91d __invoking___ + 29
    77  CoreFoundation                      0x018fc82a -[NSInvocation invoke] + 362
    78  CoreFoundation                      0x018fc9aa -[NSInvocation invokeWithTarget:] + 74
    79  BugTests                            0x093aa624 -[OCPartialMockObject handleUnRecordedInvocation:] + 68
    80  BugTests                            0x093a782c -[OCMockObject forwardInvocation:] + 108
    81  CoreFoundation                      0x018f82da ___forwarding___ + 458
    82  CoreFoundation                      0x018f80ee _CF_forwarding_prep_0 + 14
    83  BugTests                            0x093821ee __34-[ASHViewModelSpec spt_defineSpec]_block_invoke_2 + 174
    84  BugTests                            0x093ad76c runExampleBlock + 1500
    85  BugTests                            0x093aedb2 __48-[SPTExampleGroup compileExamplesWithNameStack:]_block_invoke + 258
    86  BugTests                            0x093b413c -[SPTXCTestCase spt_runExampleAtIndex:] + 556
    87  CoreFoundation                      0x018fc91d __invoking___ + 29
    88  CoreFoundation                      0x018fc82a -[NSInvocation invoke] + 362
    89  XCTest                              0x20103c6c -[XCTestCase invokeTest] + 221
    90  XCTest                              0x20103d7b -[XCTestCase performTest:] + 111
    91  BugTests                            0x093b4b28 -[SPTXCTestCase performTest:] + 152
    92  XCTest                              0x20104c48 -[XCTest run] + 82
    93  XCTest                              0x201033e8 -[XCTestSuite performTest:] + 139
    94  XCTest                              0x20104c48 -[XCTest run] + 82
    95  XCTest                              0x201033e8 -[XCTestSuite performTest:] + 139
    96  XCTest                              0x20104c48 -[XCTest run] + 82
    97  XCTest                              0x201033e8 -[XCTestSuite performTest:] + 139
    98  XCTest                              0x20104c48 -[XCTest run] + 82
    99  XCTest                              0x201066ba +[XCTestProbe runTests:] + 183
    100 Foundation                          0x012bd5ec __NSFireDelayedPerform + 372
    101 CoreFoundation                      0x018c6ac6 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
    102 CoreFoundation                      0x018c64ad __CFRunLoopDoTimer + 1181
    103 CoreFoundation                      0x018ae538 __CFRunLoopRun + 1816
    104 CoreFoundation                      0x018ad9d3 CFRunLoopRunSpecific + 467
    105 CoreFoundation                      0x018ad7eb CFRunLoopRunInMode + 123
    106 GraphicsServices                    0x038fc5ee GSEventRunModal + 192
    107 GraphicsServices                    0x038fc42b GSEventRun + 104
    108 UIKit                               0x00347f9b UIApplicationMain + 1225
    109 Bug                                 0x000023cd main + 141
    110 libdyld.dylib                       0x01f4f701 start + 1
)

Like I said, not strictly a RVM issue, but open to suggestions. Feel free to close if it's too off-topic.

Error Handling in ViewModel

This is just a quick question:

I have noticed that in the GroceryList, the GCYViewModel class inherits from RVMViewModel and adds the errors signal.

Is there a specific reason why this kind of error handling is not part of RVMViewModel directly?

Maybe its because the assumption, that all errors are handled equally (e.g. just display the error message on the screen) is too specific and its best practice to just subscribe to the error event of each Action of a viewModel individually and handle the error of that Action individually?

ViewModel creation boilerplate in table/collectionviews

One problem I've yet to find a solution to using MVVM is where to neatly take care of Model > ViewModel conversion/wrapping.

I tend to have a ViewModel class per each TableViewCell/CollectionViewCell, and hence when I create a datasource that initializes itself with a set of Model objects, I need to do something like the following:

+ (NSArray *)viewModelsFromModels:(NSArray *)models {
  NSMutableArray *array       = [NSMutableArray array];
  for (Model *model in models) {
    ViewModel *viewModel = [[ViewModel alloc] initWithModel:model];
    [array addObject:viewModel];
  }
  return array;
}

This is....kind of gross, and also leads to creeping complexity and difficulty of use whenever you have to unwrap viewmodels to get to their underlying models (eg, when handing off a viewmodel to another class that wants to create it's own viewmodel).

I've thought of a few solutions to this but none really work:

  • Create categories on the underlying model that creates a viewmodel for whatever our current context is. This a: gets messy and b: doesn't let us do any caching/lazy computation of viewmodel properties (which is a nice bonus), it also could have performance hits since we'd be creating/destroying these objects frequently.
  • Have every cell store a viewModel and have the datasource update the underlying model property of the viewModel. This also means we don't get any nice caching of our viewModel properties, and limits us to functions that expose/transform the underlying models properties in realtime as they are called.
  • Tucking away this code into a generic macro like MVVM_ADAPT(collection, targetClass). This is sort of okay.

I still haven't come up with a nice way around this, it's my only gripe with MVVM right now. What do you do in your projects?

View model observing itself

In order to do validation, I'm creating an isValid signal property on the view model. The signal is created the usual way with -combineLatest:reduce: with the input signals being RACObserve(self, propertyNameX). It seems strange to be observing self, but it also seems to be a necessary approach. Is this the way to do it, or is there a better way?

EDIT: Related to #2.

didBecomeInactiveSignal should skip:1

Discussing with @kylef, it's not intuitive that didBecomeInactiveSignal immediately sends a value of NO in most cases. Complicating matters is the fact that didBecomeInactiveSignal and its corresponding didBecomeActiveSignal are lazily-loaded.

I understand that the header includes the following comment:

// If the receiver is currently active, this signal will send once immediately
// upon subscription.

However, to quote @joshaber, "If we're expecting people to read the docs, we're gonna have a bad time. :hurtrealbad:"

I would suggest we consider that when they are created, if the would immediately send a value due to the current state of active, then they skip:1. Thoughts?

Static strings, images etc. where do they belong?

I am still new at the MVVM playground but so far I like it a lot, but where do static strings, images etc. belong?

So which one is preferable:
self.title = NSLocalizedString(@"SomeKey", @"....");
or
self.title = self.viewModel.title; (add RAC+RACObserve)

I am leaning towards the last one because future change would not involve the ViewController.

Another example could be TablieView section header titles, Ash Furrow does the following in his C-41 code:

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    if (section == ASHEditRecipeViewControllerMetadataSection) {
        return nil;
    } else if (section == ASHEditRecipeViewControllerFilmTypeSection) {
        return NSLocalizedString(@"Film Type", @"Edit View Controller section title");
    } else if (section == ASHEditRecipeViewControllerStepsSection) {
        return NSLocalizedString(@"Steps", @"Edit View Controller section title");
    } else {
        return nil;
    }
}

But would it not be better to ask the ViewModel like:

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    return [self.viewModel headerTitleForSection:section];
}

-Morten

Communicating up the chain from subviews in ReactiveCocoa MVVM world

Curious when/if you guys leverage the responder chain. I've been playing with different ways of communicating events and status amongst view controllers and subviews like cells. Of most interest to me is communicating back up that chain, which is where I'm wondering if the responder chain would be a good solution.

For propagating view information down the chain, I've tried different combinations of the the cell just having access to the view controller view model (since it's technically representing some of the data coming from that view model), or subviews/cells having their own view model. The later is certainly more modular, but more work.

My main question lies with some event occurring in a subview/cell (a uibutton tap or a uiswitch flipping) somehow being propagated up to the main view model, ultimately so it can affect other views/cells in the main view (think of flipping a switch and hiding some cells). When the cell shares the view controller's view model, it's easy enough because you just call a method on that view model. When the cell has it's own view model, there are a lot of options, none of which feel quite ideal. You could call a method on the cell view model, which in turn would call a method on it's delegate (the view controller view model). You could expose a signal on the cell view model, which the parent view model observes. There are a few other possible solutions too.

So I've been wondering if it would be easier to not try and send messages back up that chain, but instead use the responder chain to call a method on the view controller, which in turn can update the view model appropriately, which would propagate any changes back down the view model hierarchy.

// cc @ashfurrow

How to properly select content os a model based on a filter selected by a UISegmentedControl?

I'm just starting a project and using ReactiveCocoa/ReactiveViewModel for the first time.

I have a view with a segmented control and a table view. The segment has 3 options: recent, all and search.

I created a view model as data source of table view, and now I want that when the user select recent or all, the table view show something like 15 more recent or all data from Core Data, and when the user select filter, a search filed should appear and the content should be filtered based on the text of the search field.

How is the best approach to the view model know what is the selection of the UISegmentedControl?

Should I just create a integer value in the view model, set it via RACCommand when the user changes the selection, and watch it on the view model?

你好 更新框架文件为啥老是报错了?

hhbdeMacBook-Air:ReactiveViewModel-master huabinhu$ carthage update
*** Cloning Nimble
*** Cloning Quick
*** Cloning xcconfigs
*** Cloning ReactiveCocoa
*** Checking out Nimble at "v0.2.0"
*** Checking out Quick at "v0.2.3"
*** Downloading ReactiveCocoa.framework binary at "v2.5"
GitHub API request failed: NetworkError(Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSURLSessionDownloadTaskResumeData=<CFData 0x7faeeb813ae0 [0x7fffc843bbd0]>{length = 7085, capacity = 16384, bytes = 0x3c3f786d6c2076657273696f6e3d2231 ... 2f706c6973743e0a}, NSErrorFailingURLKey=https://github-cloud.s3.amazonaws.com/releases/3606624/32544950-ffb3-11e4-9089-020ae2920dfd.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAISTNZFOVBIJMK3TQ%2F20161016%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20161016T031029Z&X-Amz-Expires=300&X-Amz-Signature=b08e6b7b6ded8c129c177cc16d05fdc6245dd28a8eef9ccff9864dd9191aa29d&X-Amz-SignedHeaders=host&actor_id=0&response-content-disposition=attachment%3B%20filename%3DReactiveCocoa.framework.zip&response-content-type=application%2Foctet-stream, _kCFStreamErrorDomainKey=4, NSLocalizedDescription=The request timed out., NSErrorFailingURLStringKey=https://github-cloud.s3.amazonaws.com/releases/3606624/32544950-ffb3-11e4-9089-020ae2920dfd.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAISTNZFOVBIJMK3TQ%2F20161016%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20161016T031029Z&X-Amz-Expires=300&X-Amz-Signature=b08e6b7b6ded8c129c177cc16d05fdc6245dd28a8eef9ccff9864dd9191aa29d&X-Amz-SignedHeaders=host&actor_id=0&response-content-disposition=attachment%3B%20filename%3DReactiveCocoa.framework.zip&response-content-type=application%2Foctet-stream, NSUnderlyingError=0x7faee967fb60 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, _kCFStreamErrorCodeKey=-2102})

Using forwardSignalWhileActive to fix KVO performance on iOS 9.

Hi! I'm trying to work around #2383. We're using a few RACSignals in our UICollectionViewCells, so we are hitting the performance issues pretty quickly.

I saw some talk on #12 about using -forwardSignalWhileActive: to dispose upon deactivation, and resubscribe upon activation.

Will disposing on the signal in this manner trigger an 'un-observe' on KVO?

can`t pod install 0.3.3

I cant pod install 0.3.3, even if repo update. When run pod search ReactiveViewModel`.

-> ReactiveViewModel (0.3)
   Model-View-ViewModel, using ReactiveCocoa.
   pod 'ReactiveViewModel', '~> 0.3'
   - Homepage: https://github.com/ReactiveCocoa/ReactiveViewModel
   - Source:   https://github.com/ReactiveCocoa/ReactiveViewModel.git
   - Versions: 0.3, 0.2, 0.1.1 [master repo]

But ReactiveCoccoa exist the newest release when run pod search ReactiveCocoa.

-> ReactiveCocoa (7.2.0)
   Streams of values over time
   pod 'ReactiveCocoa', '~> 7.2.0'
   - Homepage: https://github.com/ReactiveCocoa/ReactiveCocoa
   - Source:   https://github.com/ReactiveCocoa/ReactiveCocoa.git
   - Versions: 7.2.0, 7.1.0, 7.1.0-rc.2, 7.1.0-rc.1, 7.0.1, 7.0.0, 7.0.0-rc.1, 7.0.0-alpha.2, 7.0.0-alpha.1, 6.1.0-alpha.2, 6.1.0-alpha.1, 6.0.2, 6.0.1, 6.0.0,
   6.0.0-rc.3, 6.0.0-rc.2, 6.0.0-rc.1, 6.0.0-alpha.1, 5.0.4, 5.0.3, 5.0.2, 5.0.1, 5.0.0, 5.0.0-rc.1, 5.0.0-alpha.6, 5.0.0-alpha.5, 5.0.0-alpha.3, 5.0.0-alpha.2, 4.2.2,
   4.2.1, 4.1.0, 4.0.4-alpha-4, 4.0.4-alpha-1, 4.0.3-alpha-3, 4.0.3-alpha-1, 4.0.2-alpha-3, 4.0.2-alpha-1, 4.0.1, 4.0.1-alpha-3, 4.0.1-alpha-1, 4.0.0, 4.0.0-alpha-3,
   4.0.0-alpha-2, 4.0.0-alpha-1, 4.0.0-RC.2, 4.0.0-RC.1, 3.0.0, 3.0.0-swift2, 3.0-beta.9, 3.0-beta.6, 3.0-alpha.3, 3.0.0-alpha.1, 3.0-RC.1, 2.5, 2.4.7, 2.4.6, 2.4.5,
   2.4.4, 2.4.2, 2.3.1, 2.3, 2.2.4, 2.2.3, 2.2.2, 2.2, 2.1.8, 2.1.7, 2.1.6, 2.1.5, 2.1.4, 2.1.3, 2.1.2, 2.1.1, 2.1, 2.0, 1.9.7, 1.9.6, 1.9.5, 1.9.4, 1.8.1, 1.8.0, 1.7.2,
   1.7.1, 1.7.0, 1.6.0, 1.5.0, 1.4.0, 1.3.1, 1.0.0, 0.17.1, 0.16.1, 0.13.1, 0.12.0, 0.10.0, 0.9.0, 0.8.0, 0.6.0, 0.5.0, 0.0.1 [master repo]

Will not support pod anymore?

MVVM and animation callbacks, executing one view update after another

I'm trying to determine a clean way to do the following with MVVM, with the approach that changes to the view should always be triggered by changes to the viewModel. Basically it's an action that provides information that I need to use in the second of two chained animations.

  1. Looking at a table list of items.
  2. Tap one to go into a carousel-like modal where you can page through full screen versions horizontally.
  3. Tap a button on the card indicating you would like to select that one
  4. Close the carousel with animation and then ->
  5. Animate the row you selected in the cards up to the top.

So right now I catch the tap, hide the modal with an animation, and in the completion handler for that animation I update the viewmodel, which causes the next animation step to happen (animating the row to the top).

[[RACObserve(self, optionDetailContainerViewController.cardButtonTappedSignal) switchToLatest]
    subscribeNext:^(RACTuple *values) {
        @strongify(self);
        NSNumber *index = values.first;
        NSNumber *type = values.second;
        if (type.integerValue == TCOptionDetailTypeUse) {
            id object = self.viewModel.searchResults[index.integerValue];
            if ([self.viewModel.selectedOptions containsObject:object]) {
                return;
            }
        }
        [self removeOptionDetailCardsWithCompletionHandler:^{
           @strongify(self);
            if (type.integerValue == TCOptionDetailTypeDelete) {
                [self.viewModel deleteSelectedOption:index.integerValue];
            } else {
                [self.viewModel useThirdPartyObject:self.viewModel.searchResults[index.integerValue]];
            }
        }];
    }];

Really what should happen is tapping the button updates the view model to indicate that the modal should close, and then somehow at the end of that animation, the view model is updated again to reflect the next step with the information (index, type) that gathered from the button tap.

I know this probably isn't spelled out really well, but my brain is a little foggy right now.

GitHub API request failed: NetworkError(Error Domain=NSURLErrorDomain Code=-1001 "The request timed out."

*** Checking out ios-snapshot-test-case at "da629211c17a4c507e2e866e8a19ed3122af770b"
*** Downloading realm-cocoa.framework binary at "v1.1.0"
GitHub API request failed: NetworkError(Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSURLSessionDownloadTaskResumeData=<CFData 0x7fd736535570 [0x7fffc5786bd0]>{length = 7061, capacity = 16384, bytes = 0x3c3f786d6c2076657273696f6e3d2231 ... 2f706c6973743e0a}, NSErrorFailingURLKey=https://github-cloud.s3.amazonaws.com/releases/4044891/decf6870-7c73-11e6-8998-f439aca6d4db.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAISTNZFOVBIJMK3TQ%2F20161201%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20161201T043448Z&X-Amz-Expires=300&X-Amz-Signature=e8a82b4001c68e4afa49f4c42692a42e1f8411efe63fe6d888f75676090922cd&X-Amz-SignedHeaders=host&actor_id=0&response-content-disposition=attachment%3B%20filename%3DCarthage.framework.zip&response-content-type=application%2Foctet-stream, _kCFStreamErrorDomainKey=4, NSLocalizedDescription=The request timed out., NSErrorFailingURLStringKey=https://github-cloud.s3.amazonaws.com/releases/4044891/decf6870-7c73-11e6-8998-f439aca6d4db.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAISTNZFOVBIJMK3TQ%2F20161201%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20161201T043448Z&X-Amz-Expires=300&X-Amz-Signature=e8a82b4001c68e4afa49f4c42692a42e1f8411efe63fe6d888f75676090922cd&X-Amz-SignedHeaders=host&actor_id=0&response-content-disposition=attachment%3B%20filename%3DCarthage.framework.zip&response-content-type=application%2Foctet-stream, NSUnderlyingError=0x7fd736462ed0 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, _kCFStreamErrorCodeKey=-2102})

https://github.com/danielgindi/Charts/issues/1886

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.