• Stars
    star
    1,124
  • Rank 41,395 (Top 0.9 %)
  • Language
    Objective-C
  • License
    MIT License
  • Created over 11 years ago
  • Updated almost 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Property mapping for Objective-C iOS apps.

CI Status codecov Version License Platform

Stop repeating your data parsing code in iOS apps.

Data parsing is one of most common tasks we need to do in our apps, yet still majority of people do this parsing by hand, always repeating the same code for each class they need to map.

Usual parsing requires this steps:

  • make sure you translate NSNull to nil and not crash
  • gracefully handle optional params
  • do type conversions
  • data format validation

Why Property Mapper?

There are libraries helping with that like Mantle, RESTKit and many more… But I wanted something that's self contained, easy to change / remove and requires minimal amount of code.

I've created Property Mapper as part of working on Foldify, a simple self contained solution that allows you to specify mapping between data you receive and data representation you have in your application... with some additional features, like type boxing, validation.

I don't like passing around JSON so I write parsing on top of native objects like NSDictionary/NSArray. If you get data as JSON just write a simple category that transforms JSON to native objects using NSJSONSerialization.

Example usage

Let's assume you have object like this:

@interface TestObject : NSObject
@property(nonatomic, strong) NSURL *contentURL;
@property(nonatomic, strong) NSURL *videoURL;
@property(nonatomic, strong) NSNumber *type;
@property(nonatomic, strong) NSString *title;
@property(nonatomic, strong) NSString *uniqueID;

@end

and you receive data from server in this format:

@{
  @"videoURL" : @"http://test.com/video.mp4",
	@"name" : @"Some Cool Video",
	@"videoType" : [NSNull null],
	@"sub_object" : @{
			@"title" : @616,
			@"arbitraryData" : @"data"
	}
}

this is the code you would write in your parsing code:

[KZPropertyMapper mapValuesFrom:dictionary toInstance:self usingMapping:@{
   @"videoURL" : KZBox(URL, contentURL),
     @"name" : KZProperty(title),
     @"videoType" : KZProperty(type),
     @"sub_object" : @{
         @"title" : KZProperty(uniqueID)
         }

  }];

Quite obvious what it does but in case you are confused, it will translate videoURL string to contentURL NSURL object, it will also grab title from sub_object and assign it to uniqueID. It also handles NSNull.

Advanced usage

Let's now change our mind and decide that we want our type property to be typedef enumeration, it's quite easy with KZPropertyMapper, change type mapping to following and add following method:

@"videoType" : KZCall(videoTypeFromString:, type),

//! implemented on instance you are parsing
- (id)videoTypeFromString:(NSString *)type
{
  if ([type isEqualToString:@"shortVideo"]) {
    return @(VideoTypeShort);
  }

  return @(VideoTypeLong);
}

If you need property name being passed while using selectors, just pass two-argument selector in KZCall. Second argument will be property name passed as NSString.

@"video" : KZCall(objectFromDictionary:forPropertyName:, type),

//! implemented on instance you are parsing
- (id)objectFromDictionary:(NSDictionary *)dictionary forPropertyName:(NSString *)propertyName
{
  Class objectClass = [self classForProperty:propertyName];
  id object = [objectClass new];
  [object updateFromDictionary:dictionary];
  return object;
}

Done. KVC should also take care of escaping NSNumber into int if your property uses primitive type. Same approach will work for sub-object instances or anything that you can assign to property.

Validations

You can also validate your server data before mapping it:

[KZPropertyMapper mapValuesFrom:dictionary toInstance:self usingMapping:@{
    @"videoURL" : KZBox(URL, contentURL).isRequired().min(10),
    @"name" : KZProperty(title).lengthRange(5, 12),
    @"videoType" : KZProperty(type),
    @"sub_object" : @{
      @"title" : KZProperty(uniqueID),
    },
  }];

Validators can be chained together, you can specify as many as you wish for each field, validation happens on source data before mapping happens.

If validation fails mapValues will return NO as a result, and you can use expanded method to get list of validation errors.

Any validation errors will prevent mapping, as data might be corrupted and we don't want partially updated data.

Built-in validations

Strings

  • isRequired
  • matchesRegEx
  • length
  • minLength
  • maxLength
  • lengthRange
  • oneOf
  • equalTo

Numbers

  • min
  • max
  • range

If you want more you can add validations as categories on KZPropertyDescriptor, check sample code to see how it's done, it's extremely simple.

Referencing arrays items

If your data comes to you in ordered array instead of dictionaries you can reference that as well:

sourceData = @{@"sub_object_array" : @[@"test", @123]}

@{@"sub_object_array" : @{@1 : KZProperty(uniqueID)}

This will grab first item from sub_object_array and assign it to uniqueID. It also works recursively.

Expanding boxing capabilities

You can expand boxing capabilities across whole application easily, just add category on KZPropertyMapper that implements methods like this:

+ (id)boxValueAsType:(id)value
{
	//! your boxing
}

Now you can use @Type mapping everywhere in your code.

Changelog

2.5.0

  • Added KZList that allows you to map single source property into multiple target ones.
  • Added type checking while parsing, if mismatched type is found a property will be ignored and a warning will be generated.
  • Fixed few minor bugs when working with unexpected server responses.
@{
  	@"videoURL" : KZList(
				KZBoxT(object, URL, contentURL),
				KZPropertyT(object, uniqueID),
				KZCallT(object, passthroughMethod:, type)
			)
}

Contributed by Marek Cirkos

2.0

  • New property syntax (old one still works) that allows you to get compile errors when you misspell property names.
  • Introduced concept of validators, chain-able.

Time to implement this changes was sponsored by The App Business.

Installing

  • Using CocoaPods: Add the following line to your Podfile:

    pod "KZPropertyMapper"

  • Using Carthage: Add the following to your Cartfile:

    github "krzysztofzablocki/KZPropertyMapper" "master"

  • Or just add the KZPropertyMapper/ folder to your project, making sure you enable ARC on these files.

Final note

Unit tests should serve as documentation. Default boxing types include @URL and @Date.

Follow me on twitter

More Repositories

1

Sourcery

Meta-programming for Swift, stop writing boilerplate code.
Swift
7,677
star
2

LifetimeTracker

Find retain cycles / memory leaks sooner.
Swift
3,144
star
3

Playgrounds

Better playgrounds that work both for Objective-C and Swift
Objective-C
2,633
star
4

Inject

Hot Reloading for Swift applications!
Swift
2,127
star
5

Swift-Macros

A curated list of awesome Swift Macros
Swift
2,120
star
6

Bootstrap

iOS project bootstrap aimed at high quality coding.
Objective-C
2,047
star
7

LineDrawing

Beatiful and fast smooth line drawing algorithm for iOS - as seen in Foldify.
Objective-C
1,288
star
8

Difference

Simple way to identify what is different between 2 instances of any type. Must have for TDD.
Swift
1,217
star
9

KZFileWatchers

A micro-framework for observing file changes, both local and remote. Helpful in building developer tools.
Swift
1,078
star
10

LinkedConsole

Clickable links in your Xcode console, so you never wonder which class logged the message.
Swift
931
star
11

Traits

Modify your native iOS app in real time.
Swift
904
star
12

IconOverlaying

Build informations on top of your app icon.
Shell
650
star
13

crafter

Crafter - Xcode project configuration CLI made easy.
Ruby
547
star
14

Strongify

Strongify is a 1-file µframework providing a nicer API for avoiding weak-strong dance.
Swift
444
star
15

KZNodes

Have you ever wonder how you could make Origami like editor in 1h ?
Objective-C
336
star
16

SFObservers

NSNotificationCenter and KVO auto removal of observers.
Objective-C
307
star
17

AutomaticSettings

Data driven settings UI generation.
Swift
302
star
18

CCNode-SFGestureRecognizers

Adding UIGestureRecognizers to cocos2d, painless.
Objective-C
202
star
19

DetailsMatter

Objective-C
199
star
20

Pinch-to-reveal

Pinch to reveal animation transition built with Layer masking, as seen in boeing app for iPad.
Objective-C
192
star
21

ViewModelOwners

Protocols that help make your MVVM setup more consistent
Swift
143
star
22

KZAsserts

Asserts on roids, test all your assumptions with ease.
Objective-C
100
star
23

OhSnap

Reproduce bugs your user saw by capturing and replaying data snapshots with ease.
Swift
89
star
24

Versionable

Migration for `Codable` objects.
Swift
83
star
25

SFContainerViewController

UIViewControllers containment predating Apple implementation. Works in both 4.x and 5.x iOS, no memory or hierarchy issues.
Objective-C
82
star
26

Swift-Observable

Native KVO like behaviour build in Swift.
Swift
64
star
27

BehavioursExample

Objective-C
58
star
28

SourceryWorkshops

Swift
45
star
29

Learn-iOS-GameDev-Level-0

Teeter clone accompanying tutorial at http://merowing.info/2013/04/learn-ios-game-dev-level-0/
Objective-C
24
star
30

XibReferencing

Simple category and sample showing how you can reference one Xib view from another
Objective-C
20
star
31

KZImageSplitView

Objective-C
17
star
32

NSObject-SFExecuteOnDealloc

A simple category on NSObject that allows you to execute block when object is deallocated
Objective-C
17
star
33

jenkins_jobs_to_statusboard

Ruby script that generates html table for embedding in StatusBoard by Panic http://panic.com/statusboard/
Ruby
15
star
34

SourceryPro-Feedback

Repository for discussing https://merowing.info/sourcery-pro/
12
star
35

krzysztofzablocki

2
star
36

krzysztofzablocki.github.io

Blog
HTML
2
star
37

starter-hugo-academic

Jupyter Notebook
1
star