I’ve used Parse.com quite a bit for iOS development and been very happy with it. Performance and scaling is good, and working with the cloud based service is very straight forward.
I decided to use Parse.com as the back end database solution for my latest Unity3d project. Parse now has an official Unity SDK that you can download from the Unity3d asset store.
Despite all of the benefits of using Parse with Unity there is one glaring problem. Because of the way the Unity implements their WWW class we can’t access response headers containing error information from Parse operations. So requests sent using the Parse SDK from Unity return responses which can be either successful, failed because of no connection, or failed because of some unknown cause.
Kalekindev has a great blog post addressing this issue. The solution is to create custom cloud code functions on Parse.com and call them instead. The cloud code handles the request and then returns either a success code or an appropriate error message.
Below is my initial cloud code running on Parse (cloud code is in javascript) to handle user signup.
Parse.Cloud.define("SignUp", function(request, response)
{
var user = new Parse.User();
user.set("username", request.params.username);
user.set("password", request.params.password);
user.set("email", request.params.email);
user.signUp(null,
{
success: function(user)
{
response.success(
{
"success": "Sign up successful."
});
},
error: function(user, error)
{
// We must respond with a success in order to access the
// result of the request inside Unity.
response.success(
{
"error": "Sign up failed.",
"code": error.code,
"message": error.message
});
}
});
});
And here is the Unity3d C# code snipped that makes the call to the Parse.com cloud code and handles the return code appropriately if there is an error..
private IEnumerator CloudSignUp(string sUsername,string sPassword,string sEmail)
{
var userInfo = new Dictionary
{
{"username", sUsername},
{"password", sPassword},
{"email", sEmail}
};
var t = ParseCloud.CallFunctionAsync<Dictionary>("SignUp", userInfo);
while (!t.IsCompleted)
{
yield return null;
}
if (t.IsFaulted)
{
var parseException = (ParseException)t.Exception.InnerExceptions[0];
if (parseException.Message.StartsWith("Could not resolve"))
{
Debug.Log("Failure: Connection error.");
objStatus.text="Failure: Connection error.";
objStatusPanel.SetActive(true);
}
else
{
// Possible mystery case?
Debug.Log("Failure: Unknown cause.");
objStatus.text="Failure: Unknown cause..";
objStatusPanel.SetActive(true);
}
}
else // Our request is successful but may still have failed server side.
{
object code;
if (t.Result.TryGetValue("code", out code))
{
// Handle our call specific errors here.
Debug.Log(string.Format("Failure: Code {0}", code));
string sCode = string.Format("{0}", code);
if (sCode.Equals("125")) {
objStatus.text="Email is invalid";
objStatusPanel.SetActive(true);
Debug.Log("bad email");
}
else if (sCode.Equals("202")) {
objStatus.text="Username is already in use.";
objStatusPanel.SetActive(true);
Debug.Log("bad username");
}
else if (sCode.Equals("203")) {
objStatus.text="Email is already in use.";
objStatusPanel.SetActive(true);
Debug.Log("bad username");
}
else {
objStatus.text=string.Format("Failure: Code {0}", code);
objStatusPanel.SetActive(true);
}
}
else
{
Debug.Log("Success!");
}
}
}
This approach can be used for all Parse.com functions called from Unity and gives us a robust way to handle exceptions and errors.