0

I need to sync several tables in sqlite from data from parse. I setup ReactiveCoccoa for it. However, I don't know how wait for the sync of one table before start the next.

This is what I have now:

-(RACSignal *) syncTable:(NSString *)name showProgres:(BOOL)showProgres
{
    @weakify(self);
    NSInteger __block count = 0;

    return [[RACSignal createSignal:^ RACDisposable *(id<RACSubscriber> subscriber)
    {
        PFQuery *query = [PFQuery queryWithClassName:name];
        NSDate *date = [self versionForTable:name];

        if (![name isEqualToString:@"Company"]) {
            [query whereKey:@"x_company" equalTo:[self currentCompany]];
            [query whereKey:@"updatedAt" greaterThan:date];
        } else {
            [query whereKey:@"objectId" equalTo:[self currentCompany].objectId];
        }

        NSArray *objects = [query findObjects];

        if (showProgres) {
            [SVProgressHUD showWithStatus:[NSString stringWithFormat:LOC_SYNC_TABLE, name]];
        }

        for (PFObject *object in objects) {
            [subscriber sendNext:object]; // <-- This is wrong!

            count++;
            if (showProgres) {
                [SVProgressHUD showProgress:count / [objects count] status:name];
            }
        }

        [subscriber sendCompleted];
        return nil;
    }] flattenMap:^RACStream *(PFObject *object) {
        @strongify(self);

        return [self syncRecords:name data:object];
    }];
}

I don't see how send each PFObject and also do the progress stuff.

P.D: This is the code with the changes proposed:

-(RACSignal *) syncRecords:(NSString *)table data:(PFObject *)data
{
    @weakify(self);

    return [RACSignal createSignal:^ RACDisposable *(id<RACSubscriber> subscriber)
    {
        /* STUFF */

        if (![db saveInternal:rs]) {
            NSDictionary *err = @{NSLocalizedDescriptionKey:[rs errorsAsString]};

            [subscriber sendError:[NSError errorWithDomain:LOC_ERROR_SAVE code:0 userInfo:err]];
        } else {
            /* STUFF */
        }        
        [subscriber sendCompleted];

        return nil;
    }];
}

-(RACSignal *) syncTable:(NSString *)name showProgres:(BOOL)showProgres
{
    @weakify(self);
    NSInteger __block count = 0;

    return [[[RACSignal createSignal:^ RACDisposable *(id<RACSubscriber> subscriber)
    {
/* STUFF */
        NSArray *objects = [query findObjects];
        /* If I do this in async way, the concat is not repected*/
/* STUFF */        
        for (PFObject *object in objects) {
            [subscriber sendNext:object];
/* STUFF */
        }
        return nil;
    }] map:^RACStream *(PFObject *object) {
        @strongify(self);

        return [self syncRecords:name data:object];
    }] concat];
}

-(RACSignal *) fullSync
{
    RACSignal *fullSync = [[[[[RACSignal createSignal:^ RACDisposable *(id<RACSubscriber> subscriber)
    {
/* STUFF */
        for (NSString *table in self.tablesFixed) {
            [subscriber sendNext:table];
        }

        [subscriber sendCompleted];

        return nil;
    }] map:^RACStream *(NSString *table) {
        @strongify(self);

        return [self syncTable:table showProgres:YES];
    }] doError:^(NSError *error) {
        DDLogError(@"%@", error);

        [[Db currentDb] rollbackTransaction];
    }] doCompleted:^{
        [[Db currentDb] commitTransaction];
/* STUFF */
    }] concat];


    return fullSync;
}

-(RACSignal *) autoLogin:(PFUser *)user
{
    @weakify(self);

    return [[RACSignal createSignal:^ RACDisposable *(id<RACSubscriber> subscriber)
    {
/* STUFF */
        [subscriber sendNext:user];

        [subscriber sendCompleted];

        return nil;
    }] flattenMap:^RACStream *(id user) {
        @strongify(self);
        return [self fullSync];
    }];
}

-(RACSignal *) login:(NSString *)email pwd:(NSString *)pwd
{
    DDLogInfo(@"Login user %@", email);

    @weakify(self);

    RACSignal *login = [[RACSignal createSignal:^ RACDisposable *(id<RACSubscriber> subscriber)
    {
        [PFUser logInWithUsernameInBackground:email password:pwd block:^(PFUser *user, NSError *error) {            
            if (error) {
                [subscriber sendError:error];
            } else {
                [subscriber sendNext:user];

                [subscriber sendCompleted];
            }
        }];

        return nil;
    }] flattenMap:^RACStream *(id user) {
       @strongify(self);

       return [self autoLogin:user];
    }];

    return login;
}
mamcx
  • 15,916
  • 26
  • 101
  • 189

1 Answers1

1

Conceptually, -flattenMap: ends up looking like this (a -map: plus a -flatten):

return [[[RACSignal
    createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
        // Generate and send `PFObject`s
        return nil;
    }]
    map:^(PFObject *object) {
        @strongify(self);
        return [self syncRecords:name data:object];
    }]
    flatten];

-flatten will cause the sync signals will be subscribed to as quickly as possible, disregarding their order.

Instead of flattening them, you probably want to use -concat, which will preserve ordering (so only one sync is subscribed to at a time):

return [[[RACSignal
    createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
        // Generate and send `PFObject`s
        return nil;
    }]
    map:^(PFObject *object) {
        @strongify(self);
        return [self syncRecords:name data:object];
    }]
    concat];
Justin Spahr-Summers
  • 16,893
  • 2
  • 61
  • 79
  • Now the problems is that when changed to map/concat the signals not run. I put a flatenmap higer in the chain and then only the first one is fired and the program hang. I update the sample code. – mamcx Dec 05 '13 at 18:01
  • Your `-syncTable:` signal never completes, so `-concat` will never continue onto the next signal. Also, you'll want to apply `-do…` either _after_ the `-concat`, or _inside_ the `-map:`. – Justin Spahr-Summers Dec 05 '13 at 22:27