{"id":1734,"url":"https://github.com/Wasappli/WAMapping","last_synced_at":"2025-08-02T04:32:27.005Z","repository":{"id":56926165,"uuid":"52362151","full_name":"Wasappli/WAMapping","owner":"Wasappli","description":"A library to turn dictionary into object and vice versa for iOS. Designed for speed!","archived":false,"fork":false,"pushed_at":"2017-08-30T13:44:04.000Z","size":82,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-03T09:17:52.043Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Wasappli.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-02-23T13:48:45.000Z","updated_at":"2022-11-20T15:01:01.000Z","dependencies_parsed_at":"2022-08-20T22:50:28.064Z","dependency_job_id":null,"html_url":"https://github.com/Wasappli/WAMapping","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wasappli%2FWAMapping","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wasappli%2FWAMapping/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wasappli%2FWAMapping/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wasappli%2FWAMapping/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Wasappli","download_url":"https://codeload.github.com/Wasappli/WAMapping/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228439110,"owners_count":17920018,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-01-05T20:15:54.529Z","updated_at":"2024-12-06T08:31:32.145Z","avatar_url":"https://github.com/Wasappli.png","language":"Objective-C","funding_links":[],"categories":["Parsing"],"sub_categories":["JSON","Other free courses"],"readme":"# WAMapping\n\n[![Version](https://img.shields.io/cocoapods/v/WAMapping.svg?style=flat)](http://cocoapods.org/pods/WAMapping)\n[![License](https://img.shields.io/cocoapods/l/WAMapping.svg?style=flat)](http://cocoapods.org/pods/WAMapping)\n[![Platform](https://img.shields.io/cocoapods/p/WAMapping.svg?style=flat)](http://cocoapods.org/pods/WAMapping)\n\n**Developed and Maintained by [ipodishima](https://github.com/ipodishima) Founder \u0026 CTO at [Wasappli Inc](http://wasapp.li).**\n\n**Sponsored by [Wisembly](http://wisembly.com/en/)**\n\nA fast mapper from JSON to `NSObject`\n\n- [x] Fast\n- [x] Simple to write \u0026 read\n- [x] Saves you many hours\n- [x] Supports both JSON \u003c-\u003e `NSObject`\n- [x] Designed for customisation\n- [x] Built-in CoreData, NSCoding and Memory stores\n- [x] Built-in insert or update object\n- [x] Tested\n\nGo visit the [wiki](https://github.com/wasappli/WAMapping/wiki) for more details about `WAMapping` advanced use.\n\nWAMapping is a library for iOS to turns dictionaries into objects and objects to dictionary. It's aim is to simplify the boilerplate of manually parsing the data and assigning values to an object. It's even more difficult when it comes to using it with CoreData because of the insert or update. And I do not mention performances involved. WAMapping solves this for you!\n\n## Install and use\n### Cocoapods\nUse Cocoapods, this is the easiest way to install the mapper.\n\n`pod 'WAMapping'`\n\n`#import \u003cWAMapping/WAMapping.h\u003e`\n\n### Setup mapping\n\nOn a classical use, the `source` is known as the response from a server turned into a dictionary and the `destination` is the destination object to apply the values, for example an `NSManagedObject`.\n\nLet's assume the `Enterprise` class as follows:\n\n```objc\n@interface Enterprise : NSObject\n\n@property (nonatomic, strong) NSNumber *itemID;\n@property (nonatomic, strong) NSString *name;\n@property (nonatomic, strong) NSDate *creationDate;\n@property (nonatomic, strong) NSNumber *streetNumber;\n@property (nonatomic, strong) NSArray *employees; // Can be mutable, or an `NSSet` or an `NSOrderedSet`\n@property (nonatomic, strong) NSArray *chiefs;\n\n@end\n```\n\nThe `itemID` ids your object on the store. This is not required but recommended to avoid creating duplicates.\n\nAssuming a json\n```\n{\n    \"id\": 1,\n    \"name\": \"Wasappli\",\n    \"creation_date\": \"2013-10-01\",\n    \"address\": {\n        \"street_number\": 5149\n    }\n}\n```\n\nThe mapping would looks like\n\n```objc\nWAEntityMapping *enterpriseMapping = [WAEntityMapping mappingForEntityName:@\"Enterprise\"];\nenterpriseMapping.identificationAttribute = @\"itemID\";\n\n// Add the classic attributes\n[enterpriseMapping addAttributeMappingsFromDictionary:@{\n                                                        @\"id\": @\"itemID\",\n                                                        @\"name\": @\"name\",\n                                                        @\"address.street_number\": @\"streetNumber\"\n                                                        }];      \n                                                   \n// Map custom values. Here an `NSDate` from a string using an `NSDateTransformer`\n[enterpriseMapping addMappingFromSourceProperty:@\"creation_date\"\n                          toDestinationProperty:@\"creationDate\"\n                                      withBlock:^id(id value) {\n                                          return [dateFormatter dateFromString:value];\n                                      }\n                                   reverseBlock:^id(id value) {\n                                       return [dateFormatter stringFromDate:value];\n                                   }];\n\n// Register the mapping for future use\nWAMappingRegistrar *registrar = [WAMAppingRegistrar new];\n[registrar registerMapping:enterpriseMapping];\n// [registrar registerMapping:employeeMapping];\n// WAEntityMapping *savedEnterpriseMapping = [registrar mappingForEntityName:@\"Enterprise\"];\n```\n\nAnd that's it...!\n\n### Use the mapper\n\nFirst, create a store. This is a required step.\nI'm providing three stores on this repo:\n\n- `WAMemoryStore` which relies on a simple `NSMutableSet`,\n- `WANSCodingStore` which saves your objects using `NSCoding` protocol,\n- `WACoreDataStore` which makes use of `CoreData`.\n\nYou can easily create your own store is you want to use SQLite for example, go checkout the wiki.\n\n```objc\nWAMemoryStore *store = [[WAMemoryStore alloc] init];\n\n// or\n// WACoreDataStore *store = [[WACoreDataStore alloc] initWithManagedObjectContext:localContext];\n\n// or\n// WANSCodingStore *store = [[WANSCodingStore alloc] initWithArchivePath:archivePath];\n```\n\nThen, allocate a mapper with the store\n\n```objc\nWAMapper *mapper = [[WAMapper alloc] initWithStore:store];\n```\n\nFinally, map the dictionary representation to the object:\n\n```objc\n[mapper mapFromRepresentation:json\n                      mapping:enterpriseMapping\n                   completion:^(NSArray *mappedObjects) {\n                       firstEnterprise = [mappedObjects firstObject];\n                   }];\n```\n\nAnd voilà!\n\n### Add relation ships\n\n`WAMapping` also supports relationships:\n\n- classics:\n\n```\n{\n    \"id\": 1,\n    \"first_name\": \"Marian\",\n    \"enterprise\": {\n        \"id\": 1,\n        \"name\": \"Wasappli\",\n        \"creation_date\": \"2013-10-01\",\n        \"address\": {\n            \"street_number\": 5149\n        }\n    }\n}\n```\n\n```objc\nWARelationshipMapping *enterpriseRelationship = \n[WARelationshipMapping relationshipMappingFromSourceProperty:@\"enterprise\" toDestinationProperty:@\"enterprise\" withMapping:enterpriseMapping];\n[employeeMapping addRelationshipMapping:enterpriseRelationship];\n```\n\n- With identification attribute only\n\n```\n{\n    \"enterprise\": {\n        \"id\": 1,\n        \"name\": \"Wasappli\",\n        \"creation_date\": \"2013-10-01\",\n        \"address\": {\n            \"street_number\": 5149\n        },\n        \"chiefs\": 1 # Could also be [1, 2, 3] \n    },\n    \"employees\": [{\n                  \"id\": 1,\n                  \"first_name\": \"Marian\"\n                  }]\n}\n```\n\n```objc\nWARelationshipMapping *chiefsRelationship = [WARelationshipMapping relationshipMappingFromSourceIdentificationAttribute:@\"chiefs\" toDestinationProperty:@\"chiefs\" withMapping:employeeMapping];\n[enterpriseMapping addRelationshipMapping:chiefsRelationship];\n```\n\n## Reverse mapper\nA reverse mapper is also packaged with this library. It supports the reverse transformation from an object to a dictionary.\n\n```objc\nWAReverseMapper *reverseMapper = [[WAReverseMapper alloc] init];\n\njson = [reverseMapper reverseMapObjects:enterprises\n                            fromMapping:enterpriseMapping\n                  shouldMapRelationship:nil];\n```\n\n# Default mappings\nIf you have a server which returns all dates within the same format, then you can ask the mapper or the reverse mapper once to transform the value.\n\nInstead of writing\n\n```objc\n[enterpriseMapping addAttributeMappingsFromDictionary:@{\n                                                        @\"id\": @\"itemID\",\n                                                        @\"name\": @\"name\",\n                                                        @\"address.street_number\": @\"streetNumber\"\n                                                        }];      \n                                                   \n// Map custom values. Here an `NSDate` from a string using an `NSDateTransformer`\n[enterpriseMapping addMappingFromSourceProperty:@\"creation_date\"\n                          toDestinationProperty:@\"creationDate\"\n                                      withBlock:^id(id value) {\n                                          return [dateFormatter dateFromString:value];\n                                      }\n                                   reverseBlock:^id(id value) {\n                                       return [dateFormatter stringFromDate:value];\n                                   }];\n```\n\nYou would write\n\n```objc\n[enterpriseMapping addAttributeMappingsFromDictionary:@{\n                                                        @\"id\": @\"itemID\",\n                                                        @\"name\": @\"name\",\n                                                        @\"address.street_number\": @\"streetNumber\",\n                                                        @\"creation_date\": @\"creationDate\"\n                                                        }];\n\n\nid(^toDateMappingBlock)(id ) = ^id(id value) {\n    if ([value isKindOfClass:[NSString class]]) {\n        return [dateFormatter dateFromString:value];\n    }\n    \n    return value;\n};\n\n[mapper addDefaultMappingBlock:toDateMappingBlock\n           forDestinationClass:[NSDate class]];\n```\n\nThe same thing happens to the reverse mapper. Note that if you provide a custom mapping on an `NSDate` object for a specific property (like a date with only the year), you can add the property to the entity mapping which will override the default behavior for this specific property.\n\n# Progress and cancellation\nBoth `WAMapper` and `WAReverseMapper` support `NSProgress`. Note that Apple explicitely says in their documentation about `NSProgressReporting` (which we are mimicing here) `Objects that adopt this protocol should typically be \"one-shot\"` which means you should use one `WAMapper` per map operation.\n\n## Progress\nYou can track the progress using this little piece of code. Note that the progress counts the main top objects mapped (if your array contains one object with a thousand objects as relationship, the progress will not reflect the thousand subobjects mapped). This is per choice because adopting child progress prior to iOS 9 is not great.\n\n```objc\n[mapper.progress addObserver:self\n                  forKeyPath:NSStringFromSelector(@selector(fractionCompleted))\n                     options:NSKeyValueObservingOptionNew\n                     context:NULL];\n```\n\n```objc\n- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary\u003cNSString *,id\u003e *)change context:(void *)context {\n    if ([keyPath isEqualToString:NSStringFromSelector(@selector(fractionCompleted))] \u0026\u0026 [object isKindOfClass:[NSProgress class]]) {\n        NSLog(@\"Mapping progress = %f\", [change[@\"new\"] doubleValue]);\n    } else {\n        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];\n    }\n}\n```\n\n## Cancellation\nYou can cancel the mapping or the reverse mapping using this piece of code. Note that for cancellation to happen, you have to call the mapping from an other thread!\n\n```objc\ndispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{\n    [mapper mapFromRepresentation:JSON mapping:employeeMapping completion:^(NSArray *mappedObjects, NSError *error) {\n        NSLog(@\"Mapped objects %@ - Error %@\", mappedObjects, error);\n    }];\n});\n\n[mapper.progress cancel];\n```\n\n# Side notes\n## TODOs\n\n- [ ] Benchmark against popular mappers\n- [ ] Add more tests for keypath handling, especially on relationship\n\n## Inspiration\nYou'll find inspiration from [Restkit](https://github.com/RestKit/RestKit) and [FastEasyMapping](https://github.com/Yalantis/FastEasyMapping). These are both libraries I used on projects but with issues\n\n#Contributing : Problems, Suggestions, Pull Requests?\n\nPlease open a new Issue [here](https://github.com/Wasappli/WAMapping/issues) if you run into a problem specific to WAAppRouting.\n\nFor new features pull requests are encouraged and greatly appreciated! Please try to maintain consistency with the existing code style. If you're considering taking on significant changes or additions to the project, please ask me before by opening a new Issue to have a chance for a merge.\n\n#That's all folks !\n\n- If your are happy don't hesitate to send me a tweet [@ipodishima](http://twitter.com/ipodishima)!\n- Distributed under MIT licence.\n- Follow Wasappli on [facebook](https://www.facebook.com/wasappli)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FWasappli%2FWAMapping","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FWasappli%2FWAMapping","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FWasappli%2FWAMapping/lists"}