{"id":18103589,"url":"https://github.com/fcanas/ohmkit","last_synced_at":"2025-04-13T19:26:16.880Z","repository":{"id":9319552,"uuid":"11162614","full_name":"fcanas/OHMKit","owner":"fcanas","description":"Declarative mapping between JSON and Objective-C classes","archived":false,"fork":false,"pushed_at":"2023-03-15T23:19:58.000Z","size":185,"stargazers_count":20,"open_issues_count":2,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-06T17:12:50.623Z","etag":null,"topics":["api","ios","json","macos","mantle","objective-c","objective-c-library"],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fcanas.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2013-07-03T22:19:54.000Z","updated_at":"2022-07-27T01:29:11.000Z","dependencies_parsed_at":"2023-02-10T17:00:18.786Z","dependency_job_id":null,"html_url":"https://github.com/fcanas/OHMKit","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fcanas%2FOHMKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fcanas%2FOHMKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fcanas%2FOHMKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fcanas%2FOHMKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fcanas","download_url":"https://codeload.github.com/fcanas/OHMKit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248766934,"owners_count":21158349,"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":["api","ios","json","macos","mantle","objective-c","objective-c-library"],"created_at":"2024-10-31T22:12:38.423Z","updated_at":"2025-04-13T19:26:16.849Z","avatar_url":"https://github.com/fcanas.png","language":"Objective-C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OHMKit\n\n[![CI Status](http://img.shields.io/travis/fcanas/OHMKit.svg?style=flat)](https://travis-ci.org/fcanas/OHMKit)\n[![Version](https://img.shields.io/cocoapods/v/OHMKit.svg?style=flat)](http://cocoadocs.org/docsets/OHMKit)\n[![License](https://img.shields.io/cocoapods/l/OHMKit.svg?style=flat)](http://cocoadocs.org/docsets/OHMKit)\n[![Platform](https://img.shields.io/cocoapods/p/OHMKit.svg?style=flat)](http://cocoadocs.org/docsets/OHMKit)\n[![Coverage Status](https://coveralls.io/repos/fcanas/OHMKit/badge.svg?branch=master\u0026service=github)](https://coveralls.io/github/fcanas/OHMKit?branch=master)\n\nOHMKit makes it easy to hydrate Objective-C model objects from web services or local files. It works especially well with JSON. It's a lot like [Mantle](https://github.com/Mantle/Mantle) and [JSONModel](https://github.com/icanzilb/JSONModel) except that OHMKit doesn't require your models to inherit from a base class, making it more suitable for use with [Core Data](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/cdProgrammingGuide.html), [Parse](https://parse.com/), [Realm](http://realm.io/), or other libraries that _do_ require you to inherit from a base class.\n\nOHMKit is a system for declaratively expressing how to translate data from JSON or plist to native Objective-C model objects. OHMKit does it without requiring your model to inherit from a base class, so it works with NSObjects, NSManagedObjects, or anything else that fits with your class hierarchy. And you can specify custom mappings anywhere you want, not just in the model. So you can keep the details of mapping a service to you models out of your model code and in your service code where it may be more appropriate.\n\nFit this JSON\n\n```json\n{\n  \"name\": \"Fabian\",\n  \"favorite_word\":  \"absurd\",\n  \"favorite_number\": 47\n}\n```\n\ninto this object\n\n```objc\n@interface MYModel : NSObject\n@property (nonatomic, strong) NSString *name;\n@property (nonatomic, strong) NSString *favoriteWord;\n@property (nonatomic, assign) NSInteger favoriteNumber;\n@end\n```\n\nMap `user_name` from your web service to `userName` in your Objective-C models. Map a dictionary of numbers to a `UIColor`. Or hydrate a whole hierarchical JSON response, including arrays, dictionaries, and arbitrarily deep hierarchies of real Objective-C objects ... with a single line of code.\n\n## Why?\n\nOHMKit exists because [RestKit](https://github.com/RestKit/RestKit) (which is awesome by the way), is sometimes too big, heavy, and indirect. Because [Mantle](https://github.com/Mantle/Mantle) and [JSONModel](https://github.com/icanzilb/JSONModel) require your models to inherit from a base class.\n\nBecause sometimes, the web services your code consumes doesn't perfectly match your model objects.\n\nOHMKit is under 200 lines of well-tested code being leveraged in the app store now in apps used by millions of users.\n\n### What OHMKit is Not\n\nOHMKit doesn't know about networks. Use [AFNetworking](https://github.com/AFNetworking/AFNetworking).\n\nOHMKit doesn't know about routes. Use [SOCKit](https://github.com/jverkoey/sockit).\n\nOHMKit doesn't know about JSON. Use [NSJSONSerialization](https://developer.apple.com/library/ios/documentation/foundation/reference/nsjsonserialization_class/Reference/Reference.html)\n\nOHMKit doesn't know about CoreData. It will not manage graphs of entities for you quite like RestKit does. But OHMKit does not care about your model class' super class. So you can safely make subclasses of `NSManagedObject` mappable.\n\n## Usage\n\n### Basic Mapping\n\nGiven a model\n\n```objc\n@interface MYModel : NSObject\n@property (nonatomic, strong) NSString *name;\n@property (nonatomic, strong) NSString *favoriteWord;\n@property (nonatomic, assign) NSInteger favoriteNumber;\n@end\n```\n\nAnywhere in you application, make the model mappable, and assign it a dictionary of mappings from the keys a service will provide to the keys your actual model object uses. \n\n```objc\nOHMMappable([MYModel class]);\nOHMSetMapping([MYModel class], @{@\"favorite_word\"  : @\"favoriteWord\",\n                                 @\"favorite_number\": @\"favoriteNumber\");\n```\n\t\nAnd now _anywhere_ in your application, objects of the class `MYModel` can be hydrated with a dictionary from a service whose keys will be translated by the mapping dictionary you provided.\n\n```objc\nMYModel *testModel = [[MYModel alloc] init];\n\n[testModel setValuesForKeysWithDictionary:@{@\"name\"           : @\"Fabian\",\n                                            @\"favorite_word\"  : @\"absurd\",\n                                            @\"favorite_number\": @47];\n```\n\nLikewise, _anywhere_ in your application, objects of the class `MYModel` can be dehydrated to a dictionary for use with a service whose keys will be translated by the mapping dictionary you provided.\n\n```objc\nMYModel *testModel = [[MYModel alloc] init];\ntestModel.name = @\"Fabian\";\ntestModel.favoriteWord = @\"absurd\";\ntestModel.favoriteNumber = 47;\n\nNSDictionary *dictionary = [testModel dictionaryWithValuesForKeys:OHMMappableKeys([MYModel class])];\n```\n\n\n### Recursive Mapping\n\nRecursive mapping of mappable objects comes for free. If an object conforming to `\u003cOMMappable\u003e` has a property whose type also conforms to `\u003cOMMappable\u003e`, and the value for that key in the hydration dictionary is itself a dictionary, we'll instantiate a new model object and hydrate it. \n\n```objc\n@interface MYClass : NSObject\n@property (nonatomic, strong) NSString *name;\n@end\n\n@interface MYClass2 : NSObject\n@property (nonatomic, strong) NSString *name;\n@property (nonatomic, strong) NSString *favoriteWord;\n@property (nonatomic, assign) NSInteger favoriteNumber;\n@property (nonatomic, assign) MYClass *favoriteObject;\n@end\n\nOHMMappable([MYClass class]);\n\nOHMMappable([MYClass2 class])\nOHMSetMapping([MYClass2 class], @{@\"favorite_word\"  : @\"favoriteWord\", \n                                @\"favorite_number\": @\"favoriteNumber\", \n                                @\"favorite_object\" : @\"favoriteObject\"});\n\nMYModel *testModel = [[MYClass2 alloc] init];\n                             \nNSDictionary *class2Response = @{@\"name\"           : @\"Fabian\", \n                                 @\"favorite_word\"  : @\"absurd\", \n                                 @\"favorite_number\": @2, \n                                 @\"favorite_object\": @{@\"name\" : @\"Rock\"}};\n\n[testModel setValuesForKeysWithDictionary:class2Response];\n```\n\nNow, `testModel.favoriteObject` is an instance of `MYClass` hydrated with \"Rock\" as its name.\n\nInternally, the new model object is initialized with `[[ alloc] init]`, and then hydrated with `[ setValuesForKeysWithDictionary:dictionary]`. If you have a model that needs special consideration for initialization, use an adapter block.\n\n### Arrays\n\nArrays of dictionaries can be mapped to a class as well.\n\n```objc\n@interface Person : NSObject\n@property (nonatomic, copy) NSString *name;\n@end\n\n@interface Roster : NSObject\n@property (nonatomic, strong) NSArray *people;\n@end\n\nOHMMappable([Person class]);\nOHMSetArrayClasses([Roster class], @{@\"people\":[Person class]});\n\nNSDictionary *response = @{@[@{@\"name\":@\"Bert\"},\n                             @{@\"name\":@\"Ernie\"},\n                             @{@\"name\":@\"Count\"}];\n\nRoster *roster = [Roster new];\n[roster setValuesForKeysWithDictionary:response];\n```\n\n### Blocks serve as adapters to handle special properties\n\nUsers can pass a dictionary of blocks for field requiring special handling. Say a service sends back a dictionary that looks something like this:\n\n```json\n{\n    \"favorite_color\": [\n        122,\n        50,\n        80\n    ],\n    \"least_favorite_color\": [\n        121,\n        51,\n        81\n    ]\n}\n```\n\nand we expect to map it to a model like this\n\n```objc\n@interface MYModel : NSObject\n@property (nonatomic, strong) UIColor *favoriteColor;\n@property (nonatomic, strong) UIColor *leastFavoriteColor;\n@end\n```\n\nYou can adapt the response with an adapter block:\n\n```objc\nOHMMappable([MYModel class]);\nOHMSetMapping([MYModel class], @\"least_favorite_color\" : @\"leastFavoriteColor\", @\"favorite_color\" : @\"favoriteColor\")\nOHMValueAdapterBlock colorFromNumberArray = ^(NSArray *numberArray) {\n    return [UIColor colorWithRed:[numberArray[0] integerValue]/255.0\n                           green:[numberArray[1] integerValue]/255.0\n                            blue:[numberArray[2] integerValue]/255.0\n                           alpha:1];\n};\nOHMSetAdapter([MYModel class], @{@\"favoriteColor\": colorFromNumberArray, @\"leastFavoriteColor\": colorFromNumberArray});\n```\n\nWhen dehydrating, you can adapt the output with a reverse adapter block:\n\n```objc\nOHMMappable([MYModel class]);\nOHMSetMapping([MYModel class], @\"least_favorite_color\" : @\"leastFavoriteColor\", @\"favorite_color\" : @\"favoriteColor\")\nOHMValueAdapterBlock numberArrayFromColor = ^(NSColor *color) {\n\tCGFloat red, green, blue, alpha;\n\t[color getRed: \u0026red green: \u0026green blue: \u0026blue alpha: \u0026alpha];\n\treturn @[@(red), @(green), @(blue)];\n};\nOHMSetReverseAdapter([MYModel class], @{@\"favoriteColor\": numberArrayFromColor, @\"leastFavoriteColor\": colorFromNumberArray});\n```\n\nNote that the key for the adapter is the key on the model object, not on the response. And adapters are added for a property, not a type. If the above example had multiple properties that were colors, you would have to set an adapter block for each property. It would be smart to reuse adapter blocks in your code.\n\nThe `OHMValueAdapterBlock` type is a block that takes an `id` and returns an `id`. *i.e* `typedef id(^OHMValueAdapterBlock)(id);`\n\n\n## Using it in a project\n\nUse [CocoaPods](http://www.cocoapods.org), add OHMKit to your `PodFile`, and run `$ pod install`\n\n```ruby\npod 'OHMKit'\n```\n\n## How?\n\nOHMKit is a [mixin](http://en.wikipedia.org/wiki/Mixin) that makes it easy to keep any direct knowledge of the idiosyncrasies of the service you're consuming tucked away in a single place. \n\nIt leverages the power of Key Value Coding ([KVC](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/KeyValueCoding.html)) that's built right in to Cocoa. It safely wraps `-setValue:forKey:` and `-setValue:forUndefinedKey:` to make calls to `setValuesForKeysWithDictionary:` and `dictionaryWithValuesForKeys:` extremely powerful.\n\n## Contributing\n\nBug fixes, pull requests, enhancement requests and feedback are welcome. \n\nIf you plan on contributing code, please notice that OHMKit has tests. If you're fixing a bug, please include a test that exposes the bug and therefore guards against a regression.\n\n## TODO\n\n### Undefined Keys\n\nThe behavior of undefined keys should be configurable at 3 levels:\n\n1. Raise, because I should know about everything.\n2. Drop unrecognized keys. We don't need them, but we shouldn't crash.\n3. Add keys to a dictionary so that serialization/deserialization can be symmetric\n\nOption 2 is currently the only behavior, and I'm inclined to leave is as the default behavior.\n\n### NSCoding\n\nIt might be nice if we built a way to make a class `NSCoding` compatible if it's not already. I like [Mantle](https://github.com/github/Mantle), but I don't want to be told what my super class should be.\n\n### NSValueTransformer\n\nAdapter blocks versus `NSValueTransformer`s. There's no reason why both can't co-exist.\n\n# License\n\n```\nCopyright (c) 2013-2015 Fabian Canas. All rights reserved.\n\nThis code is distributed under the terms and conditions of the MIT license.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffcanas%2Fohmkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffcanas%2Fohmkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffcanas%2Fohmkit/lists"}