Since the release of this article, the iOS SDK for Mobile Services has changed so that you can no longer send a zero over as the ID. This will lead to an invalid ID error. I've adjusted the code shown here to send over the value 1 for the ID. Since the ID is ignored on the server side for our storage project, this shouldn't affect anything.
This article is the companion to one I just posted about connecting Mobile Services to Windows Azure Table and Blob Storage. Prior to taking a look through this article and the mentioned code, I would go through the original article on Mobile Services and Windows Azure Storage. This article will cover how to connect the Mobile Service we set up there with an iOS client using the Mobile Services SDK for iOS. All of the source code for this iOS app is available here in GitHub. Since this app deals with talking to Table and Blob Storage through Mobile Services, I’m only going to highlight here when we’re talking to Mobile Services from iOS and maybe a few other important pieces. If you want to see how the entire app works (such as how we display table names and blob names in a table view) take a closer look at the source code.
Before you run the app
The only thing you need to do before you run this app is adjust the init method of the StorageService class to use your Mobile Service’s URL and Application Key. You can grab these by logging into the Windows Azure portal, going to your Mobile Service, and clicking the MANAGE KEYS button when viewing your service’s dashboard. Those values go into the MSClient’s clientWithApplicationURLString method call.
The Storage Service
All of the “action” for talking to our Mobile Service is done inside of the StorageService class. For that reason, the majority of the code we’ll look at today is from that class. The first thing we’ll look at is the init method for the class:
As mentioned above, you’ll need to set the mobileserviceurl and applicationkey to your Mobile Service’s before running. After that we’re just setting up some local properties like the tables we’ll use to get access to each of the Mobile Service’s tables we set up in the first article. That’s all there is as far as setup goes. Now let’s look at each individual operation in the class.
Reading tables is done in the same way we would if we were going to read data normally from Mobile Services:
Since the iOS SDK relies upon NSDictionary which isn’t typed, we don’t need to do anything special to get the data on tables out and into a local array. After we pull down that table, we can go through the array and use each item’s TableName property to fill our table view.
Creating tables is just like a normal operation where we’re just going to pass the table name over in the NSDictionary we’re going to insert:
For deleting tables, we have to do things a little differently than normal Mobile Services’ deletes:
Here, we’re passing the table name over as parameters. This is necessary because when you call delete, everything is stripped out of the JSON item that is sent over but the id. The idea is that on the server side you just delete by ID so you don’t need to send the other information over the wire. This means we HAVE to pass the table name over through some other means, hence, parameters.
Reading table rows
Once we’ve tapped on a table in our table view, we can then call the TableRows read method to get all of the row data back:
Here we’re constructing a custom query string to pass into the readWithQueryString method. We’re doing this because on the server side we’re looking at the request.parameters object to get the table name we’re going to read. If you look at how we’re handling displaying the data for each row in the table view, you’ll see some “somewhat” complicated code that I pulled out of the old Windows Azure Toolkit. Since just displaying the data in a table view like that is pretty unlikely to happen, I’m not too worried about how the data is exactly displayed as long as it works.
Inserting table rows
Inserting rows requires us to send over both the NSDictionary with all of the row properties as well as a set of parameters with the table name in them:
Deleting table rows
Just like for deleting tables, we need to handle things a little differently to delete table rows:
Here our parameters have three items in them: the table name, the Partition Key, and the Row Key.
Updating table rows
Just like with inserting, we pass over the NSDictionary with all of the properties on it and the parameters array with the table name in it:
Table Storage itself handles updating the item by the table name, Partition Key, and Row Key. Now that we’ve looked at all of the Table operations, let’s move over to Blobs.
Reading the containers is a normal read against that table:
Creating retainers requires us to pass in two things, the NSDictionary item with the container name, and a NSDicationary of parameters:
The parameters contain the flag for whether or not the container should be public or private. If the container is created as public, we’ll be able to load up the blobs without needing a Shared Access Signature (SAS, but more on that later).
Deleting containers, just like deleting tables, requires that we send an id over. We can’t send the container name as part of the item with the id because everything but the id will get stripped off:
Instead the container name is sent over as part of the parameters.
Reading the blobs just requires us to send over the name of the container we’re looking in. Just like when we’re getting table rows, we do this with a custom query string:
Inserting blobs and loading the data
Creating new blobs is handled a little bit interestingly. In order to create a blob, our Mobile Service tells Blob Storage it wants to create a new blob with a specified container name and blob name. It does this by generating a Shared Access Signature which we can use as part of a URL to then post the blob data on the client. So, from the client we call the getSasUrlForNewBlob method:
This passes the container and blob name over as parameters and it comes back with the SAS URL from the server. Once we get that back, we can then post those bytes from the client which it does with a NSMutableURLRequest and NSURLConnection. Now in the case of private containers where we can’t just use the URL of the blob to access the data,(in our case we’re saving images, so we can’t just use the blob URL to set the source of the image) we need to create a SAS URL to access that data. This method pulls double duty by fetching a read and write URL from the server. In reality we might want to split this functionality between two methods for security purposes but for this demo it’s been combined. You can see how we’re using this to load the data by checking out the tappedLoadWithSAS method in BlobDetailsViewController.m.
Deleting blobs is similar to deleting tables or containers:
We pass over an empty id and then put the container and blob name in the parameters that get sent over.
Finished with storage
That’s all we need to do from the client side. Many of these method calls you’ve seen don’t really do anything differently than calls to Mobile Services when we’re using SQL Database storage normally. Thankfully we can abstract our client away enough to not worry about some of these things. In the case of methods where we need to pass additional information, like deletes where there isn’t a JSON item being sent over, we use the option parameters value to send the information over. One important point to make is that for the purposes of this demo, everything should work regardless of what information is stored in the tables (and to some degree the blobs). In reality if you were building an application that was going to make use of Blob and Table Storage from Mobile Services, you’d in theory know what data was in your tables and blobs so things wouldn’t be handled as generically as I’ve done in this client application.
For this application I wanted to explore a new way of reloading the data on my table views. Normally I would generate delegates and trigger a data refresh on the delegate, which would reload the table view, whenever the data changed. For this sample I used NSNotificationCenter instead. The table view classes are setting themselves as observers of whatever the reload method is (for example refreshTableRows, refreshBlobs, etc and whenever the data changed, a notification was sent to say that data had changed. This was a pretty easy way to handle sending information back that didn’t require very much work (and arguably less code than implementing the delegates that would be used instead).
Today we looked at how to implement a client that connects to a Mobile Service that uses Table and Blob Storage. Some aspects of it are no different from using normal SQL Database storage. Some of it requires a few additional steps. With the knowledge from this article and the server side one, you should now know how to comfortably combine SQL Database, Table, and Blob Storage. A good knowledge of Blob Storage and getting SAS URLs (and how to upload blobs with them) is important even if you stick to SQL Storage since it, and the JSON that is used between the client and Mobile Services, isn’t really meant for file storage. Good luck and remember that the source code for this and the server side scripts is all available here on GitHub.