READ THIS FIRST

Update: 3-4-2016: If you're finding this blog and looking for information related to Azure Mobile Services, I'd strongly recommend checking out Azure Mobile Apps. Azure Mobile Apps is a new version (consider it a v2) of Azure's mobile backend support. All of the same features of Azure Mobile Services are there, with a lot of other very cool features to go along. You can read more about Azure Mobile Apps, and how to transition from Azure Mobile Services, here.

Mobile Services with Android Update 8-29-2013:
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

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

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:

Deleting tables

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 containers

Reading the containers is a normal read against that table:

Creating containers

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

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 blobs

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

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.

Using NSNotificationCenter

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).

Conclusion

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.


Chris Risner


39 Comments

Paulo Cauca

Hello, Chris, can you help me about dificult bellow ?

How can I pass parameters to read function in Windows Azure mobile service ?

In code bellow I call the read function , but I want pass Parm1 to manipulate in Windos Azure.

[itemTable readWhere:predicate completion:^(NSArray *items, NSInteger totalCount, NSError *error) {

I want in Windows Azure make something like this...

function read(query, user, request) {
var parm1 = query.parameters.Parm1;

request.execute();
}

Tks for help me.

Paulo

Chris

Hi Paulo,

To pass parameters, you'll need to use MSTable's readWithQueryString method. So something like this:

NSString *queryString = [NSString stringWithFormat:@"param1=%@", @"MyParam"];
[self.tableRowsTable readWithQueryString:queryString completion:^...

Then in your script you access it using the request object like this:
request.parameters.param1

Chris

Mosib

Hi Chris,

I have UISearchBar in my app and i want to search for data against string from particular column in windows azure mobile service.. How can i put the condition for checking if any row's text matches with my sent string. like for example here is my query :

select * from table where column1 like "%" + user_search +"%"
and column2 = myAnotherCondition

Quick reply will be highly appreciated.
thanks!

Chris

Sorry for the slow reply Mosib. If you're just doing queries against the built in SQL Storage (i.e. Mobile Services Tables) you'll use NSPredicates and the MSQuery object. Take a look at the quick start todo application to see how a simple query is done and then you can look at the MSQuery reference here: http://www.windowsazure.com...

hector enrique martinez reyes

Hello Chris
in Blob storage the url is mobile serivce or account name to release a connection ?
for example:

MSClient *newClient = [MSClient clientWithApplicationURLString:@"https://mobileserviceurl.az..." withApplicationKey:@"applicationkey"];

or

MSClient *newClient = [MSClient clientWithApplicationURLString:@"https://accountname.blob.co..." withApplicationKey:@"primaryapplicationkey"];

and how to use the containersTable and blobsTable in [getTable:@""]; because if I connect with mobile service I get the following error,

2013-08-02 19:00:12.916 StorageDemo[2356:c07] ERROR Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (Invalid value around character 3.) UserInfo=0x888c630 {NSDebugDescription=Invalid value around character 3., com.Microsoft.WindowsAzureMobileServices.ErrorResponseKey=<nshttpurlresponse: 0x888e590="">, com.Microsoft.WindowsAzureMobileServices.ErrorRequestKey=<nsmutableurlrequest http:="" accountname.blob.core.windo...="" tables="" blobcontainertest="">}

I looked for references on errors and how to connect to blob storage but do not mention anything about this

I hope you can help me and your help is appreciated

yingja!

Hi Chris, thanks for this awesome post!

I have been using getSasUrlForNewBlob to post picture blobs to my azure containers, however, sometimes I was returned NULL, I am wondering besides following your instructions (client&server), what else am I maybe missing?

yingja!

Yes, here's the log:

An unhandled exception occurred. Error: One of your scripts caused the service to become unresponsive and the service was restarted. This is commonly caused by a script executing an infinite loop or a long, blocking operation. The service was restarted after the script continuously executed for longer than 1000 milliseconds.
at EventEmitter.<anonymous> (C:\DWASFiles\Sites\FilmDiary\VirtualDirectory0\site\wwwroot\runtime\server.js:84:17)
at EventEmitter.emit (events.js:88:20)

Chris

Well it sounds like one of your scripts is messed up. I would suggest a poor man's debugging and putting in console statements in whatever script is being called so you can see which line in the script is causing the problem.

yingja!

I was using exactly the same script you provided in another post (http://chrisrisner.com/Mobi..., I don't think the script is the issue, and the NULL situation does not happen every time (around 30%, which is a lot higher than I would expect). Maybe it's because I set the mobile service to run in asia, and testing from U.S., and that could cause some network timeout?

Chris

One of your scripts is causing the error you posted above. You can figure out exactly which line by putting in more console.log statements into the script. This should help us narrow down specifically which part of the script is causing it to happen. If your Mobile Service and your storage account are both in Asia, network issues shouldn't be causing the script to run long as the error is indicating.

Chris

It looks like there is a production problem currently when loading some scripts that cause the above mentioned error. A fix will be out soon so it should go away.

yingja!

Hi Chris, this sasUrl creation problem seems solved, appreciate that. One thing very weird is that, I found from my telemetry many Chinese users cannot post images to sasUrl (blobs are not created), while U.S. users always succeed. Do you know what might be going wrong?

Chris

Any insight into what's going wrong on the client side? Are they getting a SAS and then unable to reach the storage endpoint due to a timeout or 404 or something else?

Chris

You can call getBlobProperties (using the Azure module) and pass in the container and blob name and see if you get anything back.

Hernando

Hi Great article , but is now out date since the Windows Azure SDK for iOS has been updated.
Replace all "getTable"
self.tablesTable = [_client getTable:@"Tables"]; with
self.table = [_client tableWithName :@"Table"]:

And also the Storyboard Layout needs updated.

Christ in this article from windows azure resource "

Upload images to Windows Azure Storage by using Mobile Services" ,
http://www.windowsazure.com...

they only inserted one script in the main table , can we do this for iOS as well ?

Thanks for your help.

Chris

Hi Hernado, this sample is using the iOS SDK though getTable will still work (it's been deprecated but still works fine). If i have a chance, I'll update the code in GitHub but it should be pretty trivial for you or anyone that pulls it down to do it for now as well.

Alex Cannon

Related to this, the example code does not work for me with the latest iOS SDK (v 1.2.3). I swapped out the older framework in your example project with v1.2.3 and only renamed all `getTable` and `ApplicationWithURL:withApplicationKey` calls. Once the app builds, clicking "Blob Storage" crashes the app with the following output:

https://gist.github.com/aca...

I'm having similar problems using the StorageService class in my own app. It works fine with the older SDK but crashes with v1.2.3.

Alex Cannon

Related to this, I can't get the example project to work with the latest version of the SDK (v1.2.3). I swap out the older SDK its currently using for v1.2.3 and only rename a few function calls as need (e.g. getTable to tableWithName, ApplicationURLWithString:WithApplicationKey: to ApplicationURLWithString:applicationKey:, etc.). After the app builds, clicking "Blob Storage" crashes with the following output:

https://gist.github.com/aca...

My personal app has similar issues. It works fine with the same older version of the SDK that's used in the example project, but one that's swapped out for v1.2.3 it crashes as well:

https://gist.github.com/aca...

Chris

Just tested it out, it looks like the MSFilter method signature changed. It should now be this:

- (void) handleRequest:(NSURLRequest *)request next:(MSFilterNextBlock)onNext response:(MSFilterResponseBlock)onResponse {

Replace your existing filter method sig with that and things should work for you again. You can use the same code body from the existing filter method.

Rana Nile

thanks for sharing

شركة تخزين عفش بالرياض

شركة ترميمات بالرياض

شركة دهانات بالرياض

شركات تنظيف المنازل بجدة

شركة كشف تسربات المياة بجدة

شركة مكافحة حشرات في جدة


تنظيف خزانات بجدة


نقل عفش جدة

شركات تنظيف منازل بجدة


عزل بالرياض


عزل اسطح بالرياض


عزل خزانات بالرياض

عملية تصغير المعدة

عملية تكميم المعدة

عمليات تدبيس المعدة

شركات مكافحة القوارض بالرياض


مكافحة حشرات بالرياض


رش مبيدات بالرياض

شركة عزل خزانات بالرياض

شركة تخزين عفش بالرياض

شركة تنظيف بيارات بالرياض


تنظيف مجالس بالمدينة المنورة


تنظيف منازل بالمدينة المنورة


تنظيف فلل بالمدينة المنورة


تنظيف مسابح بجدة


تنظيف موكيت بجدة


تنظيف شقق بجدة

شركة مكافحة حشرات بالدمام

شركات تنظيف المنازل بالدمام

شركة تسليك مجاري بالدمام

تنظيف بالرياض

كشف تسربات المياه بالرياض


تنظيف فلل بالرياض

اثاث مستعمل للبيع بالرياض

شركة كشف تسربات المياة بجدة

بيع اثاث مستعمل بالرياض

شركة رش مبيدات بالأحساء


افضل شركات بالسعودية

افضل شركات بالسعودية

افضل شركات بالسعودية

مكافحة حشرات بالمدينة المنورة

شركة تنظيف شقق بالمدينة المنورة

شركة تسليك مجاري بشقراء

شركة كشف تسربات المياه بضرماء

شركة تنظيف بيارات بالدلم

شركة تنظيف بضرما

George Lovitz

Hi Chris,

First, awesome tutorial and info. Helped me greatly but I am running into a problem. I can run your same project fine and have been able to create containers and input blobs no problem. Then, I attempted to add it to my project (which I am running the latest Azure framework) and I always get a warning in the implementation stating that the following method/protocal is not declared...although it is in the .h and in the actual .m:

(void) handleRequest:(NSURLRequest *)request
onNext:(MSFilterNextBlock)onNext
onResponse:(MSFilterResponseBlock)onResponse

Always warns not declared but am able to compile and run, but when it attempts to create the container, errors with same as above. Any thoughts on this? I did think about just deleting the latest azure framework and using the one you complied with but thought I would try you first. Once again, thanks. You have been a great source of help throughout my xcode experience!

Geo...

Chris

Sorry for missing this George. If you, or anyone else is looking, this is because the latest version of the SDK for Mobile Services for iOS has changed the handelRequest method. I would suggest pulling down the iOS Quick Start for Mobile Services and comparing how that implements the method and compare the two.

young

Thanks for the good example! Just some notes on running the demo app on the current Xcode for beginners like me:
1. Set the simulator to iPhone4S or iPhone5 (the code doesn't build with 5S or later)
2. Add self.navigationController.navigationBar.transparent = NO; in viewDidLoad() to prevent navigationBar blocks input forms

Cepstrum

Hi. I'm trying to upload to the blob storage but I can't figure out what the "BlobBlobs" table should be. Is it just a random table or should it somehow be associated with the SAS? If I use a random table, the completion block returns a dictionary with key "id" and value with id of the created row and not a SAS URL.

Cepstrum

Sorry, I didn't see the other post about adding the BlobBlobs table and script. It all makes sense now! Thanks for the only iOS example in the public world!

Saerom HA

Hi Chris. Thank you for this post, it's so helpful.
I'd like to check out the full source code as well you linked in this article, but I've got 404 error when I click it.
Could you figure out what's wrong with it?
Thanks.

Leave a Comment