Skip to content

Instantly share code, notes, and snippets.

@cloned
Last active October 12, 2017 16:45
Show Gist options
  • Save cloned/fe255b4f7d6feacee319 to your computer and use it in GitHub Desktop.
Save cloned/fe255b4f7d6feacee319 to your computer and use it in GitHub Desktop.
A handy class to use WWW and WWWForm on Unity.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace WWWKit
{
/// <summary>
/// A handy class to use WWW class and WWWForm class.
///
/// Features:
///
/// * Use callback (delegate) instead of coroutine. Of course this class uses
/// coroutine internally not to stop other processes.
/// * Can use timeout.
/// * Handle complex constructor of WWW class.
///
/// Requirements:
///
/// * Unity 4.5
///
/// Example usage:
///
/// using WWWKit;
/// public class WWWClientExample : MonoBehaviour
/// {
/// void Start()
/// {
/// // GET request
/// WWWClient client = new WWWClient(this, "http://example.com/");
/// client.OnDone = (WWW www) => {
/// Debug.Log(www.text);
/// };
/// client.Request();
///
/// // POST request
/// WWWClient http = new WWWClient(this, "http://example.com/");
/// client.AddData("foo", "bar");
/// client.OnDone = (WWW www) => {
/// Debug.Log(www.text);
/// };
/// client.Request();
///
/// // POST request with binary data (file attachment)
/// byte[] binary = System.Text.Encoding.Unicode.GetBytes("bar");
/// WWWClient http = new WWWClient(this, "http://example.com/");
/// client.AddBinaryData("foo", binary, "test.txt", "application/octet-stream");
/// client.OnDone = (WWW www) => {
/// Debug.Log(www.text);
/// };
/// client.Request();
///
/// // Handle error
/// client.OnFail = (WWW www) => {
/// Debug.Log(www.error);
/// };
///
/// // Handle timed out
/// client.OnDisposed = () => {
/// Debug.Log("Timed out");
/// };
///
/// // Set timeout time (default is infinity)
/// client.Timeout = 10f;
///
/// // Add header
/// client.AddHeader("Cookie", "cookiename=cookievalue");
/// }
/// }
/// </summary>
public class WWWClient
{
public delegate void FinishedDelegate(WWW www);
public delegate void DisposedDelegate();
private MonoBehaviour mMonoBehaviour;
private string mUrl;
private WWW mWww;
private WWWForm mForm;
private Dictionary<string, string> mHeaders;
private float mTimeout;
private FinishedDelegate mOnDone;
private FinishedDelegate mOnFail;
private DisposedDelegate mOnDisposed;
private bool mDisposed;
public Dictionary<string, string> Headers
{
set { mHeaders = value; }
get { return mHeaders; }
}
public float Timeout
{
set { mTimeout = value; }
get { return mTimeout; }
}
public FinishedDelegate OnDone
{
set { mOnDone = value; }
}
public FinishedDelegate OnFail
{
set { mOnFail = value; }
}
public DisposedDelegate OnDisposed
{
set { mOnDisposed = value; }
}
public WWWClient(MonoBehaviour monoBehaviour, string url)
{
mMonoBehaviour = monoBehaviour;
mUrl = url;
mHeaders = new Dictionary<string, string>();
mForm = new WWWForm();
mTimeout = -1;
mDisposed = false;
}
public void AddHeader(string headerName, string value)
{
mHeaders.Add(headerName, value);
}
public void AddData(string fieldName, string value)
{
mForm.AddField(fieldName, value);
}
public void AddBinaryData(string fieldName, byte[] contents)
{
mForm.AddBinaryData(fieldName, contents);
}
public void AddBinaryData(string fieldName, byte[] contents, string fileName)
{
mForm.AddBinaryData(fieldName, contents, fileName);
}
public void AddBinaryData(string fieldName, byte[] contents, string fileName, string mimeType)
{
mForm.AddBinaryData(fieldName, contents, fileName, mimeType);
}
public void Request()
{
mMonoBehaviour.StartCoroutine(RequestCoroutine());
}
public void Dispose()
{
if (mWww != null && !mDisposed)
{
mWww.Dispose();
mDisposed = true;
}
}
private IEnumerator RequestCoroutine()
{
if (mForm.data.Length > 0)
{
// Overwrite added headers with WWWForm.headers because WWWForm.headers may have required
// headers to request. For example, WWWForm.headers has Content-Type like
// 'multipart/form-data; boundary="xxxx"' if WWWForm.AddBinaryData() is called.
foreach (DictionaryEntry entry in mForm.headers)
{
mHeaders[System.Convert.ToString(entry.Key)] = System.Convert.ToString(entry.Value);
}
// POST request
mWww = new WWW(mUrl, mForm.data, mHeaders);
}
else
{
// GET request
mWww = new WWW(mUrl, null, mHeaders);
}
yield return mMonoBehaviour.StartCoroutine(CheckTimeout());
if (mDisposed)
{
if (mOnDisposed != null)
{
mOnDisposed();
}
}
else if (System.String.IsNullOrEmpty(mWww.error))
{
if (mOnDone != null)
{
mOnDone(mWww);
}
}
else
{
if (mOnFail != null)
{
mOnFail(mWww);
}
}
}
private IEnumerator CheckTimeout()
{
float startTime = Time.time;
while (!mDisposed && !mWww.isDone)
{
if (mTimeout > 0 && (Time.time - startTime) >= mTimeout)
{
Dispose();
break;
}
else
{
yield return null;
}
}
yield return null;
}
}
}
@omarojo
Copy link

omarojo commented Apr 1, 2015

yes, thats what I thought. So I created a second class that uses WWWkit internally. The problem is that, the moment this class tries to make the second request, it fails.

client.OnDone = (WWW www) => {
       string processedResponse = this.hostController.analyze(www.text); //This object is an instance of a different class, and that method will execute a second http request.
...

I'm getting this error.

NullReferenceException
UnityEngine.MonoBehaviour.StartCoroutine (IEnumerator routine)
WWWKit.WWWClient.Request () (at Assets/Scripts/Artifical Intelligence/WWWClient.cs:161)

There is nothing wrong with the way Im implementing the WWWkit because I've copied that code and ran in independently and it worked fine. So there most be a problem with something else.

@cloned
Copy link
Author

cloned commented Apr 1, 2015

It seems like this.
http://answers.unity3d.com/questions/501082/running-a-monobehavior-script-a-with-a-coroutine-i.html

Test class in my sample code is safe as long as it's instantiated as a Component.

Test test = new Test();
test.Start();

This code occurs the error because Test which extends MonoBehaviour is not instantiated as a Component.

@omarojo
Copy link

omarojo commented Apr 2, 2015

Oh thank you very much. That was the problem, I needed to instantiate the Monobehaviour class a different way. But....

I still noticed that "returns" dont work inside the onDone and onFail blocks.
for example when Im returning a string:
HostRemoteController.cs(112,57): error CS0029: Cannot implicitly convert typestring' to void' HostRemoteController.cs(112,57): error CS1662: Cannot convertlambda expression' to delegate type WWWKit.WWWClient.FinishedDelegate' because some of the return types in the block are not implicitly convertible to the delegate return type

@cloned
Copy link
Author

cloned commented Apr 7, 2015

Sorry for the delay.

Following code is not compatible with WWWKit.WWWClient.FinishedDelegate.

client.OnDone = (WWW www) => {
    return "something";
};

Because the returning type of WWWKit.WWWClient.FinishedDelegate is defined as void.

public delegate void FinishedDelegate(WWW www);

WWWClient.cs, Line 73

Is it okay to call another method like this?

client.OnDone = (WWW www) => {
    String foo = "want to use this outside";
    SomeAnotherMethod(foo);
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment