- Home /
How do I pass data up nested IEnumerators?
I would like to know if I'm going about this the wrong way...
My intention is to create a set of classes that interact with my server-side API to provide pseudo ORM functionality. For example...
Bootstrap <> User <> API <> HTTP
Mapper <> Map <> API <> HTTP
Really the only class that should be a MonoBehaviour is the one at the top of each chain.
That would then "new" up a User/Map class.
These would contain properties and methods that specifically relate to those API services
The API class would be more generic, handling the specific properties of the above classes, url serialization and executing through...
The HTTP class, where the final http request is conducted through UnityWebRequest
This would conform to the single responsibility pattern. The issue I'm having is the web request needs to be in a coroutine and as the top class needs to be aware of the response content I need to pass it up the classes in a horrible chain of IEnumerators and yield returns. Using the top answer here... http://answers.unity3d.com/questions/24640/how-do-i-return-a-value-from-a-coroutine.html I've attempted to implement the below.
An example...
Loader Class creates new user and calls getUser() on it
User class creates an API object and passes the service locator "user" to the get() method
API class serializes "user", domain and SystemInfo.deviceUniqueIdentifier to create url "http://example.com/user/abcd1234"
API class calls HTTP class GET with url
HTTP class executes UnityWebRequest.Get(url)
API class should wait for response on HTTP
User class should wait for response on API
Loader class should wait for response on User
Loader class does something with response
Needless to say, I don't think posting the actual code is worthwhile as it doesn't work, besides I think it's quite messy even if I did get the data handoff correct. I suppose I could add these all to their own gameobjects as MonoBehaviours and check for a response every "Update" but that also seems incorrect. Same goes for some sort of singleton pattern, as ideally I'd like to quickly instantiate an object that handles a request/response when required. Of course, perhaps I shouldn't be saying what is "incorrect", I'm open to any thoughts.
Should I be trying to implement this as above?
If so, can you help me pass the data back up to the Loader class?
If not, how can I implement this without duplicating code and without creating a god class?
This is a lot about design patterns and the way I should be laying out my scripts really... I feel that by understanding this I'll be in good stead with planning out my objects and classes in the future.
Thank you very much.
Update
This was also posted on the forums... http://forum.unity3d.com/threads/game-script-structure-design-pattern-and-asynchronous-methods.413314/
The main difference is I showed an example of what I would have done if this were a synchronous job...
using UnityEngine;
using Project.API;
namespace Project
{
public class Loader : MonoBehaviour
{
void Start () {
User User = new User();
if (User.id != null) {
Debug.Log("User found");
}
}
}
}
namespace Project.API
{
public class User
{
private string service = "user";
private API api;
public int? id = null;
public User() {
api = new API();
object user = api.get(service);
id = user.id;
}
}
}
using UnityEngine;
using Project.HTTP;
namespace Project.API
{
public class API
{
private string domain = "http://example.com/";
private Connection connection;
private string device_id;
public API() {
connection = new Connection();
device_id = SystemInfo.deviceUniqueIdentifier;
}
public object get(string service, string[] parameters = null) {
return connection.get(domain + service + "/" + device_id + serialize(parameters));
}
private string serialize(string[] parameters) {
string serialized_parameters = "/";
if (parameters != null) {
for (int i = 0; i < parameters.Length; i++) {
serialized_parameters += parameters[i] + "/";
}
}
return serialized_parameters;
}
}
}
using webRequest; // Fake synchronous web request library
namespace Project.HTTP
{
public class Connection
{
public object get(string url) {
return www.get(url);
return new object();
}
}
}
Answer by jdean300 · Jun 26, 2016 at 05:14 AM
Perhaps I'm not understanding your problem very well, but it seems to me like the only part of this that needs to be a coroutine is the HTTP request. After that, could you just communicate through events?
HTTP:
public delegate void OnHTTPRequestFinishedDelegate(Data data);
public event OnHTTPRequestFinishedDelegate OnHTTPRequestFinished;
public IEnumerator DoRequest(string url)
{
//yield return yada yada yada until request is done
if (OnHTTPRequestFinished != null)
OnHTTPRequestFinished(/*pass your data here*/);
}
Your API class can subscribe to this event, and in the event handler perform any additional operation required at the API level. You could continue this pattern: API would have an event for when it is finished, which the Map level class subscribes to. The Map level handler does whatever additional operations it needs, then fires it's own complete event, which the Mapper class can subscribe to and act upon.
I thought perhaps the question was more suited to the forums and that it may not pass approval here so i wrote the following which includes more information...
It's an interesting thought to use Eventing to solve this, and it's not something I have utilised in my Unity journey as of yet. Are the fired events limited in scope to the stack in which they were called or not? I.e. if I have multiple webRequests firing at the same time, will the correct Event Listener be matched to the response event?
There are scoped to the object they are called on. If you are going to have multiple requests going with each HTTP level object, then you could pass an identifier with the data you send with the event and then act accordingly.