Windows Azure Mobile Services

Episode #59 | 24 minutes | published on 03/28/2013
Free Video
This week we take a look at Windows Azure Mobile Services, a back-end for mobile applications that has first class iOS support. In this episode we begin building a full featured chat application. This is part 1 of 2, in which we set up a new mobile service, wire up the SDK with CocoaPods, set up Twitter authentication and enrich the data using Javascript on the server. This episode has been sponsored by Microsoft.

Episode Links

Setting up the client

You'll need your API key to set this up (get it from the portal quickstart page). In the episode I keep this maintained as a property on my root view controller and pass it around as needed.

MSClient *client = [MSClient clientWithApplicationURLString:@"https://YOURAPP.azure-mobile.net/"
                                        withApplicationKey:@"YOURKEY"];

Authenticating the user

In the SignInViewController we authenticate the user using Twitter.

    [self.client loginWithProvider:@"twitter"
                      onController:self
                          animated:YES
                        completion:^(MSUser *user, NSError *error) {
                            if (error) {
                                // handle error
                            } else {
                                self.client.currentUser = user;

                                // insert the user
                                [self setUser:user.userId];
                            }
                        }];

On success, we need to make a call to insert the user record in our system. We do this for 2 reasons:

1) to hold their Twitter username, which we'll have to fetch using server-side Javascript 2) to hold their device token, for push notifications

NSCUser *user = [[NSCUser alloc] init];
user.deviceToken = [[NSUserDefaults standardUserDefaults] objectForKey:@"deviceToken"];
    
MSTable *usersTable = [self.client getTable:@"Users"];
[usersTable insert:[user attributes]
        completion:^(NSDictionary *item, NSError *error) {
            if (item) {
                user.userId = [item[@"id"] intValue];
                [[NSUserDefaults standardUserDefaults] setInteger:user.userId forKey:@"userId"];
                [[NSUserDefaults standardUserDefaults] synchronize];
                [self dismiss];
            } else {
                // handle error
            }
        }];

Server side behavior for new user records

We'd like the server to fetch the username for each twitter user. In addition, we need to make sure they never get 2 records for the same twitter id, so we'll implement an insert or update here.

function getUserByTwitterId(twitterId, callback) {
    tables.getTable("Users").where({userId: twitterId}).take(1).read({
        success: function(results) {
            if (results.length > 0) {
                callback(results[0]);
            } else {
                callback(null);
            }
        },
        error: function(error) {
            console.error("ERROR", error);
            callback(null, error);
        }
    });
}

function updateUser(user, item, options) {
    mssql.query("UPDATE Users SET deviceToken = ? WHERE id = ?", [item.deviceToken, user.id], options);
}

function insert(item, user, request) {
    getUserByTwitterId(user.userId, function(u) {
        if (u) {
            console.log("Found existing user.");
            updateUser(u, item, {
                success: function() {
                    request.respond(statusCodes.OK, u);
                }
            });
        } else {
            console.log("No existing user found, creating a new one");
            fetchTwitterUsername(user.userId, function(username, error) {
                if (error) {
                    console.error("Error fetching twitter username: ", error);
                    request.respond(statusCodes.ERROR, "Error fetching twitter username");
                } else {
                    console.log("Found twitter username: " + username);
                    item.username = username;
                    item.userId = user.userId;
                    console.log("Creating user:", item);
                    request.execute();
                }
            });
           
        }
    });
}

Once authenticated, we can now use the rest of the application.

Creating rooms

To create a room, we simply need to insert a "room dictionary" into the Rooms table.

   NSCRoom *room = [[NSCRoom alloc] init];
    room.name = name;
    
    MSTable *roomsTable = [self.client getTable:@"Rooms"];
    [roomsTable insert:[room attributes]
            completion:^(NSDictionary *item, NSError *error) {
                NSCRoom *room = [[NSCRoom alloc] initWithDictionary:item];
                [self insertRoomRow:room];
            }];

Inserting the row simply animates it into the tableview. We'd like to know when a room is created, as well as who created it. This is a great spot for server side code. Open up the portal and go to the Room Table Insert script.

function insert(item, user, request) {
    item.createdAt = new Date();
    item.createdBy = user.userId;
       
    request.execute();


}

Reading the list of rooms, along with a participant count

Reading the rooms is a simple query with no predicate, however we want to add an additional attribute to the response indicating how many users are in that particular room. We can do this by joining onto the Users table and counting the number of users in each room.

function read(query, user, request) {
    mssql.query("select r.id, r.name, count(u.id) as participantCount from rooms r left join users u on u.roomId = r.id group by r.id, r.name", {
        success: function(results) {
            console.log("RESULTS", results);
            request.respond(statusCodes.OK, results);
        }
    });
}
blog comments powered by Disqus
Back