Mantle로 모델 레이어 구축

7980 단어
프로젝트 개발 과정에서 자주 사용자 정의 모델을 만들고 서버에 데이터를 요청한 후 (일반적으로 Json 데이터) 사전 추출 방식으로 사용자 정의 모델에 값을 부여하여 데이터 대상으로 봉인해야 한다.이렇게 하면 몇 가지 문제가 있다.
  • 서버가 필드를 업데이트하거나 추가하면 클라이언트가 모델별로 초기화된 곳에서 값 추출 필드를 수정하거나 추가하는 과정이 번거롭다.
  • 이러한 사용자 정의 모델 대상의 서열화를 실현하여 로컬에 저장하려면 자신이 하나하나 실현해야 하고 필드를 추가하거나 수정할 때도 하나하나 변경해야 하며 과정이 번거롭다.
  • 사용자 정의 모델은 복사할 수 없습니다. 프로토콜을 실현하지 않으면 Json으로 반서열화할 수 없습니다.

  • 다행히도 위대한 github 엔지니어들은 OC 플랫폼에서 디자인 최적화, 고도의 통일된 프레임워크Mantle를 제공하여 이런 문제들을 해결했다.

    맨틀이 우리에게 가져다 준 것:

  • 실현NSCopying protocol, 자류는 드디어 직접copy가 가능
  • 실현NSCoding protocol,NSKeyedArchiver을 통해 로컬에 저장할 수 있습니다.(NSUserDefaults 교체 선택)
  • 제공 -isEqual: 및 -hash 의 기본 구현, 모델은 NSDictionary의 키로서 많이 편리해졌다
  • Model과 Json 메타데이터 간에 상호 변환 가능
  • Model 기본 사용 방법


    사용자 정의 모델


    사용자 정의 모델은 모두 통합된 자체 MTLModel 프로토콜이 필요합니다. 예를 들어 다음과 같습니다.
    typedef enum : NSUInteger {
        GHIssueStateOpen,
        GHIssueStateClosed
    } GHIssueState;
    
    @interface GHIssue : MTLModel 
    
    @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
    

    m 파일은 다음과 같습니다.
    @implementation GHIssue
    
    + (NSDateFormatter *)dateFormatter {
        NSDateFormatter *dateFormatter = [[NSDateFormatter 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",
            @"reporterLogin": @"user.login",
            @"assignee": @"assignee",
            @"updatedAt": @"updated_at"
    };
    }
    
    + (NSValueTransformer *)URLJSONTransformer {
        return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName];
    }
    
    + (NSValueTransformer *)HTMLURLJSONTransformer {
        return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName];
    }
    
    + (NSValueTransformer *)stateJSONTransformer {
        return [NSValueTransformer mtl_valueMappingTransformerWithDictionary:@{
            @"open": @(GHIssueStateOpen),
            @"closed": @(GHIssueStateClosed)
        }];
    }
    
    + (NSValueTransformer *)assigneeJSONTransformer {
        return [NSValueTransformer mtl_JSONDictionaryTransformerWithModelClass:GHUser.class];
    }
    
    + (NSValueTransformer *)updatedAtJSONTransformer {
        return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSString *str) {
            return [self.dateFormatter dateFromString:str];
        } reverseBlock:^(NSDate *date) {
            return [self.dateFormatter stringFromDate:date];
        }];
    }
    
    - (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error {
        self = [super initWithDictionary: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
    

    MTLJSONSerializing

    MTLJSONSerializing로부터 계승되고 MTLModel 협의를 실현한 대상은 이렇게 전환할 수 있다
    사전 데이터(JSONDictionary 는 사전 메타데이터를 의미)에서 모델로:
    NSError *error = nil;
    XYUser *user = [MTLJSONAdapter modelOfClass:XYUser.class    fromJSONDictionary:JSONDictionary error:&error]; 
    

    모델에서 JSONDictionary 데이터로
    NSDictionary *JSONDictionary = [MTLJSONAdapter JSONDictionaryFromModel:user];
    
    MTLJSONSerializing 사용법은 다음과 같습니다.
    @interface XYUser : MTLModel
    
    @property (readonly, nonatomic, copy) NSString *name;
    @property (readonly, nonatomic, strong) NSDate *createdAt;
    
    @property (readonly, nonatomic, assign, getter = isMeUser) BOOL meUser;
    @property (readonly, nonatomic, strong) XYHelper *helper;
    
    @end
    
    @implementation XYUser
    
    + (NSDictionary *)JSONKeyPathsByPropertyKey {
        return @{
            @"createdAt": @"created_at",
            @"meUser": NSNull.null
        };
    }
    
    - (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error {
        self = [super initWithDictionary:dictionaryValue error:error];
        if (self == nil) return nil;
    
        _helper = [XYHelper helperWithName:self.name createdAt:self.createdAt];
    
        return self;
    }
    

    @end
    되돌아오는 사전은 이 모델의 속성이 사전에서 어떻게 값을 얻는지 지정하는 데 사용된다. 예를 들어 + (NSDictionary *)JSONKeyPathsByPropertyKey; 는 사전에서 createdAt 필드를 찾고, 지정created_at 은 사전에서 값을 얻지 않는다는 것을 표시하며, 위에 열거된 모델 속성과 속성이 같은 사전의 자단(예를 들어 @"meUser": NSNull.null 은 사전에서 name 필드를 얻지 않는다.
    + JSONTransformerForKey: 사용 방법
    + (NSValueTransformer *)JSONTransformerForKey:(NSString *)key {
        if ([key isEqualToString:@"createdAt"]) {
            return [NSValueTransformer valueTransformerForName:XYDateValueTransformerName];
        }
    
        return nil;
    }
    

    위의 방법을 실현하여 속성이 사전 데이터에서 추출한 어떤 유형의 데이터를 지정하는 데 사용한다.예를 들어 위name 속성은 사전에서 값을 찾으면 createdAt 형식으로 자동으로 변환됩니다.
    만약 많은 유형이 지정해야 하는 값 유형이 있다면, 이것은 너무 우아하지 않습니다!Date는 더욱 우아한 방법을 제공했다. 유사Mantle을 실현하는 방법으로 어떤 속성이 사전에서 값을 얻은 후의 유형(또는 어떻게 값을 얻는지)을 지정한다.
    + (NSValueTransformer *)createdAtJSONTransformer {
        return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSString *str) {
            return [self.dateFormatter dateFromString:str];
        } reverseBlock:^(NSDate *date) {
            return [self.dateFormatter stringFromDate:date];
        }];
    }
    

    +classForParsingJSONDictionary: 사용 방법
    모델 그룹을 사용자 정의하면 +JSONTransformer 방법은 변환 +classForParsingJSONDictionary: 사전을 지정할 때 그것 deserializing 을 사용한다
    @interface XYMessage : MTLModel
    
    @end
    
    @interface XYTextMessage: XYMessage
    
    @property (readonly, nonatomic, copy) NSString *body;
    
    @end
    
    @interface XYPictureMessage : XYMessage
    
    @property (readonly, nonatomic, strong) NSURL *imageURL;
    
    @end
    
    @implementation XYMessage
    
    + (Class)classForParsingJSONDictionary:(NSDictionary *)JSONDictionary {
        if (JSONDictionary[@"image_url"] != nil) {
            return XYPictureMessage.class;
        }
    
        if (JSONDictionary[@"body"] != nil) {
            return XYTextMessage.class;
        }
    
        NSAssert(NO, @"No matching class for the JSON dictionary '%@'.",       JSONDictionary);
        return self;
    }
    

    @end Model class 당신이 전송한 사전 데이터에 따라 적합한 종류를 실례화할 것입니다.
    NSDictionary *textMessage = @{
        @"id": @1,
        @"body": @"Hello World!"
    };
    
    NSDictionary *pictureMessage = @{
        @"id": @2,
        @"image_url": @"http://example.com/lolcat.gif"
    };
    
    XYTextMessage *messageA = [MTLJSONAdapter modelOfClass:XYMessage.class  fromJSONDictionary:textMessage error:NULL];
    
    XYPictureMessage *messageB = [MTLJSONAdapter modelOfClass:XYMessage.class   fromJSONDictionary:pictureMessage error:NULL];
    
    MTLJSONAdapter 코드 관리:https://github.com/Mantle/Mantle
    부르자 6.0버전 사용Mantle 후: Mantle, 부르자 사용crash , Mantle crash crash 3%에 대한 더 많은 정보를 보시기 바랍니다.http://wangyangyang.gitcafe.com/2014/11/04/Mantle로 Model 레이어 구축/발표

    좋은 웹페이지 즐겨찾기