0

First of all, I'm using Storyboards, the Parse backend and objective-c (IOS8 project)

I've a got a ViewController that downloads a specific column from the row that is currently open. I also got a container within this ViewController that sends a new query, but that query needs the column value from VC1. As this doesn't download before the container launches, an exception is thrown as it tries to compare with (null).

Like so: (valueFromMainVC is null until VC1 completes its download)

[query whereKey:@"MyColumn" equalTo:valueFromMainVC];

this happens in the -(PFQuery *)queryForTable method

-(PFQuery *)queryForTable {
}

How can I solve this? I tried to use a while(){} statement so that it wouldn't begin until valueFromMainVC is downloaded, but I didn't have any success with that.

(I'm using Singleton to share valueFromMainVC between ViewControllers. As well as other values)

Help is much appreciated!

Thanks! Erik

Erik
  • 2,500
  • 6
  • 28
  • 49

2 Answers2

0

I haven't used Parse before, so my answer is going to be general. You might want to edit your question and put "Parse" in the title: "Wait for Parse query to complete before updating UI" or something similar. (That way people with Parse experience will see it and be more likely to open your question)

There are several ways that libraries use to handle async code.

Pretty much all of them involve turning your thinking around. You don't write code that waits in a loop for something to happen. Instead, you display some sort of placeholder, return control to the user, and the library notifies you when the data is read.

One way which is sort of falling out of fashion iss to define a delegate property in the library. The library sends a message to the delegate using a specific protocol once the data is read

Another way to handle this is with the notification center (NSNotificationCenter). You add an observer for a specific message, and the library sends a specific notification once your data is ready.

The third, and most common way for newer code, is to invoke a method in the library that takes a completion block. The completion block is code that doesn't get executed until the data is available. You set up your UI to display a placeholder, and the block of code you pass in updates the UI with the data once it's available.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
0

I've used Parse on a few projects now and I've had good success using strategy three @Duncan mentioned. I would wait for the first VC to load and then call a Parse query with a completion block to update the column value then load the child view in the completion block of the successful query. This prevents the child VC from being loaded until after the query for data has been completed. Hope this helps!

- (void)viewDidLoad {
  self.functionToLoadData();
}

-(void)functionToLoadData {
    PFQuery *query = [PFQuery queryWithClassName:@"invites"];
    [query whereKey:@"MyColumn" equalTo:valueFromMainVC];
    [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
        if (error) {
            NSLog(@"Error retrieving objects");
        } else {
            // Update necessary variables/fields and load your child view as necessary
        }
    }];
}
dstefanis
  • 247
  • 1
  • 7
  • Not sure I understand. I have a VC with a child VC inside a container that loads once the parent VC loads. It then tries to compare with something that is yet to exist in the app before downloaded. Could you please elaborate what you mean and how this would work? One solution that I thought of would be to somehow call viewDidLoad on the container upon request (this would be called within the completion block of the valueFromMainVC download) instead of the container being launched at once – Erik Oct 30 '14 at 19:18
  • Sorry for the confusion, let me know if I'm still not understanding correctly. Assuming the code I provided above runs in your first VC, the function I provided queries the data for necessary for the original VC and it doesn't launch the child VC until that query for the data has finished and returned successfully. You would put the code to launch the child VC in the `else` statement in my code. – dstefanis Oct 30 '14 at 19:49
  • No problem;) Now I see what you mean, that was what I thought too. How can I "launch" the container on request rather than right away? – Erik Oct 30 '14 at 19:59
  • Cool. Sorry, by launch I meant present the view controller programatically. I know you said you're using Storyboards, so I'd check out this other question which details how to do that with and without storyboards. I believe I've used this successfully in the past http://stackoverflow.com/a/16134703/2292725 – dstefanis Oct 30 '14 at 20:21
  • but if I present it like that, it would take up the whole screen, just like any other modal segue. How can I control the behaviour of a containerView where the VC will be? So that it'll be shown as a control and not a fullscreen ViewController – Erik Oct 30 '14 at 20:41
  • Good point, sorry forgot about that. I don't have as much experience doing this, but you could create a custom nib/xib file for the child VC and then create an instance of it programmatically and add it to the main VC's view. Something along the lines of `ChildVC *test = [[ChildVC alloc] initWithNibName:@"ChildVC" bundle:nil]; [self.view addSubview:test.view];` – dstefanis Oct 30 '14 at 22:58
  • Yeah, might go with that approach. Thanks for the help!:) – Erik Oct 30 '14 at 23:21