More Related Content


iOS App with as RESTful Backend

  1. Stefano Zanetti § DevCamp iOS App with as RESTful Backend
  2. DevCamp Stefano Zanetti  Apple iOS Developer Superpartes Innovation Campus & H-Farm  Co-founder di # Pragma Mark ―  [tt] @Doh__ [in] Stefano Zanetti [fb] stefano.znt [email]
  3. DevCamp What is The perfect cloud for your apps. Parse allows your team to focus on creating a great user experience and forget server maintenance and complex infrastructure.
  4. DevCamp What does this mean? • Backend for apps and websites • Database NoSQL (schemaless: if you need to store something, store key/value data without prepare any table) • Store your app’s data in the cloud • Parse automatically creates RESTful API for you • Push notification • Social • Hosting • Cloud code
  5. DevCamp Who can you it?
  6. DevCamp How much? BasicBasicBasic ProProPro EnterpriseEnterpriseEnterprise Great for developer to get startedGreat for developer to get startedGreat for developer to get started For Production applicationsFor Production applicationsFor Production applications For advanced featuresFor advanced featuresFor advanced features FREEFREEFREE $199/month$199/month$199/month contact Parsecontact Parsecontact Parse Request Pushes Burst limit Request Pushes Burst limit Request Pushes Burst limit 1 milion/ month 1 milion/ month 20/second 15 milion/ month 5 milion/ month 40/second contact Parse
  7. DevCamp Features
  8. DevCamp More features
  9. DevCamp App Settings
  10. DevCamp Where?
  11. DevCamp Dashboard
  12. DevCamp General
  13. DevCamp Application Keys
  14. DevCamp Push Notifications
  15. DevCamp Web Hosting
  16. DevCamp Administrative Tools
  17. DevCamp Data Browser
  18. DevCamp Analytics
  19. DevCamp Push Notifications Status
  20. DevCamp Send push
  21. DevCamp Demo
  22. DevCamp Get started
  23. DevCamp SDK • Quick start: • • Download from official site: • • CocoaPods: • pod 'Parse', '~> 1.2.9'
  24. DevCamp Quick start
  25. DevCamp Quick start [Parse setApplicationId:@"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" clientKey:@"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]; 1.Download the Sample Project 2.Xcode 4.6+ and iOS targetting 4.3+ 3.Open project and uncomment the first line of application:didFinishLaunchingWithOptions: in the application delegate file: 4.Compile and run
  26. DevCamp Create & Save an Object PFObject *testObject = [PFObject objectWithClassName:@"TestObject"]; [testObject setObject:@"bar" forKey:@"foo"]; [testObject save]; 5.Copy and paste next code somewhere in the project 6.Compile and run
  27. DevCamp Yeah!! You saved your first object
  28. DevCamp Which libraries Parse needs? • AudioToolbox.framework • CFNetwork.framework • CoreGraphics.framework • CoreLocation.framework • libz.1.1.3.dylib • MobileCoreServices.framework • QuartzCore.framework • Security.framework • StoreKit.framework • SystemConfiguration.framework • AdSupport.framework (optional if iOS targetting is than then 6.0) • Social.framework (optional if iOS targetting is less than 6.0) • Accounts.framework (optional if iOS targetting is less than 6.0) If you're targeting iOS versions less than 5.0, you'll need to add the "-fobjc- arc" flag to the "Other Linker Flags" entry in your target build settings.
  29. DevCamp PFObject
  30. DevCamp Saving/Updating Objects PFObject *post = [PFObject objectWithClassName:@"Post"]; [post setObject:@"New post" forKey:@"title"]; [post setObject:@"This is my first message" forKey:@"message"]; [post setObject:[NSNumber numberWithBool:NO] forKey:@"visible"]; [post save]; • PFObject contains key-value pairs of JSON- compatible data. • This data is schemaless • Interface is similar to NSMutableDictionary
  31. DevCamp Check Data Browser • You don't have to configure or set up a new Class • You don't need to specify a key for the object you are saving Parse automatically fills 3 field: • objectId • createdAt • updatedAt objectId: "xWMyZ4YEGZ", title: "New post", message: "This is my first message", visible: false, createdAt:"2011-06-10T18:33:42Z", updatedAt:"2011-06-10T18:33:42Z"
  32. DevCamp Retriving Data Using PFQuery you can retrive a PFObject PFQuery *query = [PFQuery queryWithClassName:@"Post"]; PFObject *post = [query getObjectWithId:@"xWMyZ4YEGZ"]; NSString *postTitle = [post objectForKey:@"title"]; BOOL visible = [[post objectForKey:@"visible"] boolValue];
  33. DevCamp Note!! The three special values are provided as properties: NSString *objectId = post.objectId; NSDate *updatedAt = post.updatedAt; NSDate *createdAt = post.createdAt; If you need to refresh an object you already have with the latest data that is in the Parse Cloud, you can call the refresh method like so: [myObject refresh];
  34. DevCamp Saving in background Just call saveInBackground method [post saveInBackground];
  35. DevCamp Blocks If you want to run code when operation is completed you can use blocks (iOS 4.0+) or callbacks methods. [post saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {   if (!error) {     // The Post saved successfully.   } else {     // There was an error saving the Post.   } }];
  36. DevCamp Callbacks // First set up a callback. - (void)saveCallback:(NSNumber *)result error:(NSError *)error {   if (!error) {     // The Post saved successfully.   } else {     // There was an error saving the Post.   } }   // Then, elsewhere in your code... [post saveInBackgroundWithTarget:self selector:@selector(saveCallback:error:)];
  37. DevCamp Load in background PFQuery *query = [PFQuery queryWithClassName:@"Post"]; [query getObjectInBackgroundWithId:@"xWMyZ4YEGZ"                              block:^(PFObject *post, NSError *error) {   if (!error) {     // The get request succeeded. Log the score     NSLog(@"The title is: %d", [[post objectForKey:@"title"] intValue]);   } else {     // Log details of our failure     NSLog(@"Error: %@ %@", error, [error userInfo]);   } }]; With Blocks
  38. DevCamp Load in background // First set up a callback. - (void)getCallback:(PFObject *)post error:(NSError *)error {   if (!error) {     // The get request succeeded. Log the score     NSLog(@"The title is: %d", [[post objectForKey:@"title"] intValue]);   } else {     // Log details of our failure     NSLog(@"Error: %@ %@", error, [error userInfo]);   } }   // Then, elsewhere in your code... PFQuery *query = [PFQuery queryWithClassName:@"Post"]; [query getObjectInBackgroundWithId:@"xWMyZ4YEGZ"                             target:self                           selector:@selector(getCallback:error:)]; With CallBacks
  39. DevCamp Saving Objects Offline Just call saveEventually method and system store the update on the device until a network connection is available [post saveEventually];
  40. DevCamp Saving Counter Objects The “likes” field is a counter: [post incrementKey:@"likes"]; [post saveInBackground]; or [post incrementKey:@"likes" byAmount:3]; [post saveInBackground];
  41. DevCamp Saving Array Objects • addObject:forKey: and addObjectsFromArray:forKey: append the given objects to the end of an array field. • addUniqueObject:forKey: and addUniqueObjectsFromArray:forKey: add only the given objects which aren't already contained in an array field to that field.The position of the insert is not guaranteed. • removeObject:forKey: and removeObjectsInArray:forKey: remove all instances of each given object from an array field. [post addUniqueObjectsFromArray:[NSArray arrayWithObjects:@"stefano", @"massimo", nil] forKey:@"users"]; [post saveInBackground];
  42. DevCamp Delete Objects To delete an object from the cloud: [myObject deleteInBackground]; To delete e single field from an Object: // After this, the visilble field will be empty [myObject removeObjectForKey:@"visible"];   // Saves the field deletion to the Parse Cloud [myObject saveInBackground];
  43. DevCamp Demo
  44. DevCamp Relational Data
  45. DevCamp One-To-Many Relationship // Create the post PFObject *myPost = [PFObject objectWithClassName:@"Post"]; [myPost setObject:@"I'm Hungry" forKey:@"title"]; [myPost setObject:@"Where should we go for lunch?" forKey:@"content"];   // Create the comment PFObject *myComment = [PFObject objectWithClassName:@"Comment"]; [myComment setObject:@"Let's do Sushirrito." forKey:@"content"];   // Add a relation between the Post and Comment [myComment setObject:myPost forKey:@"parent"];   // This will save both myPost and myComment [myComment saveInBackground]; You can link objects:
  46. DevCamp One-To-Many Relationship You can also link objects using just their objectIds like so: // Add a relation between the Post with objectId "1zEcyElZ80" and the comment [myComment setObject:[PFObject objectWithoutDataWithClassName:@"Post" objectId:@"1zEcyElZ80"]               forKey:@"parent"];
  47. DevCamp One-To-Many Relationship By default, when fetching an object, related PFObjects are not fetched: PFObject *post = [fetchedComment objectForKey:@"parent"]; [post fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {   NSString *title = [post objectForKey:@"title"]; }];
  48. DevCamp Many-To-Many Relationship A User may have many Posts that they might like. PFUser *user = [PFUser currentUser]; PFRelation *relation = [user relationforKey:@"likes"]; [relation addObject:post]; [user saveInBackground]; Add relation: [relation removeObject:post]; Remove relation:
  49. DevCamp Many-To-Many Relationship By default, the list of objects in this relation are not downloaded [[relation query] findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {   if (error) {      // There was an error   } else {     // objects has all the Posts the current user liked.   } }];
  50. DevCamp Many-To-Many Relationship If you want only a subset of the Posts you can add extra constraints to the PFQuery returned by query PFQuery *query = [relation query]; // Add other query constraints.
  51. DevCamp Data types
  52. DevCamp What types are supported? NSNumber *number = [NSNumber numberWithInt:42]; NSString *string = [NSString stringWithFormat:@"the number is %i", number]; NSDate *date = [NSDate date]; NSData *data = [@"foo" dataUsingEncoding:NSUTF8StringEncoding]; NSArray *array = [NSArray arrayWithObjects:string, number, nil]; NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:number, @"number",                                                                       string, @"string",                                                                       nil]; NSNull *null = [NSNull null];   PFObject *bigObject = [PFObject objectWithClassName:@"BigObject"]; [bigObject setObject:number    forKey:@"myNumber"]; [bigObject setObject:string    forKey:@"myString"]; [bigObject setObject:date      forKey:@"myDate"]; [bigObject setObject:data      forKey:@"myData"]; [bigObject setObject:array     forKey:@"myArray"]; [bigObject setObject:dictionary forKey:@"myDictionary"]; [bigObject setObject:null      forKey:@"myNull"]; [bigObject saveInBackground]; Are supported: NSString, NSNumber, NSDate, NSData, and NSNull.You can nest NSDictionary and NSArray objects to store more structured data within a single PFObject.
  53. DevCamp PFQuery
  54. DevCamp Basic Queries PFQuery *query = [PFQuery queryWithClassName:@"Post"]; [query whereKey:@"title" equalTo:@"pragmamark"]; [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {   if (!error) {     // The find succeeded.     NSLog(@"Successfully retrieved %d scores.", objects.count);   } else {     // Log details of the failure     NSLog(@"Error: %@ %@", error, [error userInfo]);   } }]; The general pattern is to create a PFQuery, put conditions on it, and then retrieve a NSArray of matching PFObjects using either findObjectsInBackgroundWithBlock: or findObjectsInBackgroundWithTarget:selector:
  55. DevCamp Basic Queries // Only use this code if you are already running it in a background // thread, or for testing purposes! PFQuery *query = [PFQuery queryWithClassName:@"Post"]; [query whereKey:@"title" equalTo:@"pragmamark"]; NSArray* scoreArray = [query findObjects]; If you are already in a background thread:
  56. DevCamp NSPredicate NSPredicate *predicate = [NSPredicate predicateWithFormat:                           @"title = 'pragmamark'"]; PFQuery *query = [PFQuery queryWithClassName:@"Post" predicate:predicate]; These features are supported: • Simple comparisons such as =, !=, <, >, <=, >=, and BETWEEN with a key and a constant. • Containment predicates, such as x IN {1, 2, 3}. • Key-existence predicates, such as x IN SELF. • BEGINSWITH expressions. • Compound predicates with AND, OR, and NOT. • Sub-queries with "key IN %@", subquery. The following types of predicates are not supported: • Aggregate operations, such as ANY, SOME,ALL, or NONE. • Regular expressions, such as LIKE, MATCHES, CONTAINS, or ENDSWITH. • Predicates comparing one key to another. • Complex predicates with many ORed clauses.
  57. DevCamp Query Constraints [query whereKey:@"playerName" notEqualTo:@"Michael Yabuti"]; [query whereKey:@"playerAge" greaterThan:[NSNumber numberWithInt:18]]; query.limit = 10; // limit to at most 10 results query.skip = 10; // skip the first 10 results // Sorts the results in ascending order by the score field [query orderByAscending:@"score"];   // Sorts the results in descending order by the score field [query orderByDescending:@"score"]; // Restricts to wins < 50 [query whereKey:@"wins" lessThan:[NSNumber numberWithInt:50]];   // Restricts to wins <= 50 [query whereKey:@"wins" lessThanOrEqualTo:[NSNumber numberWithInt:50]];   // Restricts to wins > 50 [query whereKey:@"wins" greaterThan:[NSNumber numberWithInt:50]];   // Restricts to wins >= 50 [query whereKey:@"wins" greaterThanOrEqualTo:[NSNumber numberWithInt:50]];
  58. DevCamp Constraints on Array // Finds scores from anyone who is neither Jonathan, Dario, nor Shawn NSArray *names = [NSArray arrayWithObjects:@"Jonathan Walsh",                                            @"Dario Wunsch",                                            @"Shawn Simon",                                            nil]; [query whereKey:@"playerName" notContainedIn:names]; or [query whereKey:@"playerName" containedIn:names]; If you want to retrieve objects matching several different values
  59. DevCamp Constraints on Array // Find objects where the array in arrayKey contains 2. [query whereKey:@"arrayKey" equalTo:[NSNumber numberWithInt:2]]; For keys with an array type, you can find objects where the key's array value contains 2 by: // Find objects where the array in arrayKey contains each of the // elements 2, 3, and 4. [query whereKey:@"arrayKey" containsAllObjectsInArray:@[@2, @3, @4]]; You can also find objects where the key's array value contains each of the values 2, 3, and 4 with the following:
  60. DevCamp Check particular key // Finds objects that have the score set [query whereKeyExists:@"score"];   // Finds objects that don't have the score set [query whereKeyDoesNotExist:@"score"]; If you want to retrieve objects that have a particular key set or not:
  61. DevCamp Matches Key PFQuery *teamQuery = [PFQuery queryWithClassName:@"Team"]; [teamQuery whereKey:@"winPct" greaterThan:[NSNumber withDouble: 0.5]]; PFQuery *userQuery = [PFQuery queryForUser]; [userQuery whereKey:@"hometown" matchesKey:@"city" inQuery:teamQuery]; [userQuery findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {     // results will contain users with a hometown team with a winning record }]; You can use the whereKey:matchesKey:inQuery: method to get objects where a key matches the value of a key in a set of objects resulting from another query. (use whereKey:doesNotMatchKey:inQuery: to get objects where a key does not match)
  62. DevCamp Restrict the returned fields PFQuery *query = [PFQuery queryWithClassName:@"GameScore"]; [query selectKeys:@[@"playerName", @"score"]]; NSArray *results = [query findObjects]; The remaining fields can be fetched later by calling one of the fetchIfNeeded variants on the returned objects: PFObject *object = (PFObject*)[results objectAtIndex:0]; [object fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {   // all fields of the object will now be available here. }];
  63. DevCamp Queries on string value // Finds barbecue sauces that start with "Big Daddy's". PFQuery *query = [PFQuery queryWithClassName:@"BarbecueSauce"]; [query whereKey:@"name" hasPrefix:@"Big Daddy's"];
  64. DevCamp Relational queries // Assume PFObject *myPost was previously created. PFQuery *query = [PFQuery queryWithClassName:@"Comment"]; [query whereKey:@"post" equalTo:myPost];   [query findObjectsInBackgroundWithBlock:^(NSArray *comments, NSError *error) {     // comments now contains the comments for myPost }]; If you want to retrieve objects where a field matches a particular PFObject, you can use whereKey:equalTo: just like for other data types. [query whereKey:@"post"         equalTo:[PFObject objectWithoutDataWithClassName:@"Post" objectId:@"1zEcyElZ80"]];
  65. DevCamp Matches Query PFQuery *innerQuery = [PFQuery queryWithClassName:@"Post"]; [innerQuery whereKeyExists:@"image"]; PFQuery *query = [PFQuery queryWithClassName:@"Comment"]; [query whereKey:@"post" matchesQuery:innerQuery]; [query findObjectsInBackgroundWithBlock:^(NSArray *comments, NSError *error) {     // comments now contains the comments for posts with images }]; If you want to retrieve objects where a field contains (or not) a PFObject that match a different query, you can use whereKey:matchesQuery: (or whereKey:notMatchQuery:) Note that the default limit of 100 and maximum limit of 1000 apply to the inner query as well.
  66. DevCamp Include Key PFQuery *query = [PFQuery queryWithClassName:@"Comment"]; // Retrieve the most recent ones [query orderByDescending:@"createdAt"]; // Only retrieve the last ten query.limit = [NSNumber numberWithInt:10]; // Include the post data with each comment [query includeKey:@"post"];   [query findObjectsInBackgroundWithBlock:^(NSArray *comments, NSError *error) {     // Comments now contains the last ten comments, and the "post" field     // has been populated. For example:     for (PFObject *comment in comments) {          // This does not require a network access.          PFObject *post = [comment objectForKey:@"post"];          NSLog(@"retrieved related post: %@", post);     } }]; If you want to return multiple types of related objects in one query use includeKey: method:
  67. DevCamp Include Key [query includeKey:@""]; You can also do multi level includes using dot notation:
  68. DevCamp Caching Policy query.cachePolicy = kPFCachePolicyNetworkElseCache; The default query behavior doesn't use the cache, but you can enable caching by setting query.cachePolicy. • kPFCachePolicyIgnoreCache: is the default cache policy. • kPFCachePolicyCacheOnly :The query only loads from the cache, ignoring the network. If there are no cached results, that causes a PFError. • kPFCachePolicyNetworkOnly:The query does not load from the cache, but it will save results to the cache. • kPFCachePolicyCacheElseNetwork:The query first tries to load from the cache, but if that fails, it loads results from the network. If neither cache nor network succeed, there is a PFError. • kPFCachePolicyNetworkElseCache:The query first tries to load from the network, but if that fails, it loads results from the cache. If neither network nor cache succeed, there is a PFError. • kPFCachePolicyCacheThenNetwork:The query first loads from the cache, then loads from the network. In this case, the callback will actually be called twice - first with the cached results, then with the network results. Since it returns two results at different times, this cache policy cannot be used synchronously with findObjects.
  69. DevCamp Control the cache’s behavior BOOL isInCache = [query hasCachedResult]; Check to see if there is a cached result for the query with: [query clearCachedResult]; Remove any cached results for a query with: [PFQuery clearAllCachedResults]; Remove cached results for queries with: query.maxCacheAge = 60 * 60 * 24;  // One day, in seconds. Control the maximum age of a cached result with:
  70. DevCamp Counting Objects PFQuery *query = [PFQuery queryWithClassName:@"GameScore"]; [query whereKey:@"playername" equalTo:@"Sean Plott"]; [query countObjectsInBackgroundWithBlock:^(int count, NSError *error) {   if (!error) {     // The count request succeeded. Log the count     NSLog(@"Sean has played %d games", count);   } else {     // The request failed   } }]; If you just need to count how many objects match a query, but you do not need to retrieve the objects that match, you can use countObjects instead of findObjects.
  71. DevCamp Compound Queries PFQuery *lotsOfWins = [PFQuery queryWithClassName:@"Player"]; [lotsOfWins whereKey:@"wins" greaterThan:[NSNumber numberWithInt: 150]];   PFQuery *fewWins = [PFQuery queryWithClassName:@"Player"]; [fewWins whereKey:@"wins" lessThan:[NSNumber numberWithInt:5]]; PFQuery *query = [PFQuery orQueryWithSubqueries:[NSArray arrayWithObjects:fewWins,lotsOfWins,nil]]; [query findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {   // results contains players with lots of wins or only a few wins.   }]; If you want to find objects that match one of several queries, you can use orQueryWithSubqueries: method.
  72. DevCamp Subclasses
  73. DevCamp Subclassing PFObject 1.Declare a subclass which conforms to the PFSubclassing protocol. 2.Implement the class method parseClassName.This is the string you would pass to initWithClassName: and makes all future class name references unnecessary. 3.Import PFObject+Subclass in your .m file.This implements all methods in PFSubclassing beyond parseClassName. 4.Call [YourClass registerSubclass] in your ApplicationDelegate before Parse setApplicationId:clientKey:.
  74. DevCamp Example of Subclassing // Armor.h @interface Armor : PFObject<PFSubclassing> + (NSString *)parseClassName; @end   // Armor.m // Import this header to let Armor know that PFObject privately provides most // of the methods for PFSubclassing. #import <Parse/PFObject+Subclass.h>   @implementation Armor + (NSString *)parseClassName {   return @"Armor"; } @end   // AppDelegate.m #import <Parse/Parse.h> #import "Armor.h"   - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {   [Armor registerSubclass];   [Parse setApplicationId:parseAppId clientKey:parseClientKey]; }
  75. DevCamp Property & methods // Armor.h @interface Armor : PFObject<PFSubclassing> + (NSString *)parseClassName; @property (retain) NSString *displayName; @end   // Armor.m @dynamic displayName; @dynamic iconFile;   - (UIImageView *)iconView {   PFImageView *view = [[PFImageView alloc] initWithImage:kPlaceholderImage];   view.file = self.iconFile;   [view loadInBackground];   return [view autorelease]; }
  76. DevCamp Query PFQuery *query = [Armor query]; [query whereKey:@"rupees" lessThanOrEqualTo:PFUser.currentUser.rupees]; [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {   if (!error) {     Armor *firstArmor = [objects objectAtIndex:0];     // ...   } }];
  77. DevCamp PFFile
  78. DevCamp Why PFFile? PFFile lets you store application files in the cloud that would otherwise be too large or cumbersome to fit into a regular PFObject. (up to 10 megabytes)
  79. DevCamp Saving a file on the cloud Then you can associate a PFFile onto a PFObject just like any other piece of data: NSData *data = [@"Working at Parse is great!" dataUsingEncoding:NSUTF8StringEncoding]; PFFile *file = [PFFile fileWithName:@"resume.txt" data:data]; [file saveInBackground]; PFObject *jobApplication = [PFObject objectWithClassName:@"JobApplication"] [jobApplication setObject:@"Joe Smith" forKey:@"applicantName"]; [jobApplication setObject:file        forKey:@"applicantResumeFile"]; [jobApplication saveInBackground];
  80. DevCamp Upload / Download NSData *data = [@"Working at Parse is great!" dataUsingEncoding:NSUTF8StringEncoding]; PFFile *file = [PFFile fileWithName:@"resume.txt" data:data]; [file saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {   // Handle success or failure here ... } progressBlock:^(int percentDone) {   // Update your progress spinner here. percentDone will be between 0 and 100. }]; Use saveInBackgroundWithBlock:progressBlock: and getDataInBackgroundWithBlock:progressBlock:
  81. DevCamp PFUser
  82. DevCamp SignUp / SignIn PFUser *user = [PFUser user];     user.username = @"my name";     user.password = @"my pass"; = @"";       // other fields can be set just like with PFObject     [user setObject:@"415-392-0202" forKey:@"phone"];       [user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {       if (!error) {           // Hooray! Let them use the app now.       } else {           NSString *errorString = [[error userInfo] objectForKey:@"error"];           // Show the errorString somewhere and let the user try again.       }     }]; [PFUser logInWithUsernameInBackground:@"myname" password:@"mypass"   block:^(PFUser *user, NSError *error) {     if (user) {         // Do stuff after successful login.     } else {         // The login failed. Check error to see why.     } }]
  83. DevCamp Current User PFUser *currentUser = [PFUser currentUser]; To get the current logged user you use the cached currentUser object: [PFUser logOut]; You can clear the current user by logging them out:
  84. DevCamp PFACL
  85. DevCamp Security To limit access to some users you have many possibility: • ACLWithUser: • setReadAccess:forUser: or setWriteAccess:forUser: • setPublicReadAccess: or setPublicWriteAccess: • setDefaultACL:withAccessForCurrentUser:
  86. DevCamp ACL methods PFObject *groupMessage = [PFObject objectWithClassName:@"Message"]; PFACL *groupACL = [PFACL ACL];       // userList is an NSArray with the users we are sending this message to. for (PFUser *user in userList) {     [groupACL setReadAccess:YES forUser:user];     [groupACL setWriteAccess:YES forUser:user]; } groupMessage.ACL = [PFACL ACLWithUser:[PFUser currentUser]];  [groupACL setPublicReadAccess:YES]; groupMessage.ACL = groupACL; [groupMessage saveInBackground];
  87. DevCamp Default ACL To help ensure that your users' data is secure by default, you can set a default ACL: // data is visible to the world (readonly) PFACL *defaultACL = [PFACL ACL]; [defaultACL setPublicReadAccess:YES]; [PFACL setDefaultACL:defaultACL withAccessForCurrentUser:YES]; // data is only accessible by the user itself [PFACL setDefaultACL:[PFACL ACL] withAccessForCurrentUser:YES];
  88. DevCamp PFRole
  89. DevCamp Grouping users Roles provide a logical way of grouping users with common access privileges to your Parse data: • Administrator • User • Guest • ...
  90. DevCamp Create Role // By specifying no write privileges for the ACL, we can ensure the role cannot be altered. PFACL *roleACL = [PFACL ACL]; [roleACL setPublicReadAccess:YES]; PFRole *role = [PFRole roleWithName:@"Administrator" acl:roleACL]; [role saveInBackground]; PFRole *role = [PFRole roleWithName:roleName acl:roleACL]; for (PFUser *user in usersToAddToRole) {   [role.users addObject:user]; } [role saveInBackground];
  91. DevCamp Security for Objects PFRole *moderators = /* Query for some PFRole */; PFObject *wallPost = [PFObject objectWithClassName:@"WallPost"]; PFACL *postACL = [PFACL ACL]; [postACL setWriteAccess:YES forRole:moderators]; wallPost.ACL = postACL; [wallPost saveInBackground];
  92. DevCamp Security for Objects PFRole *moderators = /* Query for some PFRole */; PFObject *wallPost = [PFObject objectWithClassName:@"WallPost"]; PFACL *postACL = [PFACL ACL]; [postACL setWriteAccess:YES forRole:moderators]; wallPost.ACL = postACL; [wallPost saveInBackground]; [postACL setWriteAccess:YES forRoleWithName:@"Moderators"];
  93. DevCamp Hierarchy PFRole *administrators = /* Your "Administrators" role */; PFRole *moderators = /* Your "Moderators" role */; [moderators.roles addObject:administrators]; [moderators saveInBackground]; Any user with Administrator privileges should also be granted the permissions of any Moderator:
  94. DevCamp Best practise [PFUser enableAutomaticUser]; PFACL *defaultACL = [PFACL ACL]; // Optionally enable public read access while disabling public write access. // [defaultACL setPublicReadAccess:YES]; [PFACL setDefaultACL:defaultACL withAccessForCurrentUser:YES]; • restrict access to data as much as possible • specify a default ACL based upon the current user
  95. DevCamp Demo
  96. DevCamp PFPush
  97. DevCamp Push notitification - (void)application:(UIApplication *)application         didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {     // Store the deviceToken in the current Installation and save it to Parse.     PFInstallation *currentInstallation = [PFInstallation currentInstallation];     [currentInstallation setDeviceTokenFromData:deviceToken];     [currentInstallation saveInBackground]; } Every Parse application installed on a device registered for push notifications has an associated Installation object.The Installation object is where you store all the data needed to target push notifications.
  98. DevCamp Installation While it is possible to modify a PFInstallation just like you would a PFObject, there are several special fields that help manage and target devices. • badge:The current value of the icon badge for iOS apps. Changing this value on the PFInstallation will update the badge value on the app icon. Changes should be saved to the server so that they will be used for future badge-increment push notifications. • channels:An array of the channels to which a device is currently subscribed. • timeZone:The current time zone where the target device is located.This value is synchronized every time anInstallation object is saved from the device (readonly). • deviceType:The type of device, "ios", "android", "winrt", "winphone", or "dotnet"(readonly). • installationId: Unique Id for the device used by Parse (readonly). • deviceToken:The Apple generated token used for iOS devices (readonly). • channelUris:The Microsoft-generated push URIs for Windows devices (readonly). • appName:The display name of the client application to which this installation belongs (readonly). • appVersion:The version string of the client application to which this installation belongs (readonly). • parseVersion:The version of the Parse SDK which this installation uses (readonly). • appIdentifier:A unique identifier for this installation's client application. In iOS, this is the Bundle Identifier(readonly).
  99. DevCamp Channels // When users indicate they are pragmamark fans, we subscribe them to that channel. PFInstallation *currentInstallation = [PFInstallation currentInstallation]; // When users indicate they are no longer Giants fans, we unsubscribe them. // [currentInstallation removeObject:@”pragmamark” forKey:@”channels”]; [currentInstallation addUniqueObject:@"pragmamark" forKey:@"channels"]; [currentInstallation saveInBackground]; This allows you to use a publisher-subscriber model for sending pushes. Devices start by subscribing to one or more channels, and notifications can later be sent to these subscribers.
  100. DevCamp Sending Pushes to channel // Send a notification to all devices subscribed to the "Giants" channel. PFPush *push = [[PFPush alloc] init]; [push setChannel:@"pragmamark"]; // [push setChannels:@[@”pragmamark”, @”devcamp”]]; [push setMessage:@"PragmaDevCamp is coming!!"]; [push sendPushInBackground];
  101. DevCamp Sending Pushes to queries // Create our Installation query PFQuery *pushQuery = [PFInstallation query]; [pushQuery whereKey:@"local" equalTo:@"IT"];   // Send push notification to query PFPush *push = [[PFPush alloc] init]; [push setQuery:pushQuery]; // Set our Installation query [push setMessage:@"Ancora pochi giorni per registrarsi al DevCamp"]; [push sendPushInBackground];
  102. DevCamp Customizing • alert: the notification's message. • badge: (iOS only) the value indicated in the top right corner of the app icon.This can be set to a value or toIncrement in order to increment the current value by 1. • sound: (iOS only) the name of a sound file in the application bundle. • content-available: (iOS only) if you are using Newsstand, set this value to 1 to trigger a background download. • action: (Android only) the Intent should be fired when the push is received. If not title or alert values are specified, the Intent will be fired but no notification will appear to the user. • title: (Android & Windows 8 only) the value displayed in the Android system tray or Windows toast notification.
  103. DevCamp Customizing example // Create date object for tomorrow NSDateComponents *comps = [[NSDateComponents alloc] init]; [comps setYear:2013]; [comps setMonth:5]; [comps setDay:30]; NSCalendar *gregorian =   [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; NSDate *date = [gregorian dateFromComponents:comps];   // Send push notification with expiration date PFPush *push = [[PFPush alloc] init]; [push expireAtDate:date]; // [push expireAfterTimeInterval: 60 * 60 * 24 * 7] - 1 week [push setQuery:everyoneQuery]; [push setMessage:@"Season tickets on sale until May 30th"]; [push sendPushInBackground];
  104. DevCamp Receiving pushes - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {   . . .   // Extract the notification data   NSDictionary *notificationPayload = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];   } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {   // Create empty photo object   NSString *photoId = [userInfo objectForKey:@"p"]; }
  105. DevCamp Clearing the badge - (void)applicationDidBecomeActive:(UIApplication *)application {   PFInstallation *currentInstallation = [PFInstallation currentInstallation];   if (currentInstallation.badge != 0) {     currentInstallation.badge = 0;     [currentInstallation saveEventually];   }   // ... }
  106. DevCamp Demo
  107. DevCamp • • • easily-create-a-web-backend-for-your-apps-with- parse Link
  108. DevCamp Domande? @pragmamarkorg
  109. DevCamp NSLog(@”Thank you!”);