How to use Coroutines and Callback properly? Retrieving an array out of an IEnumerator

Hi folks,
we have some trouble with an IEnumerator and don’t know how to retrieve an array out of it. We found out that you have to do it with a callback , but we don’t really know how to use it.
Here is the code of the IEnumerator and the void that needs to receive a string array from it.

    public void StartRoutineGetProjects(string username, string password, string url){
        StartCoroutine(GetProjects(username, password, url));
       
        // here we dont know how to receive the array, need some help here
    }

    public IEnumerator GetProjects (string username, string password, string url, Action<string[]> callback)
    {
        string privateURL = "http://" + url + "/Unity/myprojects.php";

        WWWForm form = new WWWForm ();
        form.AddField ("username", username);
        form.AddField ("password", password);

        // Send WWWForm
        WWW projects_get = new WWW (privateURL, form);

        if (projects_get.error != null && projects_get.error != "") {
            Debug.Log ("Internal Error");
        } else {
            // splitting the result at "|"
            string[] tempProjects = projects_get.text.Split ("|".ToCharArray ());
           
            yield return tempProjects;
           
            callback(tempProjects) // <-- here we want to return the array

        }
    }

We would be glad for every help we can get.

2 Likes

You’re missing a yield on your WWW object. This will allow you to wait until it’s complete.

WWW projects_get = new WWW (privateURL, form);
yield return projects_get;

Remove this line

yield return tempProjects;

You need to pass in something that will get called as the 4th parameter. It will receive a string array.

E.g.

public void StartRoutineGetProjects(string username, string password, string url){
        StartCoroutine(GetProjects(username, password, url,MyMethod));
 
    }

void MyMethod(string[] stringArray)
{
    // Do something with stringArray
}

Or perhaps use a lambda expression

public void StartRoutineGetProjects(string username, string password, string url){
        StartCoroutine(GetProjects(username, password, url,(stringArray)=>{
            // Do something with stringArray
        }));
 
    }

Also think about passing null back when you have your error condition so that you know that the async operation has completed (but failed). And handle it appropriately.

E.g.

Debug.Log ("Internal Error");
callback(null);
2 Likes

Thank you very much for your help, I changed my code as you said.
Unfortunately I missed something in my first post. The void function is a string[ ]-function as well, that shall pass the array to another script. Here is the code , how it looks now. I have to return the string[ ] somehow , but i don’t know exactly where to do this.

    public string[] StartRoutineGetProjects(string username, string password, string url){
        string[] temp;

        StartCoroutine(GetProjects(username, password, url,(stringArray)=>{
            temp = stringArray;
           // return temp does not work here
        }));
       // and here i dont have access to temp or stringArray , so i cannot return it here
    }

    public IEnumerator GetProjects (string username, string password, string url, Action<string[]> callback)
    {
        string privateURL = "http://" + url + "/Unity/myprojects.php";

        WWWForm form = new WWWForm ();
        form.AddField ("username", username);
        form.AddField ("password", password);

        // send WWWForm
        WWW projects_get = new WWW (privateURL, form);

        // Receiving projects
        yield return projects_get;

        if (projects_get.error != null && projects_get.error != "") {
            Debug.Log ("Interner Fehler");
            callback (null);
        } else {
            // splitting the result at "|"
            string[] tempProjekte = projects_get.text.Split ("|".ToCharArray ());

            callback (tempProjekte);
        }
    }
1 Like

Well you can’t return it because it doesn’t even get created until the async www has completed (with success). You can add an Action to StartRoutineGetProjects and handle it from wherever that gets called from.

public void StartRoutineGetProjects(string username, string password, string url, Action<string[]> callback){
        StartCoroutine(GetProjects(username, password, url,callback));
      }
2 Likes

And how do I handle the callback from another script? I have another script that created an object of this class. I somehow need to get access to the array from there. I think it would be best if i post my other script here as well, so you might understand what i am trying to do.

    public void LoginStart()
    {
        try{
            userName = inputUsername.text;
            password = inputPassword.text;
            dbscript.StartRoutineCheckLoginCorrect(userName,password,url);
          
            // dbscript is an object of the previous mentioned class.
            // At this point we need to create an array , that will be filled with
            // the array from the other script.
            dbscript.StartRoutineGetProjects(userName,password,url, callbackvariable);
          
            // Switching UI
            if (dbscript.login == true) {
                Debug.Log("Login works!");
                menuvisibilityscript.HideLoginMenu ();
                menuvisibilityscript.ShowProjectsMenu ();
            }
        }
        catch(NullReferenceException ex){
            Debug.Log (ex.ToString ());
        }
    }
1 Like

Well getting the array is an operation that takes time so you’ll have to wait until it’s complete. Your login will have to wait.

IEnumerator Login()
{
    string[] stringArray = null;
    bool wait  = true;
    dbscript.StartRoutineGetProjects(userName,password,url, (callbackvariable)=>{
        // Async op completed.
        stringArray = callbackvariable;
        wait = false;
    });

    while(wait) yield return null;

   // Do something with stringArray
}
2 Likes

[SOLVED] Yes finally it works. Thank you very much. We don’t want to withhold our solution, so others can benefit from it.
So here is the code.
First the databasescript:

    public void StartRoutineCheckLoginCorrect (string username, string password, string url, Action<string[]> callback)
    {
        StartCoroutine (Login (username, password, url, callback));
    }

    IEnumerator Login (string username, string password, string url,Action<string[]> callback)
    {
        string loginURL = "http://" + url + "/Unity/mylogin.php";
        WWWForm form = new WWWForm ();
        form.AddField ("username", username);
        form.AddField ("password", password);

        WWW users_get = new WWW (loginURL, form);

        yield return users_get;

        if (users_get.error != null && users_get.error != "") {
            Debug.Log ("Login failed");

        } else {
            string[] temp = users_get.text.Split ("*".ToCharArray ());

            if (temp.Length <= 2 || temp [0].ToString () == "Username or password false") {
                Debug.Log (temp [0].ToString ());
                login = false;
            } else {
                Debug.Log ("Login succeeded");
                login = true;
                callback (temp);
            }
        }
    }

    public void StartRoutineGetProjects(string id, string username, string url, Action<string[]> callback){
        StartCoroutine (GetProjects (id, username, url,callback));
    }

    public IEnumerator GetProjects (string id, string username, string url, Action<string[]> callback)
    {
        string privateURL = "http://" + url + "/Unity/myprojects.php";

        WWWForm form = new WWWForm ();
        form.AddField ("id", id);
        form.AddField ("username", username);

        WWW projects_get = new WWW (privateURL, form);

        yield return projects_get;

        if (projects_get.error != null && projects_get.error != "") {
            Debug.Log ("Interner Fehler");
            callback (null);
        } else {
            string[] tempProjekte = projects_get.text.Split ("|".ToCharArray ());

            callback (tempProjekte);

        }
    }

Second the Loginscript: Here we get Access to the variables from the databasescript.

    public void LoginStart ()
    {
        StartCoroutine (Login ());
    }

    IEnumerator Login ()
    {
        userName = inputUsername.text;
        password = inputPassword.text;

        string[] userData = null;
        bool wait2 = true;
        dbscript.StartRoutineCheckLoginCorrect (userName, password, url,(callback) =>{
            userData = callback;
            wait2 = false;
        });

        while (wait2) {
            yield return null;
        }

        id = userData [0];

        string[] stringArray = null;
        bool wait = true;
        dbscript.StartRoutineGetProjects (id, userName, url, (callback) => {
            stringArray = callback;
            wait = false;
        });

        while (wait) {
            yield return null;
        }

        FillProjectsDropdown (stringArray);
    }
3 Likes