我是ReactiveCocoa和MVVM的新手。我正在尝试做简单的登录屏幕。 故事板中的我的视图控制器有2个文本字段(用户名,密码)和一个按钮。 单击该按钮时,它应将POST请求用户名和密码发送到服务器并接收安全令牌。收到响应后,它应该执行sendCompleted。但是,在我的RACCommand executionSignals块中的subscribeCompleted永远不会被执行,我无法进入下一个屏幕。
// ViewController.m
#import "ViewController.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
#import <ReactiveCocoa/RACEXTScope.h>
#import "ViewModel.h"
@interface ViewController ()
@property (strong, nonatomic) ViewModel *viewModel;
@property (weak, nonatomic) IBOutlet UIButton *button;
@property (weak, nonatomic) IBOutlet UITextField *usernameTextField;
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;
@end
@implementation ViewController
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (!self) return nil;
_viewModel = [ViewModel new];
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Reactive stuff
RAC(self.viewModel, username) = self.usernameTextField.rac_textSignal;
RAC(self.viewModel, password) = self.passwordTextField.rac_textSignal;
self.button.rac_command = self.viewModel.loginCommand;
@weakify(self);
// Logged in successfully
[[[[self.viewModel.loginCommand.executionSignals logAll] flattenMap:^RACStream *(RACSignal *execution) { // I don't understand this part well. What does flattenMap do in this particular example?
return [execution ignoreValues]; // I assume it should ignore sendNext:
}] subscribeCompleted:^{
NSLog(@"completed"); // This subscribeCompleted block never get called!
//[self performSegueWithIdentifier:@"nextScreen" sender:nil]; go to next screen or something similar
}];
// Error occured during login
[self.viewModel.loginCommand.errors subscribeNext:^(id error) { // It works fine.
NSLog(@"Login error: %@", error);
// Show alert view
}];
}
@end
// ViewModel.h
#import <Foundation/Foundation.h>
@class RACCommand;
@interface ViewModel : NSObject
@property (nonatomic, readonly) RACCommand *loginCommand;
@property (strong, nonatomic) NSString *username;
@property (strong, nonatomic) NSString *password;
@property (nonatomic, readonly, getter=isLoading) BOOL loading; // Used for loading indicatior view.
@end
// ViewModel.m
#import "ViewModel.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
#import <ReactiveCocoa/RACEXTScope.h>
@interface ViewModel ()
@property (strong, nonatomic) RACCommand *loginCommand;
@property (nonatomic, assign, getter=isLoading) BOOL loading;
@end
@implementation ViewModel
// Initializer
- (instancetype)init {
self = [super init];
if (!self) return nil;
_networkManager = [NetworkManager shared];
return self;
}
// Getter
- (RACCommand*)loginCommand {
if (_loginCommand) {
_loginCommand = [[RACCommand alloc]initWithEnabled:[self validateLoginInputs] signalBlock:^RACSignal *(id input) {
return [self loginUserSignal];
}];
}
return _loginCommand;
}
// Methods
- (RACSignal*)validateLoginInputs {
return [RACSignal combineLatest:@[RACObserve(self, username), RACObserve(self, password)]
reduce:^id(NSString *username, NSString *password) {
return @(username.length > 0 && password.length > 0);
}];
}
- (RACSignal*)loginUserSignal {
@weakify(self);
return [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self);
self.loading = YES;
[self.networkManager validateLoginWithUsername:self.username
password:self.password
success:^(NSDictionary *jsonResponse) {
[subscriber sendNext:jsonResponse];
[subscriber sendCompleted]; // it doesnt work
} failure:^(NSError *error) {
[subscriber sendError:error]; // however it works
}];
}] doNext:^(id x) {;
// Save secure token to memory
}] finally:^{
// When error occurs or complete, stop animation spinner.
@strongify(self);
self.loading = NO;
}]
replayLazily];
}
我做了类似的事情:
[self.viewModel.loginCommand.executionSignals subscribeNext:^(id signal) {
[signal subscribeNext:^(id x) {
NSLog(@"Logged in successfully");
// [self perfromSegueWithIdentifier:@"SetPin" sender:nil];
}];
}];
但我认为这不是一个干净的解决方案。
答案 0 :(得分:0)
self.viewModel.loginCommand.executionSignals - 不发送“已完成”
ViewModel.h
@property (nonatomic, strong, readonly) RACSignal *completed;
ViewModel.m
self = [super init];
if (self) {
RACSubject *completed = [RACSubject subject];
_completed = completed;
[[[self.loginCommand.executionSignals map:^id(RACSignal *value) {
return [value doCompleted:^{
[completed sendCompleted];
}];
}] switchToLatest]
subscribeNext:^(id x) {
}];
return self;
}