Top Banner
Mantle Dominik Gruber, @the_dom Cocoaheads Vienna – Aug. 8, 2013 https://github.com/github/Mantle
23

2013-08-08 | Mantle (Cocoaheads Vienna)

Jan 18, 2015

Download

Technology

Dominik Gruber

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

Talk given at Cocoaheads Vienna on Aug. 8, 2013
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: 2013-08-08 | Mantle (Cocoaheads Vienna)

Mantle

Dominik Gruber, @the_dom Cocoaheads Vienna – Aug. 8, 2013

https://github.com/github/Mantle

Page 2: 2013-08-08 | Mantle (Cocoaheads Vienna)

Agenda

• Context

• Core Data

• Alternatives to Core Data

• Mantle

Dominik Gruber @the_dom

Page 3: 2013-08-08 | Mantle (Cocoaheads Vienna)
Page 4: 2013-08-08 | Mantle (Cocoaheads Vienna)

Core Data

• Obvious choice

• Well-documented

• It works

• But...

• It’s slow

• No direct SQL queries

• A lot of boilerplate

Dominik Gruber @the_dom

Page 5: 2013-08-08 | Mantle (Cocoaheads Vienna)

Why is Core Data SO slow?

• No mass update/delete

• Synchronization between NSManagedObjectContext  

• No transactions

Dominik Gruber @the_dom

Page 6: 2013-08-08 | Mantle (Cocoaheads Vienna)

Alternatives

• Raw SQLite

• libsqlite3.dylib

• FMDB

• NSCoder

• Mantle

Dominik Gruber @the_dom

Page 7: 2013-08-08 | Mantle (Cocoaheads Vienna)

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

Page 8: 2013-08-08 | Mantle (Cocoaheads Vienna)

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

Page 9: 2013-08-08 | Mantle (Cocoaheads Vienna)

-­‐  (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

Page 10: 2013-08-08 | Mantle (Cocoaheads Vienna)

-­‐  (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

Page 11: 2013-08-08 | Mantle (Cocoaheads Vienna)

-­‐  (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

Page 12: 2013-08-08 | Mantle (Cocoaheads Vienna)

-­‐  (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

Page 13: 2013-08-08 | Mantle (Cocoaheads Vienna)

-­‐  (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

Page 14: 2013-08-08 | Mantle (Cocoaheads Vienna)

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

Page 15: 2013-08-08 | Mantle (Cocoaheads Vienna)

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

Page 16: 2013-08-08 | Mantle (Cocoaheads Vienna)

@implementation  GHIssue  !+  (NSDictionary  *)JSONKeyPathsByPropertyKey  {          return  @{                  @"URL":  @"url",                  @"HTMLURL":  @"html_url",                  @"reporterLogin":  @"user.login",                  @"assignee":  @"assignee",                  @"updatedAt":  @"updated_at"          };  }  

Dominik Gruber @the_dom

Page 17: 2013-08-08 | Mantle (Cocoaheads Vienna)

+  (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

Page 18: 2013-08-08 | Mantle (Cocoaheads Vienna)

+

• Automatically implemented

• <NSCoding>!

• <NSCopying>!

• -­‐isEqual:!

• -­‐hash  

• [MTLJSONAdapter  JSONDictionaryFromModel:]  

• It’s possible to handle interface changes with Mantle

Dominik Gruber @the_dom

Page 19: 2013-08-08 | Mantle (Cocoaheads Vienna)

-

Dominik Gruber @the_dom

• Bad documentation

• No persistence

• Some pitfalls

Page 20: 2013-08-08 | Mantle (Cocoaheads Vienna)

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

Page 21: 2013-08-08 | Mantle (Cocoaheads Vienna)

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

Page 22: 2013-08-08 | Mantle (Cocoaheads Vienna)

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

Page 23: 2013-08-08 | Mantle (Cocoaheads Vienna)

Q & A

Dominik Gruber @the_dom