bandrei2408
bandrei2408

Reputation: 110

Problems on updating UI from async callback

I have the following code :

@property (weak, nonatomic) IBOutlet UIView *loadingView;

-(void) hideLoadingScreen{
    self.loadingViewRect = self.loadingView.frame;
    [self.loadingView setHidden:YES];
    [self.loadingView setFrame:CGRectMake(0,0,0,0)];
}
- (void)viewDidAppear:(BOOL)animated{
    [self.apiClient checkLoggedInWithCallback:^(int status){
        if(status == ONLINE_LOGGED_IN){
            [self.dbAPI isPullSynchronized:^(BOOL isPullSynced){
                if(isPullSynced){
                    self.data = [self.dbAPI getTodayVisits];
                    [[self tableView] reloadData];
                    [self hideLoadingScreen];
                } else {
                    [self.apiClient getTodayVisits:^(NSArray* visits){
                        [self.dbAPI insertTodayVisits:visits withCallback:^(int status){
                            self.data = [self.dbAPI getTodayVisits];
                            [[self tableView] reloadData];
                        }];
                    }];
                }
            }];
        } else if(status == ONLINE_NOT_LOGGED_IN || status == ONLINE_INVALID_TOKEN || status == OFFLINE_NOT_LOGGED_IN) {
            //Redirect to login
        } else {
            //Get from Local DB
            self.data = [self.dbAPI getTodayVisits];
            [[self tableView] reloadData];
        }
    }];
}

The hideLoadingScreen method won't execute (I mean it gets executed, but the UI doesn't update).

I tried all things to make it work (including dispatching [self hideLoadingScreen] to the main thread via GCD and performSelectorOnMainThread / making a __block BOOL variable isLoading and sleeping on the main thread till that variable was changed etc.). I also called the hideLoadingView method on the viewDidAppear method and it works, but I want it to hide when the callback is executed. Unfortunately, I couldn't find a solution by searching on stackoverflow, neither on google (I must say I tried all the solutions I found).

L.E. I logged self.loadingView as Rob Napier suggested

New code:

-(void) hideLoadingScreen{
    self.loadingViewRect = self.loadingView.frame;
    NSLog(@"hideLoadingScreen before: %@",self.loadingView);
    [self.loadingView setHidden:YES];
    [self.loadingView setFrame:CGRectMake(0,0,0,0)];
    NSLog(@"hideLoadingScreen after: %@",self.loadingView);
}

- (void)viewDidAppear:(BOOL)animated{
    NSLog(@"%@",self.loadingView);
    [self.apiClient checkLoggedInWithCallback:^(int status){
        if(status == ONLINE_LOGGED_IN){
            [self.dbAPI isPullSynchronized:^(BOOL isPullSynced){
                if(isPullSynced){
                    dispatch_async(dispatch_get_main_queue(), ^{
                        self.data = [self.dbAPI getTodayVisits];
                        [[self tableView] reloadData];
                        NSLog(@"async before: %@",self.loadingView);
                        [self hideLoadingScreen];
                        NSLog(@"async after: %@",self.loadingView);
                    });
                } else {
                    [self.apiClient getTodayVisits:^(NSArray* visits){
                        [self.dbAPI insertTodayVisits:visits withCallback:^(int status){
                            self.data = [self.dbAPI getTodayVisits];
                            [[self tableView] reloadData];
                        }];
                    }];
                }
            }];
        } else if(status == ONLINE_NOT_LOGGED_IN || status == ONLINE_INVALID_TOKEN || status == OFFLINE_NOT_LOGGED_IN) {
            //Redirect to login
        } else {
            //Get from Local DB
            self.data = [self.dbAPI getTodayVisits];
            [[self tableView] reloadData];
        }
    }];
}

Logs:

2016-01-08 16:22:25.973 sunwaves.reporting[4566:282042] async before: <UIView: 0x7fa681e745d0; frame = (0 0; 1024 650); autoresize = RM+H+BM; layer = <CALayer: 0x7fa681e291c0>>
2016-01-08 16:22:25.973 sunwaves.reporting[4566:282042] hideLoadingScreen before: <UIView: 0x7fa681e745d0; frame = (0 0; 1024 650); autoresize = RM+H+BM; layer = <CALayer: 0x7fa681e291c0>>
2016-01-08 16:22:25.974 sunwaves.reporting[4566:282042] hideLoadingScreen after: <UIView: 0x7fa681e745d0; frame = (0 0; 0 0); hidden = YES; autoresize = RM+H+BM; layer = <CALayer: 0x7fa681e291c0>>
2016-01-08 16:22:25.974 sunwaves.reporting[4566:282042] async after: <UIView: 0x7fa681e745d0; frame = (0 0; 0 0); hidden = YES; autoresize = RM+H+BM; layer = <CALayer: 0x7fa681e291c0>>

Upvotes: 0

Views: 80

Answers (1)

Rob Napier
Rob Napier

Reputation: 299275

The majority of UIKit calls must be made on the main queue. You should use dispatch_async(dispatch_get_main_queue(),... to do this.

This includes your calls to reloadData at a minimum.

You assignments to self.data are also likely not thread-safe (unless you've done something special in the setter). So those need to be on the main queue.

And of course your calls to hideLoadingScreen.

I assume that most of these blocks execute off the main queue, so that means putting in dispatch_async in several places.

- (void)viewDidAppear:(BOOL)animated{
    [self.apiClient checkLoggedInWithCallback:^(int status){
        if(status == ONLINE_LOGGED_IN){
            [self.dbAPI isPullSynchronized:^(BOOL isPullSynced){
                if(isPullSynced){
                    dispatch_async(dispatch_get_main_queue(), ^{
                        self.data = [self.dbAPI getTodayVisits];
                        [[self tableView] reloadData];
                        [self hideLoadingScreen];
                    });
                } else {
                    [self.apiClient getTodayVisits:^(NSArray* visits){
                        [self.dbAPI insertTodayVisits:visits withCallback:^(int status){
                            dispatch_async(dispatch_get_main_queue(), ^{
                                self.data = [self.dbAPI getTodayVisits];
                                [[self tableView] reloadData];
                            });
                        }];
                    }];
                }
            }];
        } else if(status == ONLINE_NOT_LOGGED_IN || status == ONLINE_INVALID_TOKEN || status == OFFLINE_NOT_LOGGED_IN) {
            //Redirect to login
        } else {
            //Get from Local DB
            dispatch_async(dispatch_get_main_queue(), ^{
                self.data = [self.dbAPI getTodayVisits];
                [[self tableView] reloadData];
            });
        }
    }];
}

Upvotes: 2

Related Questions