{"id":1652,"url":"https://github.com/Wasappli/WANetworkRouting","last_synced_at":"2025-08-02T04:32:08.777Z","repository":{"id":56926166,"uuid":"52636183","full_name":"Wasappli/WANetworkRouting","owner":"Wasappli","description":"An iOS library to route API paths to objects on client side with request, mapping, routing and auth layers","archived":false,"fork":false,"pushed_at":"2017-08-30T14:00:48.000Z","size":106,"stargazers_count":10,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-15T04:48:20.407Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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.md","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-26T22:11:33.000Z","updated_at":"2022-11-20T15:01:01.000Z","dependencies_parsed_at":"2022-08-21T04:50:57.448Z","dependency_job_id":null,"html_url":"https://github.com/Wasappli/WANetworkRouting","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/Wasappli/WANetworkRouting","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wasappli%2FWANetworkRouting","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wasappli%2FWANetworkRouting/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wasappli%2FWANetworkRouting/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wasappli%2FWANetworkRouting/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Wasappli","download_url":"https://codeload.github.com/Wasappli/WANetworkRouting/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wasappli%2FWANetworkRouting/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268334615,"owners_count":24233793,"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","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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:52.365Z","updated_at":"2025-08-02T04:32:08.482Z","avatar_url":"https://github.com/Wasappli.png","language":"Objective-C","funding_links":[],"categories":["Networking"],"sub_categories":["Video","Other free courses"],"readme":"# WANetworkRouting\n\n[![Version](https://img.shields.io/cocoapods/v/WANetworkRouting.svg?style=flat)](http://cocoapods.org/pods/WANetworkRouting)\n[![License](https://img.shields.io/cocoapods/l/WANetworkRouting.svg?style=flat)](http://cocoapods.org/pods/WANetworkRouting)\n[![Platform](https://img.shields.io/cocoapods/p/WANetworkRouting.svg?style=flat)](http://cocoapods.org/pods/WANetworkRouting)\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 routing library to fetch objects from an API and map them to your app\n\n- [x] Highly customizable: network request, authentication and mapping layers are separate, you can create your own.\n- [x] Default network request layer built on top of AFNetworking 3.0\n- [x] Default mapping layer built on top of WAMapping\n- [x] Built-in object router\n- [x] Built-in batch manager\n- [x] Different configurations available\n- [x] `NSProgress` support\n- [x] Tested and used on real projects\n\nGo visit the [wiki](https://github.com/wasappli/WANetworkRouting/wiki) for more details about `WANetworkRouting` advanced use.\n\n`WANetworkRouting` is a library which turns `GET enterprises/:itemID` to `Enterprise : NSManagedObject` from a simple configuration step.\n\n## Install and use\n### Cocoapods\nUse Cocoapods, this is the easiest way to install the router.\n\n`pod 'WANetworkRouting'`\n\n`#import \u003cWANetworkRouting/WANetworkRouting.h\u003e` \n\n### `WANetworkRoutingManager`\n`WANetworkRoutingManager` is the core component of `WANetworkRouting`. It handles all the requests for you.\n\nThe initialization should contains at least a request manager. `WANetworkRouting` has a built in manager `WAAFNetworkingRequestManager` built on top of `AFNetworking 3.0`.\n\n```objc\n// Create a request manager\nWAAFNetworkingRequestManager *requestManager = [WAAFNetworkingRequestManager new];\n\n// Create the network routing manager \nWANetworkRoutingManager *routingManager = [WANetworkRoutingManager managerWithBaseURL:[NSURL URLWithString:@\"http://baseURL.com\"]\n                                                                       requestManager:requestManager\n                                                                       mappingManager:nil\n                                                                authenticationManager:nil];\n\n```\n\n** Note ** This `routingManager` will have not mapping layer nor authentication layer. This example demonstrates how easy it is to test your API before configuring the mapping.\n\nYou can then perform any operations among `GET|POST|PUT|PATCH|DELETE|HEAD`. For eg:\n\n- Fetch all the enterprises\n\n```objc\n[routingManager getObjectsAtPath:@\"enterprises\"\n                      parameters:nil\n                         success:^(WAObjectRequest *objectRequest, WAObjectResponse *response, NSArray *mappedObjects) {\n                             NSDictionary *json = response.responseObject;\n                             // Do something with the JSON\n                         }\n                         failure:^(WAObjectRequest *objectRequest, WAObjectResponse *response, id\u003cWANRErrorProtocol\u003e error) {\n                         }];\n```\n\n- Update an enterprise\n\n```objc\n[routingManager putObject:nil\n                     path:@\"enterprises/1\"\n               parameters:@{@\"key\": @\"newValue\"}\n                  success:^(WAObjectRequest *objectRequest, WAObjectResponse *response, NSArray *mappedObjects) {\n                  }\n                  failure:^(WAObjectRequest *objectRequest, WAObjectResponse *response, id\u003cWANRErrorProtocol\u003e error) {\n                  }];\n```\n\n### Add mapping layer\nThis is where things become to be interesting\n\nThe idea is to add a mapping layer so that your json gets mapped as objects. This is done by using a default mapping service built on top of [`WAMapping`](https://github.com/wasappli/WAMapping). I strongly encourage you to read the docs about how to configure the mapping.\n\nThere are 3 steps:\n\n- Configure the mapping,\n- Configure the response descriptor,\n- Optional: configure the request descriptor.\n\n#### Step 1: configure the mapping\n\nHave a look at [`WAMapping`](https://github.com/wasappli/WAMapping) for every details about the mapping:\n\n```objc\n// Specify a store to use between WAMemoryStore, WANSCodingStore, WACoreDataStore, your own store.\nWAMemoryStore *memoryStore = [[WAMemoryStore alloc] init];\n           \n// Create the mapping description for `Enterprise`\nWAEntityMapping *enterpriseMapping = [WAEntityMapping mappingForEntityName:@\"Enterprise\"];\nenterpriseMapping.identificationAttribute = @\"itemID\";\n[enterpriseMapping addAttributeMappingsFromDictionary:@{\n                                                        @\"id\": @\"itemID\",\n                                                        @\"name\": @\"name\",\n                                                        @\"address.street_number\": @\"streetNumber\"}];\n// Create the mapping manager\nWAMappingManager *mappingManager = [WAMappingManager mappingManagerWithStore:memoryStore];\n                                                                \nWAAFNetworkingRequestManager *requestManager = [WAAFNetworkingRequestManager new];\n\n// Create the network routing manager \nWANetworkRoutingManager *routingManager = [WANetworkRoutingManager managerWithBaseURL:[NSURL URLWithString:@\"http://baseURL.com\"]\n                                                                       requestManager:requestManager\n                                                                       mappingManager:mappingManager\n                                                                authenticationManager:nil];\n                                                                \n// Add a default date formatter on mapper\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[mappingManager addDefaultMappingBlock:toDateMappingBlock\n                   forDestinationClass:[NSDate class]];\n```\n\n#### Step 2: configure the response descriptors\nThis will map a path with a mapping. For example, when fetching `/enterprises`, you would get a JSON as\n\n```\n{\n\t\"array_of_enterprises\": [{\n\t\t\"name\": \"Enterprise 1\"\n\t}, {\n\t\t\"name\": \"Enterprise 2\"\n\t}]\n}\n```\n\nThe request descriptor would then be as follows:\n\n```objc\nWAResponseDescriptor *enterprisesResponseDescriptor =\n[WAResponseDescriptor responseDescriptorWithMapping:enterpriseMapping\n                                             method:WAObjectRequestMethodGET\n                                        pathPattern:@\"enterprises\" // The path on the URL\n                                            keyPath:@\"array_of_enterprises\"]; // The key path to access the enterprises on JSON\n```\n\nFor a `GET` or a `PUT` on an enterprise, which would be returned as\n\n```\n{\n\t\"name\": \"Enterprise 1\"\n}\n```\n\n```objc\nWAResponseDescriptor *singleEnterpriseResponseDescriptor =\n[WAResponseDescriptor responseDescriptorWithMapping:enterpriseMapping\n                                             method:WAObjectRequestMethodGET | WAObjectRequestMethodPUT // Specify multiple methods\n                                        pathPattern:@\"enterprises/:itemID\" // The path\n                                            keyPath:nil]; // The response would directly returns the object\n```\n\nPlease note the syntax `enterprises/:itemID`: `:` means that the value would be replaced dynamically knowning that `itemID` is the property name on client side.\n\nFinally, add the response descriptors to the mapping manager\n\n```objc\n[mappingManager addResponseDescriptor:enterprisesResponseDescriptor];\n[mappingManager addResponseDescriptor:singleEnterpriseResponseDescriptor];\n```\n\nYou can now use `WANetworkRouting` as this:\n\n```objc\n[routingManager getObjectsAtPath:@\"enterprises\"\n                      parameters:nil\n                         success:^(WAObjectRequest *objectRequest, WAObjectResponse *response, NSArray *mappedObjects) {\n                             NSArray\u003cEnterprise *\u003e *enterprises = mappedObjects;\n                             // Do something with the enterprises\n                         }\n                         failure:^(WAObjectRequest *objectRequest, WAObjectResponse *response, id\u003cWANRErrorProtocol\u003e error) {\n                         }];\n```\n\n#### Step 3: configure the request descriptor\nThis step is optional, but allows you to reverse map an object to send it to the server.\nRather than creating by yourself the parameters dictionary on a `POST` for example, you could write:\n\n```objc\nWARequestDescriptor *enterpriseRequestDescriptor =\n[WARequestDescriptor requestDescriptorWithMethod:WAObjectRequestMethodPOST\n                                     pathPattern:@\"enterprises\" // The path on the URL\n                                         mapping:enterpriseMapping\n                                  shouldMapBlock:nil // On optional block which let you configure rather you want to reverse map a relation ship or not\n                                  requestKeyPath:nil]; // The key path for the final dictionary\n                                  \n[mappingManager addRequestDescriptor:enterpriseRequestDescriptor];\n```\n\nPlease note that the `requestKeyPath` fits with how your object would be wrapped on the parameters. If nil, the dictionary is the object as dictionary.\n\n```\n{\n\t\"requestKeyPathValue\": {\n\t\t\"name\": \"Enterprise 1\"\n\t}\n}\n```\n\nThis allows you to post an object like this\n\n```objc\nEnterprise *enterprise = [Enterprise new];\nenterprise.name = @\"Test\";\n        \n[routingManager postObject:enterprise\n                      path:@\"enterprises\"\n                parameters:nil\n                   success:^(WAObjectRequest *objectRequest, WAObjectResponse *response, NSArray *mappedObjects) {\n                   }\n                   failure:^(WAObjectRequest *objectRequest, WAObjectResponse *response, id\u003cWANRErrorProtocol\u003e error) {\n                   }];\n```\n\nThis will reverse map the `enterprise` to a dictionary and send it as parameters on `POST enterprise`. All magically without anymore work!\n\n**Note**: If `enterprise` has no value for its `identificationAttribute` (see the mapping), the enterprise object would be automatically deleted from the store since the object returned by the server would be a new one with an `identificationAttribute`. Said in other words: you do not have to deal with potential duplicates.\n\n### Add routing layer\n\nWe just saw this call\n\n```objc\n[routingManager postObject:enterprise\n                      path:@\"enterprises\"\n                parameters:nil\n                   success:^(WAObjectRequest *objectRequest, WAObjectResponse *response, NSArray *mappedObjects) {\n                   }\n                   failure:^(WAObjectRequest *objectRequest, WAObjectResponse *response, id\u003cWANRErrorProtocol\u003e error) {\n                   }];\n```\n\nWouldn't it be great to write instead?\n\n```objc\n[routingManager postObject:enterprise\n                      path:nil // No value for path\n                parameters:nil\n                   success:^(WAObjectRequest *objectRequest, WAObjectResponse *response, NSArray *mappedObjects) {\n                   }\n                   failure:^(WAObjectRequest *objectRequest, WAObjectResponse *response, id\u003cWANRErrorProtocol\u003e error) {\n                   }];\n```\n\nHere comes the router!\n\n```objc\nWANetworkRoute *postEnterpriseRoute =\n[WANetworkRoute routeWithObjectClass:[Enterprise class]\n                         pathPattern:@\"enterprises\"\n                              method:WAObjectRequestMethodPOST];\n\n[routingManager.router postEnterpriseRoute];\n```\n\nEach time you are trying to `POST` an object of class `Enterprise`, it will supply the `enterprises` path for you.\n\nHere is an other example with `GET` and `PUT` routes:\n\n```objc\nWANetworkRoute *enterpriseRoute =\n[WANetworkRoute routeWithObjectClass:[Enterprise class]\n                         pathPattern:@\"enterprises/:itemID\"\n                              method:WAObjectRequestMethodGET | WAObjectRequestMethodPUT];\n\n[routingManager.router addRoute:enterpriseRoute];\n```\n\n### Add authentication layer\n\nThis layer is important if your API has some authentication. Basically you should have a login/signup endpoint which returns some kind of token that you can renew if the requests gets an error like `401: token expired`.\nBy implementing the simple `WARequestAuthenticationManagerProtocol` protocol, and passing an instance of your class to the router manager, it will:\n\n- Ask to authenticate the `NSMutableURLRequest`. Should be a token on `Authorization` HTTP header field.\n- Ask if a request should be replayed: you received `401: token expired` for example then yes.\n- Ask you to authenticate (renew the authorization somehow) and replay the request (`[routingManager enqueueRequest:]`) (The request will automatically be re authorized for you).\n\nThis will allow the routing manager to run every requests without any surprise when authentication has expired!\n\n### Add batch \nThe batch managers answers two needs:\n\n1/ Let's say that you want to minimize the impact on server of having multiple calls to perform at the same time. Like `GET /me`, `GET /meetings`, `GET /configuration`\n2/ Have offline support, for example add notes to a meeting and automatically sync them when you are back online!\n\nWell, `WABatchManager` is there for you\n\n#### Step 1: create a batch api\nThis library comes with a built-in batch manager from the following specifications. You can either create an API which conforms to the specification, or create a new batch manager from `WABatchManager` which fits your needs.\n\nThe specifications: (this is  closely following the batch api requests by facebook [Facebook batch API](https://developers.facebook.com/docs/graph-api/making-multiple-requests))\n\n- Set a limit per session\n- The data sent has the following format\n\n```\n{\n\t\"batch\": [{\n\t\t\"uri\": \"\\/meetings\\/Ufd8f4e\\/notes\",\n\t\t\"method\": \"POST\",\n\t\t\"body\": \"{\\\"note\\\":{\\\"hash\\\":\\\"817ebd76b6a89eff47bbd8e53f633c93eeadf23e\\\",\\\"title\\\":\\\"New first note\\\",\\\"type\\\":\\\"item\\\",\\\"position\\\":0}}\",\n\t\t\"headers\": {\n          \"Content-Type\": \"application/json\"\n        }\n\t}, {\n\t\t\"uri\": \"\\/meetings\\/Ufd8f4e\\/notes\",\n\t\t\"method\": \"POST\",\n\t\t\"body\": \"{\\\"note\\\":{\\\"hash\\\":\\\"f4e893ab9a754c273707b264359e7ec12262959d\\\",\\\"title\\\":\\\"New second note\\\",\\\"type\\\":\\\"note\\\",\\\"position\\\":1}}\"\n\t}]\n}\n```\nPlease note that `uri` is relative to the base URL and `body` is an encoded string\n\n- Respond with\n\n```\n[\n  {\n    \"body\": \"[{\\\"note\\\":\\\"...\\\"}]\n    \"code\": 200\n  },\n  {\n    \"body\": \"[{\\\"note\\\":\\\"...\\\"}]\n    \"code\": 200\n  }\n ]\n ```\n\n#### Step 2: create a batch manager\n\n```objc\nWABatchManager *batchManager = [[WABatchManager alloc] initWithBatchPath:@\"/batch\" limit:20];\n\nroutingManager = [WANetworkRoutingManager managerWithBaseURL:[NSURL URLWithString:kBaseURL]\n                                              requestManager:requestManager\n                                              mappingManager:mappingManager\n                                       authenticationManager:authManager\n                                                batchManager:batchManager];\n```\n\n#### Manually batch requests\n\n```objc\n// Create a batch session\nWABatchSession *batchSession = [WABatchSession new];\n\n// Enqueue the requests you want to perform\n[batchSession addRequest:[WAObjectRequest requestWithHTTPMethod:WAObjectRequestMethodGET\n                                                           path:@\"/me\"\n                                                     parameters:nil\n                                                optionalHeaders:nil]];\n[batchSession addRequest:[WAObjectRequest requestWithHTTPMethod:WAObjectRequestMethodGET\n                                                           path:@\"/meetings\"\n                                                     parameters:nil\n                                                optionalHeaders:nil]];\n[batchSession addRequest:[WAObjectRequest requestWithHTTPMethod:WAObjectRequestMethodGET\n                                                           path:@\"/configuration\"\n                                                     parameters:nil\n                                                optionalHeaders:nil]];\n                                                \n// Send the session\n[batchManager sendBatchSession:batchSession\n                  successBlock:^(id\u003cWABatchManagerProtocol\u003e batchManager, NSArray\u003cWABatchResponse *\u003e *batchResponses) {\n                      \n                  }\n                  failureBlock:^(id\u003cWABatchManagerProtocol\u003e batchManager, WAObjectRequest *request, WAObjectResponse *response, id\u003cWANRErrorProtocol\u003e error) {\n                      \n                  }];\n```\n\n#### Add an offline mode\nThe offline mode works out of the box.\nBasically, you register routes describing requests which can be enqueued if there is a network issue (you should not include `GET`) and you let the library do its job! It also integrates with mapping and authentication without any further step.\n\n- Create routes to describe the requests to be enqueued\n\n```objc\n// Meetings\nWANetworkRoute *modifyMeeting = [WANetworkRoute routeWithObjectClass:nil\n                                                         pathPattern:@\"meetings/:itemID\"\n                                                              method:WAObjectRequestMethodPUT | WAObjectRequestMethodeDELETE];\n\n// Action items\nWANetworkRoute *postActionItem = [WANetworkRoute routeWithObjectClass:nil\n                                                          pathPattern:@\"meetings/:itemID/notes\"\n                                                               method:WAObjectRequestMethodPOST];\n```\n- Add them to the batch manager\n\n```objc\n[batchManager addRouteToBatchIfOffline:modifyMeeting];\n[batchManager addRouteToBatchIfOffline:postActionItem];\n```\n\n- Answers to errors\n\n```objc\n[self.apiManager putObject:meeting\n                      path:nil\n                parameters:nil\n                   success:...\n                   failure:^(WAObjectRequest *objectRequest, WAObjectResponse *response, id\u003cWANRErrorProtocol\u003e error) {\n                       SLDMeeting *meetingReturned = nil;\n                       // Check if the error is a batch one\n                       if ([[error.originalError.domain isEqualToString:WANetworkRoutingManagerErrorDomain] \u0026\u0026 error.originalError.code == WANetworkRoutingManagerErrorRequestBatched) {\n                           // Return the meeting for any UI update\n                           meetingReturned = meeting;\n                           // Store a \"has offline changes\" flag\n                           meeting.hasOfflineModifications = @YES;\n                           // Save the store\n                           [store save];\n                       }\n                       \n                       if (completion) {\n                           completion(meetingReturned, error);\n                       }\n                   }];\n```\n\n- Wait for flush. The library automatically flush the offline changes on any request made while online. You can also manually trigger the flush with \n\n```objc\nif ([batchManager needsFlushing]) {\n  [batchManager flushDataWithCompletion:completion];\n}\n```\n\n- Perform actions post flush\n\n```objc\n[batchManager setOfflineFlushSuccessBlock:^(id \u003cWABatchManagerProtocol\u003e batchManager, NSArray\u003cWABatchResponse *\u003e *batchResponses) {\n    if (![batchManager needsFlushing]) {\n        // Consider all meetings synchronized. You could also iterate trough batch responses\n        NSArray *notSyncedMeetings = [SLDMeeting findAll];\n        for (SLDMeeting *meeting in notSyncedMeetings) {\n            meeting.hasOfflineModifications = @NO;\n        }\n        \n        [store save];\n    }\n}];\n```\n\n## Progress\n\nYou can track the progress of your request. This library uses `NSProgress` class which is a great tool for dealing with progress.\nIf your app supports iOS 9+, there is the explicit child feature. You can then write:\n\n(Please note that we are using a subclass of `NSProgress` that you can find on the sample. This is because `NSProgress` does not allow you to check if a progress already has a child :/)\n\n```objc\nWAProgress *mainProgress = [[WAProgress alloc] initWithParent:nil userInfo:nil];\nmainProgress.totalUnitCount = 100; // 100%\n\n// Add an observer to track the fraction completed\n[mainProgress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNew context:nil];\n\n                                     }];\n\n[routingManager getObjectsAtPath:@\"posts\"\n                      parameters:nil\n                        progress:^(WAObjectRequest *objectRequest, NSProgress *uploadProgress, NSProgress *downloadProgress, NSProgress *mappingProgress) {\n                            // Add the progress as a child\n                            [mainProgress addChildOnce:downloadProgress withPendingUnitCount:80]; // Network counts as 80% of the time\n                            [mainProgress addChildOnce:mappingProgress withPendingUnitCount:20]; // Mapping counts as 20% of the time. This is arbitrary\n                        }\n                         success:^(WAObjectRequest *objectRequest, WAObjectResponse *response, NSArray *mappedObjects) {\n                             self.posts = mappedObjects;\n                             [self.tableView reloadData];\n                             \n                             // Remove the observer!\n                             [mainProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];\n                         }\n                         failure:^(WAObjectRequest *objectRequest, WAObjectResponse *response, id\u003cWANRErrorProtocol\u003e error) {\n                             [mainProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];\n                         }];\n\n...\n\n- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary\u003cNSString *,id\u003e *)change context:(void *)context {\n    if ([object isKindOfClass:[NSProgress class]]) {\n        NSLog(@\"New progress is %f\", [change[NSKeyValueChangeNewKey] floatValue]);\n    } else {\n        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];\n    }\n}\n\n```\n\t\nIf your app targets less than iOS 9, I suggest you to calculate your own progress, this is simple since it is synchronous.\n\n## Cancellation\n\nBecause of `NSProgress`, you can easily cancel the request:\nYou have to call `[downloadProgress cancel]` and `[mappingProgress cancel]` and it will cancel either the mapping or the server fetch task depending on where you are on the process.\n\n## Errors\n\nEach api has a way to describe errors details others than the http codes (404, 403, 401, ...). For example, you could have a response like:\n\n```\n{\n\t\"error\": {\n\t\t\"error_code\": 4,\n\t\t\"error_description\": \"The token is expired\"\n\t}\n}\n```\n\nThe `WANetworkRouting` allows you to customize the error handling and retrieve both code and error description. Let's see how:\nIt's an other protocol (again): `WANRErrorProtocol`\n\n### Create your own class\nThe routing manager comes with a default error class which does nothing except being allocated. \n\n```objc\n@interface MyAPIError : NSObject \u003cWANRErrorProtocol\u003e\n@end\n\n@implementation MyAPIError \n\n- (instancetype)initWithOriginalError:(NSError *)error response:(WAObjectResponse *)response {\n    self = [super init];\n    \n    if (self) {\n        self-\u003e_originalError = error;\n        self-\u003e_response      = response;\n        self-\u003e_finalError    = error;\n        \n        NSDictionary *errorDescription = response.responseObject[@\"error\"];\n        NSInteger errorCode = [errorDescription[@\"error_code\"] integerValue];\n        NSString *errorDesc = errorDescription[@\"error_description\"];\n        \n        self-\u003e_finalError = [NSError errorWithDomain:MyDomain\n                                                code:errorCode\n                                            userInfo:@{\n                                                       NSLocalizedDescriptionKey: errorDesc\n                                                       }];\n    }\n    \n    return self;\n}\n\n@end\n\n```\n\n### Register the class\n\n```objc\nWAAFNetworkingRequestManager *requestManager = [WAAFNetworkingRequestManager new];\nrequestManager.errorClass = [MyAPIError class];\n```\n\n### Use the class\n\n```objc\n[routingManager postObject:enterprise\n                      path:nil\n                parameters:nil\n                   success:^(WAObjectRequest *objectRequest, WAObjectResponse *response, NSArray *mappedObjects) {\n                   }\n                   failure:^(WAObjectRequest *objectRequest, WAObjectResponse *response, MyAPIError *error) {\n                   \t\tif (error.finalError.errorCode == 4) {\n                   \t\t\t// Token has expired :/\n                   \t\t}\n                   }];\n```\n\n## Delete a ressource\n\nA quick word about deletion:\n\nIf you write\n```objc\n[routingManager deleteObject:enterprise\n                        path:nil\n                  parameters:nil\n                     success:^(WAObjectRequest *objectRequest, WAObjectResponse *response, NSArray *mappedObjects) {\n                   }\n                     failure:^(WAObjectRequest *objectRequest, WAObjectResponse *response, MyAPIError *error) {\n                   }];\n```\n\nThis will automatically delete enterprise from the store on success! Be careful that if you delete an object only from it's ressource, it's up to you to delete it from your store (`[routingManager deleteObject:nil path:@\"enterprises/1\"...]`).\n\n## Get the HTTP code and header fields from the response\n\n```objc\n// WAObjectResponse *response\n\nNSInteger responseStatusCode = response.urlResponse.statusCode;\nNSDictionary *httpHeaderFields = response.urlResponse.httpHeaderFields;\n```\n\n## Inspiration\nLet's be honest, you'll find some things which looks like [Restkit](https://github.com/RestKit/RestKit).\nI used RestKit for a while on a very big projects, but it tends to be too much magic and hard to maintain for our need which remains simple.\nFor example: upgrading `AFNetworking` on RestKit is just... Well, something we wait for months!\n\nBy compartimenting the layers, I hope to fix this issue!\n\n#Contributing : Problems, Suggestions, Pull Requests?\n\nPlease open a new Issue [here](https://github.com/Wasappli/WANetworkRouting/issues) if you run into a problem specific to WANetworkRouting.\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.\nPlease also run the tests before ;)\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%2FWANetworkRouting","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FWasappli%2FWANetworkRouting","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FWasappli%2FWANetworkRouting/lists"}