Dictionary-to-model framework mantle: The most commonly used iOS model for foreign programmers

Source: Internet
Author: User
Tags dateformat

Mantle Introduction

Mantle is a simple and efficient model-layer framework based on OBJECTIVE-C written under iOS and Mac platforms.

What can mantle do?

Mantle can easily convert the JSON data, dictionaries (Dictionary) and models (that is, objective objects) to each other, support custom mappings, and implement nscoding and nscoping built-in, greatly simplifying archive operations.

Why to use the mantle traditional model layer scheme encountered problems

What's the problem with the model layer that we write with objective-c usually?

We can use the Github API for example. Now suppose we want to show a Github Issue with objective-c, what should we do?

Now we can think of

    1. Parse the JSON data dictionary directly and then show it to the UI

    2. Convert JSON data to a model, assigning a value to the UI

About 1, there are many drawbacks, you can refer to my article: using the dictionary to the model in iOS development, now assume that we have selected 2, we will roughly define the following GHIssue model:

GHIssue.h

  #import <Foundation/Foundation.h>  typedef enum : NSUInteger {      GHIssueStateOpen,      GHIssueStateClosed  } GHIssueState;    @class GHUser;  @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, readonly) NSDate *retrievedAt;    @property (nonatomic, copy) NSString *title;  @property (nonatomic, copy) NSString *body;    - (instancetype)initWithDictionary:(NSDictionary *)dictionary;    @end

Ghissue.m

  #import "GHIssue.h" #import "GHUser.h" @implementation ghissue + (NSDateFormatter *) dateformatter {NSDATEFO      Rmatter *dateformatter = [[NSDateFormatter alloc] init];      Dateformatter.locale = [[Nslocale alloc] initwithlocaleidentifier:@ "En_us_posix"];      Dateformatter.dateformat = @ "Yyyy-mm-dd ' T ' HH:mm:ss ' Z '";  return dateformatter;      }-(Instancetype) 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];      _retrievedat = [NSDate Date];      _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;      }-(Instancetype) 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 decodeintegerforkey:@ "state"];      _title = [Coder decodeobjectforkey:@ "title"];      _retrievedat = [NSDate Date];      _body = [Coder decodeobjectforkey:@ "Body"];      _reporterlogin = [Coder decodeobjectforkey:@ "Reporterlogin"];      _assignee = [Coder decodeobjectforkey:@ "assignee"];            _updatedat = [Coder decodeobjectforkey:@ "Updatedat"];  return self; }-(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 encodeInteger:self.state forkey:@ "state"];      }-(Instancetype) 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->_retrievedat = [NSDate Date];            Issue.body = Self.body;  return issue;  }-(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]; }

GHUser.h

  @interface ghuser:nsobject <nscoding, nscopying> @property (nonatomic, copy) NSString *login;  @property (nonatomic, assign) Nsuinteger ID;  @property (nonatomic, copy) NSString *avatarurl;  @property (nonatomic, copy) NSString *gravatarid;  @property (nonatomic, copy) NSString *url;  @property (nonatomic, copy) NSString *htmlurl;  @property (nonatomic, copy) NSString *followersurl;  @property (nonatomic, copy) NSString *followingurl;  @property (nonatomic, copy) NSString *gistsurl;  @property (nonatomic, copy) NSString *starredurl;  @property (nonatomic, copy) NSString *subscriptionsurl;  @property (nonatomic, copy) NSString *organizationsurl;  @property (nonatomic, copy) NSString *reposurl;  @property (nonatomic, copy) NSString *eventsurl;  @property (nonatomic, copy) NSString *receivedeventsurl;  @property (nonatomic, copy) NSString *type;    @property (nonatomic, assign) BOOL siteadmin;    -(ID) initwithdictionary: (Nsdictionary *) dictionary; @end  

You will see that there are many drawbacks to such a simple thing. There are even some other problems that are not shown in this example.

    1. The server's new data cannot be used to update this GHIssue
    2. Cannot in turn GHIssue convert JSON to
    3. For GHIssueState , if the enumeration is adapted, the existing archive will crash
    4. If the GHIssue interface changes, the existing archive will crash.
Using Mtlmodel

If we use Mtlmodel, we can declare that a class inherits from Mtlmodel

  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;    @property (nonatomic, copy, readonly) NSDate *retrievedat; @end @implementation ghissue + (NSDateFormatter *) dateformatter {NSDateFormatter *dateformatter = [[Nsdateformat      Ter alloc] init];      Dateformatter.locale = [[Nslocale alloc] initwithlocaleidentifier:@ "En_us_posix"]; Dateformatter.dateformat = @ "Yyyy-mm-dd ' T ' hh:mm:SS ' Z ' ";  return dateformatter; } + (Nsdictionary *) Jsonkeypathsbypropertykey {return @{@ "url": @ "url" @ "Htmlurl": @ "Html_url ", @" number ": @" number "@" state ": @" state "@" Reporterlogin ": @" User.login "@" Assign  EE ": @" Assignee "@" Updatedat ": @" Updated_at "}; } + (Nsvaluetransformer *) Urljsontransformer {return [Nsvaluetransformer Valuetransformerforname:mtlurlvaluetrans  Formername]; } + (Nsvaluetransformer *) Htmlurljsontransformer {return [Nsvaluetransformer Valuetransformerforname:mtlurlvaluet  Ransformername]; } + (Nsvaluetransformer *) Statejsontransformer {return [Nsvaluetransformer mtl_valuemappingtransformerwithdiction  ary:@{@ "Open": @ (Ghissuestateopen), @ "closed": @ (ghissuestateclosed)}]; } + (Nsvaluetransformer *) Assigneejsontransformer {return [Mtljsonadapter Dictionarytransformerwithmodelclass:ghu  Ser.class]; } + (NsvaluetransformeR *) Updatedatjsontransformer {return [Mtlvaluetransformer transformerusingforwardblock:^id (NSString *dateString, BOO      L *success, Nserror *__autoreleasing *error) {return [self.dateformatter datefromstring:datestring]; } reverseblock:^id (NSDate *date, BOOL *success, Nserror *__autoreleasing *error) {return [Self.dateformatter str      Ingfromdate:date];  }]; }-(Instancetype) Initwithdictionary: (nsdictionary *) Dictionaryvalue Error: (Nserror *) error {self = [super INITW      Ithdictionary:dictionaryvalue Error:error];        if (self = = nil) return nil;      Store a value that needs to be determined locally upon initialization.        _retrievedat = [NSDate Date];  return self; } @end

It is clear that we do not need to achieve, <NSCoding> <NSCopying> -isEqual: and -hash . Within your subclass of life attributes, Mtlmodel can provide the default implementation of these methods.

The problems in the original example are well settled here.

    • Mtlmodel provides one - (void)mergeValueForKey:(NSString *)key fromModel:(id<MTLModel>)model{} that can be integrated with any other model object that implements the Mtlmodel protocol.

    • +[MTLJSONAdapter JSONDictionaryFromModel:error:]It can be similar to anything MTLJSONSerializing>``协议的对象转换成JSON字典, that follows +[mtljsonadapter Jsonarrayfrommodels:error:] ", but the conversion is an array.

MTLJSONAdapterfromJSONDictionaryand JSONDictionaryFromModel can implement the transformation of the model and JSON.

JSONKeyPathsByPropertyKeyYou can implement custom mappings for models and JSON.

JSONTransformerForKeyYou can map different types of JSON and models.

classForParsingJSONDictionaryIf you are using a class cluster (refer to: The application of the cluster in iOS development), Classforparsingjsondictionary allows you to choose which class to use for JSON deserialization.

    • Mtlmodel can use a well-documented storage model without the need to implement boring nscoding protocols. The -decodeValueForKey:withCoder:modelVersion: method is automatically invoked when decoding, and can be easily customized if overridden.
Persistent mantle Mate archiving

The Mtlmodel protocol is implemented by default NSCoding and can be NSKeyedArchiver easily archived and reconciled with objects.

Mantle with Core Data

In addition to SQLite, Fmdb, if you want to perform complex queries in your data, handle many relationships, and support undo recovery, Core data is ideal.

However, this also brings some pain points:

    • There are still a lot of drawbacks Managed objects to solve the above see some of the drawbacks, but core data self-birth also has his shortcomings. It requires a lot of line code to properly configure core data and get it.
    • It is difficult to maintain correctness. Even experienced people make mistakes when using core data, and these problem frameworks cannot be solved.

If you want to get a JSON object, Core data needs to do a lot of work, but it can only get very little return.

However, if you have already used the core data,mantle in your app, it will still be a convenient conversion layer between your API and your managed model objects.

Mantle with Magicrecord (a core data framework)

Reference Magicalrecord Mate Mantle

The benefits mantle brings to us
    • Implements the Nscopying protocol, the subclass can direct copy is how cool thing

    • Realized the Nscoding protocol, say goodbye to Nsuserdefaults

    • Provides-isequal: And-hash default implementations, model as Nsdictionary key facilitates many

    • Support for custom mappings, which is useful in case of interface changes

    • Simple and do a good job, not doping network-related operations

Reasonable choice

Although the above said a series of benefits, but if your app's code size only tens of thousands of lines, or API only more than 10, or do not encounter these problems, suggest or not to introduce, kill chicken with nail knife is enough. However, the implementation and ideas of mantle are worth learning and drawing lessons from every iOS engineer.

Code

Https://github.com/terwer/MantleDemo

Reference

Https://github.com/mantle/mantle

http://segmentfault.com/a/1190000002431365

The use of the

Dictionary to model framework mantle: The iOS model most commonly used by foreign programmers

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.