命令中的ReactiveCocoa subscribeCompleted永远不会执行

时间:2014-12-02 09:09:03

标签: ios objective-c mvvm reactive-programming reactive-cocoa

我是ReactiveCocoa和MVVM的新手。我正在尝试做简单的登录屏幕。 故事板中的我的视图控制器有2个文本字段(用户名,密码)和一个按钮。 单击该按钮时,它应将POST请求用户名和密码发送到服务器并接收安全令牌。收到响应后,它应该执行sendCompleted。但是,在我的RACCommand executionSignals块中的subscribeCompleted永远不会被执行,我无法进入下一个屏幕。

ViewController.m

//  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

//  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

//  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];
       }];
    }];

但我认为这不是一个干净的解决方案。

1 个答案:

答案 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;
}