Can You Control When Custom Business Logic Return a Response to a Device

Either I am making a programming error or I am trying to force Backendless to do something it isn’t designed to do. I’ve created a Custom Business Logic function to register a user. In iOS I gather part of the new user data and pass this to the Custom Business Logic function. In the function, I add more information to the new user and then use the JavaScript API to register the user. There are two reasons I am trying to do this.

  1. I want to be able to change user parameters without having to resubmit the app for approval.

  2. So that I can pass messages back to the iOS device and then report messages to the user. If a username is already taken then I would pass the fault code to the app and test for it. Then I would tell the user that the username was taken.

The issue I am having is that the Custom Business Logic function calls back to the device before the user is registered using the JavaScript API. The means that I end up returning nil. I’ve tried a couple of options in Custom Business Logic.

Option 1 - Trying to set the return variable in the user registration callbacks.

Backendless.ServerCode.customEvent('signUpUserInCloud', function(req)
{
    // Create a new Backendless User and assign the arguments passed from the mobile device.  Also add the additional
    // arguments required for a new user.
    var newUser = new Backendless.User();
    newUser.username = req.args.username;
    newUser.firstName = req.args.firstName;
    newUser.lastName = req.args.lastName;
    newUser.password = req.args.password;
    newUser.accountClaimed = true;


    var response = undefined;


    function signUpSuccessful(user)
    {
        console.log( "I am in the signUpSuccessful.  User registered." );
        response = {"test" : "This Worked!"};
        return response;  // Trying to return response to iOS here.
    }


    function signUpError(error)
    {
        console.log("I am in the signUpError and the error is " + error.message);
        response = { "test" : "Error!" };
		return response;  // Trying to return response to iOS here.
    }


    // Call the JavaScript function to sign up the user.  
    Backendless.UserService.register( newUser, new Backendless.Async(signUpSuccessful, signUpError));
});

Option 2 - Adding the JavaScript User Registration function to a callback of it’s own.

Backendless.ServerCode.customEvent('signUpUserInCloud', function(req)
{
    // Create a new Backendless User and assign the arguments passed from the mobile device.  Also add the additional
    // arguments required for a new user.
    var newUser = new Backendless.User();
    newUser.username = req.args.username;
    newUser.firstName = req.args.firstName;
    newUser.lastName = req.args.lastName;
    newUser.password = req.args.password;
    newUser.email = req.args.email;
    newUser.accountClaimed = true;


    var response = undefined;


	// Place JavaScript user registration in a callback function.
    function signUpUser(callback)
    {
        Backendless.UserService.register( newUser, new Backendless.Async( function signUpSuccessful(user)
        {
            console.log( "I am in the signUpSuccessful.  User registered." );
            response = {"test" : "This Worked!"};
            callback()
        }, function signUpError(error) // see more on error handling
        {
            console.log("I am in the signUpError and the error is " + error.message);
            response = { "test" : "Error!" };
            callback()
        }));
    }


    function returnResponseToDevice()
    {
        return response;
    }


	// Pass returnResponseToDevice as the callback.
        signUpUser(returnResponseToDevice);


});

Both of these return nil, not the value I am trying to return. Even if user registration eventually returns a fault like “User already exists” in the CodeRunner debugger, a return value has already been sent to the iOS device.

Am I making a mistake in JavaScript or am I trying to use Backendless in a way it wasn’t intended to be used?

Hi

You have to return a Promise from your event handler to tell the CodeRunner about your asynchronous operation and to force CodeRunner to wait for it and to use its resolved/rejected value as a real result of the event handler.

Here is an example :

Backendless.ServerCode.customEvent('signUpUserInCloud', function(req) {
  var user = new Backendless.User();
  user.username = req.args.username;
  user.firstName = req.args.firstName;
  user.lastName = req.args.lastName;
  user.password = req.args.password;
  user.email = req.args.email;


  return new Promise((resolve, reject) => {
    Backendless.UserService.register(user, new Backendless.Async(function(user) {
      console.log('user has been registered', user);


      resolve({ test: 'This Worked!' });
    }, reject));
  });
});

Or, it may be a bit shorter and simpler to use native promises support in Backendless SDK :

Backendless.enablePromises();
Backendless.ServerCode.customEvent('signUpUserInCloud', function(req) {
  var user = new Backendless.User();
  user.username = req.args.username;
  user.firstName = req.args.firstName;
  user.lastName = req.args.lastName;
  user.password = req.args.password;
  user.email = req.args.email;


  return Backendless.UserService.register(user).then(user => {
    console.log('user has been registered', user);


    return { test: 'This Worked!' };
  });
});

Excellent. Thanks for the help.

I’m almost there. I’ve spent the last couple of days reading on callbacks and promises. I’m at the point where I understand them and am able to return a response to a device once the task is complete. In CodeRunner I keep getting an error and I don’t know why. Here is my new code.

Backendless.ServerCode.customEvent('signUpUserInCloud', function(req)
{
    // Create a new Backendless User and assign the arguments passed from the mobile device.  Also add the additional
    // arguments required for a new user.
    var user = new Backendless.User();
    user.username = req.args.username;
    user.firstName = req.args.firstName;
    user.lastName = req.args.lastName;
    user.password = req.args.password;
    user.email = req.args.email;
    user.accountClaimed = true;


    // Create the promise for the return
    return new Promise(function(resolve, reject)
    {
        Backendless.UserService.register(user, new Backendless.Async(function(user)
        {
            // The user was successfully registered.  This is the success callback function of Backendless.Async.
            console.log("I am in the signUpSuccessful callback " + user.username);
            resolve({"test" : "This worked!"});
        }, function(error)
        {
            // The user was not registered.  This is the error callback of the function Backendless.Async.
            console.log("I am in the signUpError callback " + error.message);
            reject({"test" : "Error"});
        }))
    });
});

If I sign up a user twice I get the error.message of “User already exists”. However, I then get an error that states the following in CodeRunner.

Error: [object Object]

In iOS I am told the fault that has been returned is nil, which I intend for it to be {“test” : “Error”}. Any ideas what’s going on? Is there an issue with my reject method?

Hi

You can’t pass a complex object as an error. Just error string message (and code using Backendless.ServerCode.Error class).
If you need to pass complex object you should use resolve in both cases, for success and failure :

return new Promise(function(resolve, reject) {
 Backendless.UserService.register(user, new Backendless.Async(function(user) {
 resolve({ "test": "This worked!" });
 }, function(error) {
 resolve({ "test": "Error" });
 }));
});

But in this case you will need somehow to distinguish in iOS was it succeeded or failed.
That’s why, I would recommend you to send an error like it was in my examples. Don’t they work for you ?

The example where you enabledPromises did not work. The other example definitely helped with the code I have above. I’ve changed the code as you suggested and am now passing the error back to iOS. In iOS I test for the faultCode to report the issue to the user.

The only remaining issue I am having is in CodeRunner’s console. While I can print error.message, error.statusCode is undefined. Is there a reason this would be undefined in JavaScript but not in iOS?

// Create the promise for the return
    return new Promise(function(resolve, reject)
    {
        Backendless.UserService.register(user, new Backendless.Async(function(user)
        {
            // The user was successfully registered.  This is the success callback function of Backendless.Async.
            console.log("I am in the signUpSuccessful callback " + user.username);
            resolve({"test" : "This worked!"});
        }, function(error)
        {
            // The user was not registered.  This is the error callback of the function Backendless.Async.
            console.log("I am in the signUpError callback " + error.message);
            console.log("The statusCode is " + error.statusCode);
            reject(error);
        }))
    });

CodeRunner Console Readout

I am in the signUpError callback Unable to register user. User already exists.
15:17:57.438 - Error: Unable to register user. User already exists.
The statusCode is undefined

Xcode Console Readout

Unable to register user. User already exists.
The faultCode is 3033

Hi Jon

I think error.code should work

Thanks Vitaly.