Search code examples
iosobjective-creactive-cocoa

ReactiveCocoa, is there a better way to RAC first and refresh later


Here's the situation

I've a ViewModel (ok, follow the MVVM architecture), which has an array property, called "specialtySegments", and a flag property called "generic", I wanna when the generic value is changed, update the specialtySegments value again

Code snippet

- (instancetype)initWithTerritory:(SAPTerritory *)territory {
    self = [super init];
    if (self) {
        _territory = territory;
        _generic = NO;            
        _selectedProduct = [_products firstObject];

        RAC(self, specialtySegments) = [self querySpecialtySegments];

    }

    return self;
}

- (RACSignal *)querySpecialtySegments {
    return [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [self asynQuerySpecialtySegments:^(NSArray *model) {
            [subscriber sendNext:model];
            [subscriber sendCompleted];
        }];


        return [RACDisposable disposableWithBlock:^{

        }];
    }] deliverOn:[RACScheduler mainThreadScheduler]] publish] autoconnect];
}


- (void)refreshData {
    [self asynQuerySpecialtySegments:^(NSArray *model) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.specialtySegments = model;
        });
    }];                    
}

And in the ViewController

@weakify(self);
[[RACObserve(self.viewModel, specialtySegments) filter:^BOOL(id value) {
    return (value != nil);
}] subscribeNext:^(id x) {
    @strongify(self) {
        [self.indicatorView stopAnimating];
        self.indicatorView.hidden = YES;
        [self.tableView reloadData];
    }
}];

And some place to change the generic value, like

if (specialtyButton.selected == NO) {
    self.viewModel.generic = NO;
    genericButton.selected = NO;
    specialtyButton.selected = YES;
    [self.viewModel refreshData];
}

That's what I know to do now, it's not kind of fancy, I think.

Could I observe the generic property in the ViewModel, and subscripNext and first the signal again.

I've try like this, but code didn't enter the querySpecialtySegments, I put a breakpoint in the method.

[RACObserve(self, generic) subscribeNext:^(id x) {
    [self querySpecialtySegments];
}];

and if I do like this, the asynQuerySpecialtySegments method would enter twice, as the RAC before, would run it once, I think

    [RACObserve(self, generic) subscribeNext:^(id x) {
        [self asynQuerySpecialtySegments:^(NSArray *model) {
            dispatch_async(dispatch_get_main_queue(), ^{
                self.specialtySegments = model;
            });
        }];
    }];

I'm a newbie to ReactiveCocoa, trying using in the project, so is there a better way to the thing I described above, thanks guy


Solution

  • OK, after one day reading I think I've found the answer by myself

    The key point is

    Most signals start out "cold," which means that they will not do any work until subscription.

    So this part code should change like this

    [RACObserve(self, generic) subscribeNext:^(id x) {
        [[self querySpecialtySegments] subscribeNext:^(id x) { // subscription is the key to trigger
            self.specialtySegments = x;  // would trigger the observer in view controller to do something
        }];
    }];
    

    Updated: Personal idea is to observe generic property in ViewModel and then trigger View to update something is not a best option, some kind of black magic, without calling refresh or something, but the view refresh automatically. With a public method -reloadData is a good choice

    - (void)refreshData {
    
        @weakify(self);
        [[self querySpecialtySegments] subscribeNext:^(id x) {
            @strongify(self) {
                self.specialtySegments = x;
            }
        }];
    }