How To Check if APIs are All Finished

I have an issue I’m trying to figure out with my APIs.

So, I have all my APIs downloading on the main menu. I noticed if the player leaves the menu when there are still APIs that haven’t finished, while unlikely, there CAN be minor issues, it’s being used to populate soccer games and if the games haven’t downloaded there might be an issue on the main page… So, I need to check if the APIs have finished before you can load the main game, if they haven’t I’ll display a loading screen and an error if it takes too long…

There’s another reason I want to check, what if one of the APIs fails because of a connection issue? I’d like to check which ones have failed and call them again.

I have one issue, I have 9 different lists being populated with the API calls, most are custom classes. So I was going to put them in a list and check their lengths, but I can’t because they are all different variables. I tried using generics but couldn’t really get it working…

For the record, here is the script, I know it’s kinda a sloppy way to populate lists with APIs lol(if you have any better ideas, I’m all ears :smile:)

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class APICaller : MonoBehaviour
{
    public List<Texture> flagSprites;
    public List<League> leagues;
    public List<Country> countries;
    public List<Team> teams;
    public List<LiveScores> scores;
    public List<Season> seasons;
    public List<Continent> continents;
    public List<Fixture> fixtures;
    public List<MatchStats> stats;
   
    public Dictionary<int, Team> teamDictionary;

    static APICaller _api;

    public static APICaller api
    {
        get
        {
            return _api;
        }
    }

    private void Awake()
    {

        _api = this;
        DontDestroyOnLoad(gameObject);

    }

    private void Start()
    {
        RequestAPI("Leagues",leagues);
        RequestAPI("Countries",countries);
        RequestAPI("Teams",teams);
        RequestAPI("Scores",scores);
        RequestAPI("Seasons",seasons);
        RequestAPI("Continents", continents);
        RequestAPI("Fixtures", fixtures);

        teamDictionary = new Dictionary<int, Team>();
        for (int i = 0; i < teams.Count; i++)
        {
            teamDictionary.Add(teams[i].id,teams[i]);
        }

    }

    public void RequestAPI<T>(string name,List<T> list)
    {      
        StartCoroutine(WaitForServerResponse(name,list));

    }
   
    IEnumerator WaitForServerResponse<T>(string name,List<T> list)
    {

        using (UnityWebRequest req = UnityWebRequest.Get(URLRefs.url.GetURL(name)))
        {

            yield return req.SendWebRequest();

            string[] pages = req.url.Split('/');
            int page = pages.Length - 1;
      

            switch (req.result)
            {
                case UnityWebRequest.Result.ConnectionError:
                    break;
                case UnityWebRequest.Result.DataProcessingError:
                    Debug.LogError(pages[page] + ": Error: " + req.error);
                    break;
                case UnityWebRequest.Result.ProtocolError:
                    Debug.LogError(pages[page] + ": HTTP Error: " + req.error + " " + name);
                    break;
                case UnityWebRequest.Result.Success:

                    ApiResponse<T> response = JsonUtility.FromJson<ApiResponse<T>>(req.downloadHandler.text);

                    for (int i = 0; i < response.data.Count; i++)
                    {
                        list.Add(response.data[i]);
                    }
                    if (name == "Countries") PopulateFlags();

                    break;
            }

        }

    }

    void PopulateFlags()
    {
        for (int i = 0; i < countries.Count; i++)
        {
            StartCoroutine(GetSprites(countries[i].image_path, i));
        }
    }

        IEnumerator GetSprites(string name, int index)
    {
        UnityWebRequest req = UnityWebRequestTexture.GetTexture(name);
        {

            yield return req.SendWebRequest();

            string[] pages = req.url.Split('/');
            int page = pages.Length - 1;

            switch (req.result)
            {
                case UnityWebRequest.Result.ConnectionError:
                    break;
                case UnityWebRequest.Result.DataProcessingError:
                    Debug.LogError(pages[page] + ": Error: " + req.error);
                    break;
                case UnityWebRequest.Result.ProtocolError:
                    Debug.LogError(pages[page] + ": HTTP Error: " + req.error + " " + name);
                    break;
                case UnityWebRequest.Result.Success:

                    flagSprites.Add(((DownloadHandlerTexture)req.downloadHandler).texture);

                    break;
            }

        }

    }


}

Ideally I could have an array/list of bools and set it to true when the API is finished, then I could check this when you press the login button?

Thanks

What you are calling an “API” is a network request.

Any individual request can be checked via its result, as you do above.

Any collection of work items that you want a program to do can be counted and checked on an individual basis to form an aggregate (total) result.

This general statement applies to network requests as well.

Some possible approaches:

  • keep a list of all of the requests pending and check if they are all done.

  • just count how many have been issued, and count how many have been complete. When those counts are equal, you are done.

1 Like

Sorry, I know the API is the request and not the result. I’m sleepy. lol

Okay, this sounds like a good option. Should I just make a list of coroutines? Is there a way to cycle through all coroutines in a list and see if they’re still running?

Bump? I just realized making a list of coroutines wouldn’t be ideal… because it wouldn’t give me a way to check if the API request was successful or not, just check if the API request is still going… I wonder if I should make the API request attempt again if there’s an error?

Besides that, I think my best option would be to put all these lists into a single list, and check if any of the lists are at 0 count, and if so, repopulate them. But I don’t know how to put them in a list, generics doesn’t seem to be working…

I need a way to check if the API requests were all successful, because I don’t want the player to be able to start the game until they are.

Count up when you issue one, count down when one completes, as I noted in my first response.

Or make 9 booleans, each one tied to a specific request succeeding.

Yeah, but again, what if one fails? I don’t need to just wait until all are finished, i need to make sure all of them successfully finished, or at least all the ones I’m using, not that they’ve all finished, but that they’ve all SUCCESSFULLY finished.

And making 9 booleans might be a little inefficient, I will be adding more API requests down the road, maybe up to 20, it would be a lot of bools to keep track of, i could make a boolean array but I would need some way to identify which bool is tied to which request, rather than just trying to remember which index goes to which API, because I would need to call the API request again if it fails…

That’s just app design:

  1. how long do you wait before you consider that they have failed? (timeout)

  2. what do you do when they fail? reissue?

  3. how do you communicate to the player they can’t start yet?

Inefficient?! You could probably check several million booleans each frame before it even began to show up in the profiler.

Make something (a class) that contains EVERYTHING necessary for a single complete request: original URL, data destination (data consumer), time of last issue, if it is complete, etc.

Put those in a list, check them all and when they’re good, off you go.

This is all just standard data processing stuff.

  1. I was planning on giving it a little delay(15-20 seconds) and trying again, if it fails a certain amount of times, return to menu.

  2. Alright, I’ll try this, that’s what I was thinking of doing

  3. I was going to have a loading screen, if it takes too long, I was going to have it return to the main menu and say there was a connection issue.

I mischose my words, not inefficient as in costly on performance, inefficient as in difficult to work with. Inefficient for me, the programmer to change lol, say I need to remove one API call from the middle(unlikely, but…), now i need to go through the code and decrease every number by 1 to get it working again. Maybe I’m overthinking it.

Alright, I’ll try a mixture of incrementing a number and reissuing every time it fails, i wish webrequests didn’t have so many different error checks haha, maybe i could make a bool that checks if it succeeded, and set it to true on success, if it’s false, call the API again.

Iust a couple of weeks ago I’ve posted this simple example of a “load balancer” in order to download multiple resources and limit the concurrent requests. It will automatically start a single coroutine which will handle all the requests as soon as you queue the first request. Currently it does not handle failed requests, so if you need to handle those you may want to implement an automatic retry three or five times before you signal an error. Of course you would need to change how a task is actually stored so you’re able to resend / recreate the request.

Thanks, I’m okay, I know how to write something like this. My main issue that I need to fix is the failed requests haha. Thanks anyway though.