0

I have a UITableView in my ViewController. To populate it, I have to make an async request that may take up to a second to complete. Where should I put it?

When I tried to make ViewDidLoad async and make a call from there, ViewWillLayoutSubviews throws an error, because it tries to position some elements that weren't assigned yet - due to the fact that not all code from ViewDidLoad was executed yet (I think).

nicks
  • 2,161
  • 8
  • 49
  • 101

3 Answers3

2

Before awaiting anything in ViewDidLoad you need to setup all your view logic. Otherwise your view initialization will not be finished when ViewDidLoad method returns. That could be a potential cause for ViewWillLayoutSubviews to fail. If it still fails, use a try/catch to make sure your service is working:

public override async void ViewDidLoad()
{
    base.ViewDidLoad();

    // setup all the view elements here (before the async call)

    try 
    {
        var results = await MakeYourAsyncRequest();

        InvokeOnMainThread(() =>
        {
            _tableView.Source = ...; // do something with the results
            _tableView.ReloadData();
        });
    }
    catch(Exception ex)
    {
        // do something with the exception
    }
}
xleon
  • 6,201
  • 3
  • 36
  • 52
  • okay. but why `Task.Run` and `InvokeOnMainThread` instead of making `ViewDidLoad` `async` and making `await`able requests from there? – nicks Feb 13 '17 at 14:50
  • Basically because ViewDidLoad is a method defined and called by iOS and is not async. Making it async in C# doesn´t mean iOS will await for it. That´s why `ViewWillLayoutSubviews` could be failing – xleon Feb 13 '17 at 14:55
0

Try putting the tableView.ReloadData(); method inside

dispatch_async(dispatch_get_main_queue(), ^{} this might solve your issue.

-:As a general rule, you should try to make sure that all of your UI interaction happens on the main thread. And your data fetching task will work in background. It looked like you were calling reload Data from your background thread, which seems risky.

Randomguy
  • 192
  • 1
  • 11
0

Depending on the data I would put the call in the AppDelegate. When the app launches the data should be fetched and saved.

When your UITableview appears it will already have the data ready or maybe an error message since you already know the result of the fetch.

The data may change thats why I would also put the fetch call in viewWillAppear() of your ViewController with the UITableview.

ViewDidLoad() is a method that gets called only once. Also it is called as the first method of the ViewController lifecycle. It would be good if you read a bit about it VC lifecycle. You can help yourself by trying it in code with printf("method name").

Community
  • 1
  • 1
Darkwonder
  • 1,149
  • 1
  • 13
  • 26
  • I think that getting data on AppDelegate is really tricky unless you need it for the app initialization. Imagine you´ve got 20 view controllers, each of those making async requests. Would you make all those requests in AppDelegate? – xleon Feb 07 '17 at 05:33
  • I agree with you @xleon. I don't know his use case scenario. I would do it only if I need it for app init like you mentioned or if the data is important but might change when the user doesn't use the app. – Darkwonder Feb 07 '17 at 10:29