Axway Mobile Backend Services: Posts, associated Photos, and utilizing Alloy Collections to pull it all together

Axway’s Mobile Backend Services (I’ll refer to this as MBS) is a great cloud storage solution that you can use with, literally, just a few lines of code. And the best part is — it’s FREE. The name has changed a few times but in one form or another I have used MBS for close to 5 years now and during that time I have come across some tips/tricks that I hope others could benefit from.

Assumptions

  1. General understanding of Axway Appcelerator (Cross-Platform Mobile Development) and Axway Mobile Backend Services.
  2. Understand how to create a “shell” app within Appcelerator.
  3. General understanding of Alloy data-binding and Collections. There are a few paths one could go here, and Jason recently wrote a blog post describing many other alternatives here — but for this article I am going to use my favorite, RESTe.

Let’s Get Started!

1. Configure RESTe Methods and Collections to retrieve data from MBS

This is not a complete config file, these are just the relevant elements to connect to MBS. I’m not going to explain RESTe here (visit the github for that) but I do want to point out a ‘hack’ I had to implement in order for Collections to work correctly (with MBS).

* Note: The MBS JSON response returns the “posts” array in a child property of “response”
MBS /posts/query JSON Response
  • RESTe was designed to look for the element in the root/parent and not in the child. No worries — the beautiful thing about RESTe is how flexible it is. By simply transforming the JSON response on-the-fly, with just a few lines of code, I was able to work around it and move the “response” object up to the parent. Viola!

Simply add this to either each desired method or globally across all methods:

onLoad: function(e, callback) {
var transform = e.response;
callback(transform);
}]

Then your config should look something like this:

//RESTe Config file
Alloy.Globals.arrowDB.config({
.....
url: "https://api.cloud.appcelerator.com/v1/",
models: [{
name: "name",
id: "id",
collections: [{
name: "queryMyPosts",
content: "posts",
read: "getQueryPosts"
}],
}],
methods: [{
name: "getQueryPosts",
get: 'posts/query.json?where={"content": "<content>"}&order=-updated_at&key=ENV_KEY_HERE&pretty_json=true',
onLoad: function(e, callback) {
var transform = e.response;
callback(transform);
},
}, {
name: "getPhoto",
get: 'photos/show.json?photo_id=<photo_id>&key=ENV_KEY_HERE&pretty_json=true',
}],
});

Alright. We now having a REST connection to MBS and Models & Collections are dynamically created and just waiting for us to use them.

2. Configure our <VIEW> to use our Alloy/RESTe Collection

I could have used ListViews here but, for this application, I chose to use TableViews. Again — I assume you know the basics of TableViews so I am only going to cover the key pieces.

* Note: I assigned my dataCollection to my Collection created in RESTe

dataCollection=”queryMyPosts”

an added a custom transform (we’ll discuss why in a sec)

dataTransform=”transformPicIDToBlob”

<TableView id="myTV" dataCollection="queryMyPosts" dataTransform="transformPicIDToBlob">
<TableViewRow rowId="{id}" rowTitle="{title}">
<ImageView id="photo_url" image="{loading_image}" />
<Label text="{content}" />
<!--More Elements Here-->
</TableViewRow>
</TableView>

3. Configure our <CONTROLLER> to retreieve and load the Collection

a) Fetch our Data

On your postLayout (or by whatever means you desire) you need to ‘fetch’ the REST JSON.

//some func
Alloy.Globals.myTVRowIndex = 0;
Alloy.Collections.queryMyPosts.fetch({
content: "desired_text_here"
});
* Take note of my Global Var, ‘Alloy.Globals.myTVRowIndex’, that I intialize to zero each time I retrieve the data. This will be used later.

We now have all of our ‘Posts’ being retrieved and the {title} and {content} being populated in the TableView! Woohoo!

Oh wait, but what about the image? Yes — that is a problem because MBS saves the photo_id in the “Post” and not the photo_url path. That’s where the dataTransform (we added to the <VIEW>) comes into play.

b) Transform our Data

So, we need to create a function and as the Collection is populating the TableView we need to intercept it, check to see if that ‘Post” has a photo, and if so make another call to retrieve the photo url.

  • If the ‘Post’ has a photo I add a ‘loading’ image (before we retrieve our remote image) otherwise I add an ‘empty’ image. (Tailor to fit your own needs). Then I increment my ‘rowIndex’ and return the transform.
function transformPicIDToBlob(model){
var transform = model.toJSON();
if (transform.hasOwnProperty("photo_id")) {
transform.loading_image = "/loadingImage_thumbnail.png";
getPhotoURL(Alloy.Globals.myTVRowIndex, transform.photo_id);
} else {
transform.loading_image = "/transparent.png";
}
Alloy.Globals.myTVRowIndex++;
return transform;
}

* Note: My use of the ‘rowIndex’ was so that I could retroactively update the image on the appropriate row (once the image is retrieved). I went back-and-forth on a few ways to implement this, like getting the tableview data, getting the rows, looking up my rowId, to get the rowIndex, etc… — but in the end I decided to keep it simple. KISS.

c) Get the “Post” Photo URL

Initially I tried to make the REST call for the photo inside the transform and quickly ran into issues with an error like:

message = "undefined is not an object (evaluating '__alloyId114.--transform.id')";

The reason? In a word — ‘async’. The transform was so quick that it was already moving onto the next row and once the photo was retrieved from the remote server it couldn’t update the row because, well, it wasnt on that row anymore. doh.

So, I moved that out into its own function — which follows a more “pure function” methodology anyways.

So, upon receiving the rowIndex and photoID, I make a call to get the photo url and update the corresponding row.

function getPhotoURL(rowIndex, photoID){
Alloy.Globals.arrowDB.getPhoto({
photo_id: photoID
}, function(img) {
$.myTV.data[0].rows[rowIndex].children[0].image = img.response.photos[0].urls.original;
});
}
  • Note: The use of ‘children’ is to drill down into the actual element of the row that I want to change. Depending on the # of Views, nested Views, Labels, etc… you may have several ‘children’. I may come back and modify this to be more ‘dynamic’ and interchangeable with all my apps — but it works and is solid — as long as you dont change your <VIEW> layout.

Viola! You now have MBS as your cloud storage (FREE from Axway), being pulled in via RESTe, populating an Alloy Collection, and making an additional call (as needed) to get the Photo URL and update the respective TableViewRow.

I am sure there are some ‘better’ ways to accomplish some of the above but these are working (GREAT) for me and are very simple in nature. I welcome any feedback or suggestions.