Upload
aaron-douglas
View
1.577
Download
8
Tags:
Embed Size (px)
DESCRIPTION
Most applications writing for mobile platforms need some sort of integration with web services. Unfortunately, most times backend infrastructure becomes an oversight and the app ends up suffering in the long run. Using cloud infrastructure technologies like Parse can help you get from concept to App Store insertion in no time. Parse's simple API, extendable Cloud Code feature and Push notification API let you focus on your app while getting a scalable backend solution.
Citation preview
Scalable Cloud Apps That Won't Keep You Up At
Night
Aaron Douglas @astralbodies Twitter/ADN
Red Arrow Labs - Milwaukee, WI
Friday, March 8, 13
So you have an app (or an idea)
Friday, March 8, 13
You want people to download and use it.
Friday, March 8, 13
Most apps need to integrate with
something.
Friday, March 8, 13
Because integration (collaboration) is
interesting
Friday, March 8, 13
Interesting apps = MOAR MONEY
Friday, March 8, 13
Why should I use the “Cloud”?
Friday, March 8, 13
Scalability & Infrastructure
Friday, March 8, 13
Cross Platform
Friday, March 8, 13
Our App Idea
Friday, March 8, 13
• Conference App
• Speakers
• Sessions
• Map
• Comments & Notes
• Photos
Friday, March 8, 13
• Still a hot mess with Core Data
• iOS and Mac only
• Hard to debug
Why not iCloud?
Friday, March 8, 13
Options
Friday, March 8, 13
We’re Going to Use Parse
Friday, March 8, 13
What Parse Provides
• Data storage - RESTful API & Native SDKs
• Queries
• ACLs / Users
• Cloud Code & Push Notifications
• File Storage
• In-App Purchases
• GeoPoints & Spatial Queries
Friday, March 8, 13
Friday, March 8, 13
$ vi Podfilepod 'Parse'
$ pod install
Add To Your Project
Friday, March 8, 13
Friday, March 8, 13
Friday, March 8, 13
AppDelegate.m
#import <Parse/Parse.h>
...
[Parse setApplicationId:@"3lZSGBNTs03wQigx2apDHwUv6ZczMVaIib8nUz" clientKey:@"rstZrElVxBiB5tzsWn10QK9AfrRR0GkQQj16Hj"];
Friday, March 8, 13
Objects
• Session
• Schedule
• Speaker
• Attendee Notes
• Attendee Schedule
• ... and on and on
Friday, March 8, 13
PFObject
• Key-Value pairs
• Schema-less
• JSON data types: NSString, NSNumber, NSDate, NSData, NSArray, NSDictionary
• Associations - 1:1, 1:N, N:N
• objectId, updatedAt, createdAt
Friday, March 8, 13
PFObject
PFObject *speaker = [PFObject objectWithClassName:@"Speaker"]; [speaker setObject:@"Marty" forKey:@"firstName"]; [speaker setObject:@"McFly" forKey:@"lastName"]; [speaker setObject:@"Hill Valley, CA" forKey:@"location"]; NSLog(@"Object ID before save: %@", speaker.objectId); [speaker save]; // MAGIC HAPPENS HERE NSLog(@"Object ID after save: %@", speaker.objectId);
2013-03-03 16:19:08.923 CocoaConfParse[4803:c07] Object ID before save: (null)2013-03-03 16:19:09.468 CocoaConfParse[4803:c07] Object ID after save: 6iXsTrvWd0
Friday, March 8, 13
Back on Parse.com...
Friday, March 8, 13
Refresh
[speaker refresh];
[speaker refresh:&error];
[speaker refreshInBackgroundWithBlock:^(PFObject *object, NSError *error) { // Code}];
Friday, March 8, 13
Removing Data
[speaker removeObjectForKey:@"favoriteStarWarsCharacter"]; [speaker deleteInBackground];
Friday, March 8, 13
Relationships
PFObject *session = [PFObject objectWithoutDataWithClassName:@"Session" objectId:@"6iXsTrvWd0"];
[sessionComment setObject:session forKey:@"session"];
PFObject *session = [sessionComment objectForKey:@"session"];
[session fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) { NSString *title = [post objectForKey:@"title"];}];
Friday, March 8, 13
Query by ID
PFQuery *query = [PFQuery queryWithClassName:@"Speaker"]; PFObject *object = [query getObjectWithId:@"jFgtbb2aCb"]; NSLog(@"Name: %@ %@", object[@"firstName"], object[@"lastName"]);
2013-03-03 17:08:52.484 CocoaConfParse[6892:c07] Name: Marty McFly
Friday, March 8, 13
Querying PFQuery *query = [PFQuery queryWithClassName:@"Speaker"]; [query whereKey:@"lastName" equalTo:@"McFly"]; query.cachePolicy = kPFCachePolicyNetworkElseCache; PFObject *object = [query getFirstObject]; NSLog(@"Name: %@ %@", object[@"firstName"], object[@"lastName"]);
2013-03-03 17:08:52.484 CocoaConfParse[6892:c07] Name: Marty McFly
Friday, March 8, 13
Queries
• NSPredicate
• 1000 records max (100 default)
• Relational queries
• Cache policies
Friday, March 8, 13
Asynchronous
PFObject *speaker = ...;
[speaker save]; [speaker saveEventually]; [speaker saveInBackground];
[speaker saveInBackgroundWithBlock: ^(BOOL succeeded, NSError *error){}];
Friday, March 8, 13
Users[PFUser enableAutomaticUser]; PFUser *currentUser = [PFUser currentUser];[currentUser setObject:[NSDate date] forKey:@"lastLaunched"];[currentUser saveInBackground];
PFUser *user = [PFUser user];user.username = @"martymcfly";user.password = @"awesomesauce";[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
}]; [PFUser logInWithUsernameInBackground:@"martymcfly" password:@"awesomesauce" block:^(PFUser *user, NSError *error) {
}];
Friday, March 8, 13
ACLs
PFObject *feedback = [PFObject objectWithClassName:@"SessionFeedback"]; [feedback setObject:@"I enjoyed it." forKey:@"comment"];
feedback.ACL = [PFACL ACLWithUser:[PFUser currentUser]];
[feedback saveInBackground];
Friday, March 8, 13
Built-In Views
Friday, March 8, 13
App Structure
• Where does Parse start & end?
• Service Layers
• Parse everywhere
Friday, March 8, 13
Data Transfer Objects
• Map PFObject to some object in your code
• Parse is contract/schema-free
Friday, March 8, 13
Push Notifications
• Still requires APN setup
• Simplifies addressing & registration
• Cross platform (Android & iOS)
Friday, March 8, 13
- (void)application:(UIApplication *)applicationdidRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{ // Store the deviceToken in the current // Installation and save it to Parse. PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:deviceToken]; [currentInstallation saveInBackground];}
Push Registration
Friday, March 8, 13
Channels
PFInstallation *currentInstallation = [PFInstallation currentInstallation]; [currentInstallation addUniqueObject:@"Speakers" forKey:@"channels"]; [currentInstallation saveInBackground];
PFPush *push = [[PFPush alloc] init]; [push setChannel:@"Speakers"]; [push setMessage:@"Thanks for all the fish!"]; [push sendPushInBackground];
Friday, March 8, 13
In-App Purchases
• Wraps StoreKit
• Receipt verification
• Asset download
• PFProductTableViewController
Friday, March 8, 13
What about server-side calls?
Friday, March 8, 13
• Create new speakers & sessions
• Attendees register preferences
• Push notifications when session changes
• Welcome emails
Friday, March 8, 13
Cloud Code lets you deploy server-side code
Friday, March 8, 13
Cloud Code
• RESTful API
• JavaScript Language
• Triggers & Validation
• Modules
Friday, March 8, 13
$ curl -s https://www.parse.com/downloads/cloud_code/installer.sh | sudo /bin/bash
$ parse new CocoaConfCloudCodeEmail: [email protected]:1:CocoaConfSelect an App:1
$ cd CocoaConfCloudCode
Installing the CL Tool
Friday, March 8, 13
Parse.Cloud.define("numberOfSpeakers", function(request, response) { var query = new Parse.Query("Speaker"); query.find({ success: function(results) { response.success(results.count); }, error: function() { response.error("Speaker lookup failed"); } });});
$ vi cloud/sessions.js
Friday, March 8, 13
$ parse deploy$ curl -X POST \ -H "X-Parse-Application-Id: MN28ox..." \ -H "X-Parse-REST-API-Key: RItKo..." \ -H "Content-Type: application/json" \ -d '{}' \ https://api.parse.com/1/functions/numberOfSpeakers
{ "result": 1}
Friday, March 8, 13
Friday, March 8, 13
Modules
Friday, March 8, 13
Cloud Code
• Still in its infancy
• No scheduled events
• Take a look at iron.io
Friday, March 8, 13
Cost
• Free (1 million API requests / Pushes / 1GB)
• $199/month (15mil / 5mil / 10GB)
• Enterprise >$199
Friday, March 8, 13
Portability
• Import
• Export
• Service layer to abstract
• Never really want to lock into a technology
Friday, March 8, 13
Code
Friday, March 8, 13
Questions?
Friday, March 8, 13