reactivecocoa / reactiveviewmodel Goto Github PK
View Code? Open in Web Editor NEWModel-View-ViewModel, using ReactiveCocoa
License: MIT License
Model-View-ViewModel, using ReactiveCocoa
License: MIT License
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. "
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?
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.
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.
UINavigationController
stack?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.
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:
model
)?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
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.
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
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?
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})
Especially how hierarchies work and line up to the view and model layers (or don't).
Xcode 10 force to generate errors when using @keypath, including RACObserve, this might be a problem in RACObjC.
Hi! I'm trying to work around #2383. We're using a few RACSignal
s in our UICollectionViewCell
s, 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?
It seems that ReactiveViewModel assume that a ViewModel only has 1 Model.
What if I want to bind a ViewModel to multiple Models?
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;
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.
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?
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.
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?
Why ReactiveViewModel
depend on so old ReactiveCocoa
version???
Using these instructions.
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
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.
see the title.
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.
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.
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.
*** 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})
- (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
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
Can you add document about how to build it and how to use it?
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:
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.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?
don't quite get the idea. can't we just do
[[self didBecomeActiveSignal] subscribeNext: ^(id x)];
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
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.