2013-08-08 | Mantle (Cocoaheads Vienna)

  • 173 views
Uploaded on

Introduction to Mantle, a Model framework for Cocoa and Cocoa Touch. …

Introduction to Mantle, a Model framework for Cocoa and Cocoa Touch.

Talk given at Cocoaheads Vienna on Aug. 8, 2013

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
173
On Slideshare
0
From Embeds
0
Number of Embeds
1

Actions

Shares
Downloads
3
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Mantle https://github.com/github/Mantle Dominik Gruber, @the_dom Cocoaheads Vienna – Aug. 8, 2013
  • 2. Agenda • • • • Context Core Data Alternatives to Core Data Mantle Dominik Gruber @the_dom
  • 3. Core Data • • Obvious choice • • Well-documented It works But... • • • It’s slow No direct SQL queries A lot of boilerplate Dominik Gruber @the_dom
  • 4. Why is Core Data SO slow? • • • No mass update/delete Synchronization between NSManagedObjectContext   No transactions Dominik Gruber @the_dom
  • 5. Alternatives • • Raw SQLite • • libsqlite3.dylib FMDB NSCoder • Mantle Dominik Gruber @the_dom
  • 6. NSCoder “(...) the interface used by concrete subclasses to transfer objects and other Objective-C data items between memory and some other format. This capability provides the basis for archiving and distribution.” Dominik Gruber @the_dom
  • 7. typedef  enum  :  NSUInteger  {          GHIssueStateOpen,          GHIssueStateClosed   }  GHIssueState;   ! @interface  GHIssue  :  NSObject  <NSCoding,  NSCopying>   ! @property  (nonatomic,  copy,  readonly)  NSURL  *URL;   @property  (nonatomic,  copy,  readonly)  NSURL  *HTMLURL;   @property  (nonatomic,  copy,  readonly)  NSNumber  *number;   @property  (nonatomic,  assign,  readonly)  GHIssueState  state;   @property  (nonatomic,  copy,  readonly)  NSString  *reporterLogin;   @property  (nonatomic,  copy,  readonly)  NSDate  *updatedAt;   @property  (nonatomic,  strong,  readonly)  GHUser  *assignee;   ! @property  (nonatomic,  copy)  NSString  *title;   @property  (nonatomic,  copy)  NSString  *body;   ! -­‐  (id)initWithDictionary:(NSDictionary  *)dictionary;   ! @end Dominik Gruber @the_dom
  • 8. -­‐  (id)initWithDictionary:(NSDictionary  *)dictionary  {          self  =  [self  init];          if  (self  ==  nil)  return  nil;   !        _URL  =  [NSURL  URLWithString:dictionary[@"url"]];          _HTMLURL  =  [NSURL  URLWithString:dictionary[@"html_url"]];          _number  =  dictionary[@"number"];   !        if  ([dictionary[@"state"]  isEqualToString:@"open"])  {                  _state  =  GHIssueStateOpen;          }  else  if  ([dictionary[@"state"]  isEqualToString:@"closed"])  {                  _state  =  GHIssueStateClosed;          }   !        _title  =  [dictionary[@"title"]  copy];          _body  =  [dictionary[@"body"]  copy];          _reporterLogin  =  [dictionary[@"user"][@"login"]  copy];          _assignee  =  [[GHUser  alloc]   initWithDictionary:dictionary[@"assignee"]];   !        _updatedAt  =  [self.class.dateFormatter   dateFromString:dictionary[@"updated_at"]];   !        return  self;   Dominik Gruber @the_dom
  • 9. -­‐  (id)initWithCoder:(NSCoder  *)coder  {          self  =  [self  init];          if  (self  ==  nil)  return  nil;   !        _URL  =  [coder  decodeObjectForKey:@"URL"];          _HTMLURL  =  [coder  decodeObjectForKey:@"HTMLURL"];          _number  =  [coder  decodeObjectForKey:@"number"];          _state  =  [coder  decodeUnsignedIntegerForKey:@"state"];          _title  =  [coder  decodeObjectForKey:@"title"];          _body  =  [coder  decodeObjectForKey:@"body"];          _reporterLogin  =  [coder   decodeObjectForKey:@"reporterLogin"];          _assignee  =  [coder  decodeObjectForKey:@"assignee"];          _updatedAt  =  [coder  decodeObjectForKey:@"updatedAt"];   !        return  self;   } Dominik Gruber @the_dom
  • 10. -­‐  (void)encodeWithCoder:(NSCoder  *)coder  {          if  (self.URL  !=  nil)                  [coder  encodeObject:self.URL  forKey:@"URL"];          if  (self.HTMLURL  !=  nil)                  [coder  encodeObject:self.HTMLURL  forKey:@"HTMLURL"];          if  (self.number  !=  nil)                  [coder  encodeObject:self.number  forKey:@"number"];          if  (self.title  !=  nil)                  [coder  encodeObject:self.title  forKey:@"title"];          if  (self.body  !=  nil)                  [coder  encodeObject:self.body  forKey:@"body"];          if  (self.reporterLogin  !=  nil)                  [coder  encodeObject:self.reporterLogin   forKey:@"reporterLogin"];          if  (self.assignee  !=  nil)                  [coder  encodeObject:self.assignee  forKey:@"assignee"];          if  (self.updatedAt  !=  nil)                  [coder  encodeObject:self.updatedAt  forKey:@"updatedAt"];   !        [coder  encodeUnsignedInteger:self.state  forKey:@"state"];   } Dominik Gruber @the_dom
  • 11. -­‐  (id)copyWithZone:(NSZone  *)zone  {          GHIssue  *issue  =  [[self.class  allocWithZone:zone]  init];          issue-­‐>_URL  =  self.URL;          issue-­‐>_HTMLURL  =  self.HTMLURL;          issue-­‐>_number  =  self.number;          issue-­‐>_state  =  self.state;          issue-­‐>_reporterLogin  =  self.reporterLogin;          issue-­‐>_assignee  =  self.assignee;          issue-­‐>_updatedAt  =  self.updatedAt;   !        issue.title  =  self.title;          issue.body  =  self.body;   } Dominik Gruber @the_dom
  • 12. -­‐  (NSUInteger)hash  {          return  self.number.hash;   }   ! -­‐  (BOOL)isEqual:(GHIssue  *)issue  {          if  (![issue  isKindOfClass:GHIssue.class])  return  NO;   !        return  [self.number  isEqual:issue.number]  &&   [self.title  isEqual:issue.title]  &&  [self.body   isEqual:issue.body];   }   Dominik Gruber @the_dom
  • 13. Mantle • Simple Model Layer for iOS and OS X • Currently Version 1.2 • First release in October 2012 • Developed by GitHub
 https://github.com/github/Mantle Dominik Gruber @the_dom
  • 14. typedef  enum  :  NSUInteger  {          GHIssueStateOpen,          GHIssueStateClosed   }  GHIssueState;   ! @interface  GHIssue  :  MTLModel  <MTLJSONSerializing>   ! @property  (nonatomic,  copy,  readonly)  NSURL  *URL;   @property  (nonatomic,  copy,  readonly)  NSURL  *HTMLURL;   @property  (nonatomic,  copy,  readonly)  NSNumber  *number;   @property  (nonatomic,  assign,  readonly)  GHIssueState  state;   @property  (nonatomic,  copy,  readonly)  NSString  *reporterLogin;   @property  (nonatomic,  strong,  readonly)  GHUser  *assignee;   @property  (nonatomic,  copy,  readonly)  NSDate  *updatedAt;   ! @property  (nonatomic,  copy)  NSString  *title;   @property  (nonatomic,  copy)  NSString  *body;   ! @end Dominik Gruber @the_dom
  • 15. @implementation  GHIssue   ! +  (NSDictionary  *)JSONKeyPathsByPropertyKey  {          return  @{                  @"URL":  @"url",                  @"HTMLURL":  @"html_url",                  @"reporterLogin":  @"user.login",                  @"assignee":  @"assignee",                  @"updatedAt":  @"updated_at"          };   }   Dominik Gruber @the_dom
  • 16. +  (NSValueTransformer  *)URLJSONTransformer  {          return  [NSValueTransformer   valueTransformerForName:MTLURLValueTransformerName];   }   ! +  (NSValueTransformer  *)stateJSONTransformer  {          NSDictionary  *states  =  @{                  @"open":  @(GHIssueStateOpen),                  @"closed":  @(GHIssueStateClosed)          };   !        return  [MTLValueTransformer   reversibleTransformerWithForwardBlock:^(NSString  *str)  {                  return  states[str];          }  reverseBlock:^(NSNumber  *state)  {                  return  [states  allKeysForObject:state].lastObject;          }];   } Dominik Gruber @the_dom
  • 17. + • • • Automatically implemented • • • • <NSCoding>! <NSCopying>! -­‐isEqual:! -­‐hash   [MTLJSONAdapter  JSONDictionaryFromModel:]   It’s possible to handle interface changes with Mantle Dominik Gruber @the_dom
  • 18. • • • Bad documentation No persistence Some pitfalls Dominik Gruber @the_dom
  • 19. Persistence // Persisting NSMutableData *data = [NSMutableData data]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; [self encodeWithCoder:archiver]; [archiver finishEncoding]; [data writeToFile:[self storagePath] atomically:YES]; Dominik Gruber @the_dom
  • 20. Persistence // Loading id item = nil; NSString *path = [self storagePathForItemId:itemId]; if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { NSData *data = [NSData dataWithContentsOfFile:path]; NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; item = [[[self class] alloc] initWithCoder:unarchiver]; [unarchiver finishDecoding]; } Dominik Gruber @the_dom
  • 21. How does it work? // NSKeyValueCoding if (![obj validateValue:&validatedValue forKey:key error:error]) { return NO; } ! if (value != validatedValue) { [obj setValue:validatedValue forKey:key]; } Dominik Gruber @the_dom
  • 22. Q &A Dominik Gruber @the_dom