2

How can I get an indication to main UI thread in an iPhone app IOS program, when background task has completed?

Background

  • I'm trying to setup a loading indicator per the concept at How to add a UIActivityIndicator to a splash screen in a iphone application?
  • was going to in AppDelete use "performSelectorInBackground" to load up the model data
  • what I need therefore is in the RootViewController some way of telling when the data has finished loading in the background so that it can (a) update the tableview with the data and (b) remove any activity indicator
  • I'm assuming way to do things here is as followings:
    • in App Delegate didFinishLaunchingWithOptions pass off Model data loading to background
    • AppDelegate loads up RootViewController and it immediately setup up an activity indicator
    • once data is loaded in background, it somehow has to indicate this back to the RootViewController (? reason for this question) that it's finished
    • 2nd question is probably also when background task does inidicate its finished, how can the RootviewController check that the UI did get setup (with activity indicator etc) before it tries to disable the activity indicator
Community
  • 1
  • 1
Greg
  • 34,042
  • 79
  • 253
  • 454

2 Answers2

3

You can call back into the main thread from your background selector using -performSelectorOnMainThread:withObject:waithUntilDone: like so:

- (void)loadModel
{
    // Load the model in the background
    Model *aModel = /* load from some source */;

    [self setModel:aModel];
    [self performSelectorOnMainThread:@selector(finishedLoadingModel) withObject:nil waitUntilDone:YES];
}

- (void)finishedLoadingModel
{
    // Notify your view controller that the model has been loaded
    [[self controller] modelLoaded:[self model]];
}

Update: an even safer way to do this would be to check in -finishedLoadingModel to make sure you're running on the main thread:

- (void)finishedLoadingModel
{
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:_cmd withObject:nil waitUntilDone:YES];
    }
    // Notify your view controller that the model has been loaded
    [[self controller] modelLoaded:[self model]];
}
Cameron Spickert
  • 5,190
  • 1
  • 27
  • 34
  • thanks - any comments re how to make sure that when "modelLoaded" is called, that the main thread had got far enough along showing the UI wiht the activity indicator? i.e. how to deal with the potential issue of the "modelLoaded" method trying to hide the activtyIndicator when it may have not been setup/displayed yet? – Greg Nov 07 '11 at 01:40
  • As long as you display and hide the activity indicator on the main thread, you should be fine. The basic workflow you should follow is: 1) display activity indicator; 2) kick off your background thread; 3) after you're done and back on the main thread, hide the activity indicator. – Cameron Spickert Nov 07 '11 at 01:42
  • ok - so I might have to revisit my order - I had the background model loading being kicked off in AppDelegate:didFinishLaunchingWithOptions, with the activity indicator being kicked off as part of rootViewController's viewDidLoad – Greg Nov 07 '11 at 01:47
  • actually - in terms of "kick off your background thread", I wonder then whether this really needs to start at the "viewDidAppear" as opposed to "viewDidLoad" – Greg Nov 07 '11 at 01:51
  • Well, the order doesn't really matter. Like I said: as long as you're hiding and showing the activity indicator on the main thread, you shouldn't have a problem. In the event your model loads before you have a chance to display the indicator, just don't show it. Otherwise, there's no way your model will load before your activity indicator gets added to the view hierarchy. The only thing to make sure of, I suppose, is that your model doesn't load before your controller is initialized. – Cameron Spickert Nov 07 '11 at 01:51
1

Once you're finished loading in the background, call the following from your background thread:

[self performSelectorOnMainThread:@selector(backgroundLoadingDidFinish:) withObject:nil waitUntilDone:NO];

And then implement -(void)backgroundLoadingDidFinish:(id)sender in your RootViewController. If you need to, you can pass data back in the above method (the withObject: part).

Stuart
  • 36,683
  • 19
  • 101
  • 139
  • thanks StuDev - mind if I ask you too re how to make sure that when "modelLoaded" is called, that the main thread had got far enough along showing the UI wiht the activity indicator? i.e. how to deal with the potential issue of the "modelLoaded" method trying to hide the activtyIndicator when it may have not been setup/displayed yet? – Greg Nov 07 '11 at 01:43
  • @Greg: I think that Cameron's comment under their answer covers what you're asking - load your indicator first, then start loading data in the background. In any case, you can always check for the presence of the activity indicator before hiding it, and remember that calling `removeFromSuperview` on `nil` (i.e. if your activity indicator doesn't yet exist) is perfectly valid, and nothing bad will happen. – Stuart Nov 07 '11 at 01:56
  • thanks StuDev - good point about the "calling removeFromSuperview on nil is valid" – Greg Nov 07 '11 at 02:08