To write this article I've borrowed Wade Wegner's article:
Getting Started with the Windows Azure Toolkit for iOS which explains how to create an Azure - iOS project with Windows Azure Toolkit for iOS 1.0 and updated it because class names and method names has changed and could cause some confusion for those developers who wants to use this toolkit.
So here we go!
Unpacking the v1.0.0 library zip file
You can download the compiled storage library on github (found under downloads in https://github.com/microsoft-dpe/watoolkitios-lib). When you upzip the zip file, you’ll find several folders:
• /4.3-device – the library binary for iOS 4.3 (device)
• /4.3-simulator – the library binary for iOS 4.3 (simulator)
• /include – the headers for the library
Creating your first project using the toolkit
If you are not familiar with XCode, this is a short tutorial for getting your first project up and running. Launch XCode 4 and create a new project:
Select a View-based application and click Next.
Give the project a name and company. For the purposes of this walkthrough, we’ll call it “FirstAzureProject”. Do not include Unit Tests.
When the project opens, right click on the Frameworks folder and select “Add Files to…”
Locate the libwatoolkitios.a library file from the download package folder (from either the simulator or device folder), and add it to the Frameworks folder.
Now, click on the top most project (FirstAzureProject) in the left hand column. Click on the target in the second column. Click on the “Build Settings” header in the third column. Ensure that the “All” button is selected to show all settings.
In the search box, type in “header search” and look for an entry called “Header Search Paths”:
Double-click on this line (towards the right of the line), and click on the “+” button in the lower left.
Add the path to where the folder containing the header files are located (this is the include folder from the download). For example, "~/Desktop/v1.0.0/include" if you have extracted the folder on your desktop. Be sure to encapsulate in quotes if you have spaces in the path.
Now, click on the “Build Phases” tab and expand the “Link Binary with Libraries” section:
Click on the “+” button in the lower left, and scroll down until you find a library called “libxml2.2.7.3.dylib”. Add this library to your project. Don’t forget to add the header search path to /usr/include/libxml2
Testing Everything Works
Now that you’ve added all of the required references, let’s test that the library can be called. To do this, double click on the [ProjectName]AppDelegate.m file (e.g. FirstAzureProjectAppDelegate.m), and add the following imports to the class:
#import "WAAuthenticationCredential.h"
#import "WACloudStorageClient.h"
Perform a build. If the build succeeds, the library is correctly added to the project. If it fails, it is recommended to go back and check the header search paths.
Assuming it builds, in the .m file, add the following declarations after the @synthesize lines:
WAAuthenticationCredential *credential;
WACloudStorageClient *client;
Now, add the following lines after the [self.window makeKeyAndVisible] line in the didFinishLaunchingWithOptions method:
credential = [WAAuthenticationCredential credentialWithAzureServiceAccount:@"ACCOUNT_NAME" accessKey:@"ACCOUNT_KEY"];
client = [WACloudStorageClient storageClientWithCredential:credential];
[client fetchBlobContainersWithCompletionHandler:^(NSArray* containers, NSError* error)
{
if (error)
{
NSLog(@"%@",[error localizedDescription]);
}
else
{
NSLog(@"%i containers were found…",[containers count]);
}
}];
Maybe you could be more used to work with delegates, so, in this case you have to implement in your class <WACloudStorageClientDelegate> and sets your client’s delegate property to self and invoke fetchBlobContainers.
storage.delegate = self;
[client fetchBlobContainers];
Once you’ve done this you have to implement two methods, first one is to handle is an error occurs or the request fails, and this method is common for any action accomplished with our WACloudStorageClient object and second one is called when the WABlobContainer list is retrieved.
- (void)storageClient:(WACloudStorageClient *)client didFailRequest:(NSURLRequest *)request withError:(NSError *)error
{
NSLog(@"%@", [error description]);
}
- (void)storageClient:(WACloudStorageClient *)client didFetchBlobContainers:(NSArray *)containers
{
NSLog(@"%i Blob containers found", [containers count]);
}
Be sure to replace ACCOUNT_NAME and ACCOUNT_KEY with your Windows Azure storage account name and key, available on the Windows Azure portal
Build and run the project. You should something similar to the following output in the debug window:
2011-05-06 18:18:46.001 FirstAzureProject[27456:207] 2 containers were found…
The last line shows that this account has 2 containers. This will of course vary, depending on how many blob containers you have setup in your own Windows Azure account.
Doing more with the toolkit
Feel free to explore the class documentation to explore more of the toolkit API. To help, here are some additional examples:
In [ProjectName]AppDelegate.m class, add the following headers:
#import "WAAuthenticationCredential.h"
#import "WACloudStorageClient.h"
#import "WABlobContainer.h"
#import "WABlob.h"
#import "WAAableEntity.h"
#import "WATableFetchRequest.h"
In the didFinishLaunchingWithOptions method, after the [self.window makeKeyAndVisible] line, try testing a few of the following commands. Again, running the project will return results into the debugger window.
To authenticate using account name and key:
credential = [WAAuthenticationCredential credentialWithAzureServiceAccount:@"ACCOUNT_NAME" accessKey:@"ACCOUNT_KEY"];
To authenticate instead using the proxy service from the Windows Phone 7 toolkit, you can use the following:
credential = [WAuthenticationCredential authenticateCredentialWithProxyURL:[NSURL URLWithString:@"PROXY_URL"] user:@"USERNAME" password:@"PASSWORD" password:withCompletionHandler:^(NSError *error)
{
if (error)
{
NSLog(@"%@",[error localizedDescription]);
}
else
{
NSLog(@"Successfully logged in");
}
}];
Replace the PROXY_URL, USERNAME, and PASSWORD with the information required to access your proxy service.
Once again, if you prefer using delegates instead of handlers you must implement WAAuthenticationDelegate and chage the method invoked.
[WAAuthenticationCredential authenticateCredentialWithProxyURL:[NSURL URLWithString:proxyURL] user:usernameField.text password:passwordField.text delegate:self];
After that we have to implement both methods -loginDidSucceed which will be invoked if the login is succeed and loginDidFailWithError: if the login isn’t.
- (void)loginDidSucceed
{
Azure_Storage_ClientAppDelegate *appDelegate = (Azure_Storage_ClientAppDelegate *)[[UIApplication sharedApplication] delegate];
StorageTypeSelector *newController = [[StorageTypeSelector alloc] initWithNibName:@"StorageTypeSelector" bundle:nil];
newController.navigationItem.title = @"Storage Type";
[appDelegate.navigationController pushViewController:newController animated:YES];
[newController release];
}
- (void)loginDidFailWithError:(NSError *)error
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Login Error" message:[NSString stringWithFormat:@"An error occurred: %@", [error localizedDescription]] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];
proxyURLField.text = @"";
usernameField.text = @"";
passwordField.text = @"";
}
To create a new client using the credentials:
client = [WACloudStorageClient storageClientWithCredential:credential];
To list all blob containers (this method is not supported via the proxy server):
// get all blob containers
[client fetchBlobContainersWithCompletionHandler::^(NSArray *containers, NSError *error)
{
if (error)
{
NSLog(@"%@",[error localizedDescription]);
}
else
{
NSLog(@"%i containers were found…",[containers count]);
}
}];
To get all blobs within a container (this also is not supported by the proxy):
// get all blobs within a container
[client fetchBlobs:@"images" withBlock:^(NSArray *blobs, NSError *error)
{
if (error)
{
NSLog(@"%@",[error localizedDescription]);
}
else
{
NSLog(@"%i blobs were found in the images container…",[blobs count]);
}
}];
To get all tables from storage (this works with both direct access and proxy):
// get all tables
[client fetchTablesWithCompletionHandler:^(NSArray* tables, NSError* error)
{
if (error)
{
NSLog(@"%@",[error localizedDescription]);
}
else
{
NSLog(@"%i tables found",[tables count]);
}
}];
To create a table (works with both direct access and proxy):
// create table
[client createTableNamed:@"wadestable" withBlock:^(NSError *error)
{
if (error)
{
NSLog(@"%@",[error localizedDescription]);
}
else
{
NSLog(@"Table created");
}
}];
To delete a table (works with both direct access and proxy):
//delete a table
[client deleteTableNamed:@"wadestable" withBlock:^(NSError *error)
{
if (error)
{
NSLog(@"%@",[error localizedDescription]);
}
else
{
NSLog(@"Table was deleted");
}
}];
To get entities for a table (works with both account key and proxy):
// get entities for table developers
WATableFetchRequest* fetchRequest = [WATableFetchRequest fetchRequestForTable:@"Developers"];
[client fetchEntities:fetchRequest withCompletionHandler:^(NSArray *entities, NSError *error)
{
if (error)
{
NSLog(@"%@",[error localizedDescription]);
}
else
{
NSLog(@"%i entities found in the developer table",[entities count]);
}
}];
To get entities for a table using predicate (works with both account key and proxy):
// get entities for table developers with predicate request
NSError* error = nil;
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"Name = 'Wade' || Name = 'Vittorio' || Name = 'Nathan'"];
WATableFetchRequest* anotherFetchRequest = [WATableFetchRequest fetchRequestForTable:@"Developers" predicate:predicate error:&error];
[client fetchEntities:anotherFetchRequest withCompletionHandler:^(NSArray *entities, NSError *error)
{
if (error)
{
NSLog(@"%@",[error localizedDescription]);
}
else
{
NSLog(@"%i entities returned by this request",[entities count]);
}
}];
All of these actions could be accomplished using WACloudStorageDelegate methods instead of completion handlers. If you are interested in just have a look at the documentation to find which methods you should implement for which actions.
Doing even more with the toolkit
If you are looking to explore the toolkit further, I recommend looking at the sample application that can be found in the watoolkitios-samples project. This project demonstrates all of the functionality of the toolkit, including creating, uploading, and retrieving entities from both table and blob storage.