{"id":13906956,"url":"https://github.com/twitter/ios-twitter-network-layer","last_synced_at":"2025-07-09T22:43:14.726Z","repository":{"id":50698987,"uuid":"136392951","full_name":"twitter/ios-twitter-network-layer","owner":"twitter","description":"Twitter Network Layer is a scalable and feature rich network layer built on top of NSURLSession for Apple platforms","archived":false,"fork":false,"pushed_at":"2021-10-21T17:28:27.000Z","size":686,"stargazers_count":573,"open_issues_count":2,"forks_count":38,"subscribers_count":24,"default_branch":"master","last_synced_at":"2024-11-25T15:52:14.689Z","etag":null,"topics":["framework","http","ios","networking","nsurlsession","twitter"],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/twitter.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":"2018-06-06T22:30:51.000Z","updated_at":"2024-11-04T23:52:15.000Z","dependencies_parsed_at":"2022-09-24T13:43:29.109Z","dependency_job_id":null,"html_url":"https://github.com/twitter/ios-twitter-network-layer","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/twitter/ios-twitter-network-layer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twitter%2Fios-twitter-network-layer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twitter%2Fios-twitter-network-layer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twitter%2Fios-twitter-network-layer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twitter%2Fios-twitter-network-layer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/twitter","download_url":"https://codeload.github.com/twitter/ios-twitter-network-layer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twitter%2Fios-twitter-network-layer/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264504616,"owners_count":23618831,"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":["framework","http","ios","networking","nsurlsession","twitter"],"created_at":"2024-08-06T23:01:45.447Z","updated_at":"2025-07-09T22:43:14.703Z","avatar_url":"https://github.com/twitter.png","language":"Objective-C","readme":"# Twitter Network Layer (a.k.a TNL)\n\nThe __Twitter Network Layer__ (__TNL__) is a framework for interfacing with the __Apple__ provided\n`NSURLSession` stack that provides additional levels of control and insight over networking requests,\nprovides simple configurability and minimizes the cognitive load necessary to maintain a robust and\nwide-reaching networking system.\n\n## OSI Layering with TNL\n\nThe __Twitter Network Layer__ sits on top of the _connection/session layer_ provided by the\n__Apple NSURL__ framework.  Those frameworks are build on top of the __HTTP/1.1__ and __HTTP/2__.\nThe layer chart appears like this:\n\n```\n/--------------------------------------\\\n|                                      |\n|              User Layer              |\n|       The actual user (Layer 8)      |\n|                                      |\n|--------------------------------------|\n|                                      |\n|          Application Layer           |\n|       MVC, MVVM, etc (Layer 7e)      |\n|                                      |\n|--------------------------------------|\n|                                      |\n|     Concrete Operation/App Layer     |  \u003c------ Operations, Requests \u0026\n|             TNL (Layer 7d)           |          Responses built on TNL\n|                                      |\n|--------------------------------------|\n|                                      |\n|     Abstract Operation/App Layer     |\n|             TNL (Layer 7c)           |  \u003c------ TNL\n|                                      |\n|--------------------------------------|\n|                                      |\n|         Connection/App Layer         |\n|        NSURL Stack (Layer 7b)        |\n|                                      |\n|--------------------------------------|\n|                                      |\n|          Protocol/App Layer          |\n|     HTTP/1.1 \u0026 HTTP/2 (Layer 7a)     |\n|                                      |\n|--------------------------------------|\n|                                      |\n|            Presentation Layer        |\n| Encryption \u0026 Serialization (Layer 6) |\n|                                      |\n|--------------------------------------|\n|                                      |\n|            Session Layer             |\n|      A Feature of TCP (Layer 5)      |\n|                                      |\n|--------------------------------------|\n|                                      |\n|            Transport Layer           |\n|             TCP (Layer 4)            |\n|                                      |\n|--------------------------------------|\n|                                      |\n|         Routing/Network Layer        |\n|              IP (Layer 3)            |\n|                                      |\n|--------------------------------------|\n|                                      |\n|            Data Link Layer           |\n|          IEEE 802.X (Layer 2)        |\n|                                      |\n|--------------------------------------|\n|                                      |\n|            Physical Layer            |\n|          Ethernet (Layer 1)          |\n|                                      |\n\\--------------------------------------/\n```\n\n## Brief Overview\n\n### Features\n\n__Twitter Network Layer__ provides a framework on top of __Apple__'s `NSURLSession` framework with\nnumerous benefits.  Here are some of the features provided by __TNL__:\n\n- All the features of `NSURLSession`, simplified where appropriate\n- `NSOperation` based request operations (for `NSOperation` features)\n- Strong separation of roles in the framework's objects\n- Immutable/mutable pairings of requests (`TNLHTTPRequest`) and configurations (`TNLRequestConfiguration`)\n- Encapsulated immutable responses (`TNLResponse`)\n- Prioritization of requests\n- Selectable response body consumption modes (`NSData` storage, callback chunking or saving to file)\n- Request hydration (enables polymorphic requests and dynamically populated requests)\n- Dynamic retrying with retry policies\n- More events (request operation state transitions, progress, network state/condition updates, etc)\n- Modular delegates for separation of roles and increased reusability and easy overriding\n\n### Usage\n\nThe high level concept of how to use __TNL__ is rather straightforward:\n\n1. Set up any reuseable settings (by doing any combination of the following):\n    - Build shared accessors to resuable `TNLRequestConfiguration` instances\n    - Implement a `TNLRequestDelegate` (if added functionality is desired beyond just handling the result)\n    - Configure a `TNLRequestConfiguration` for reuse\n    - Configure the `TNLGlobalConfiguration`\n2. Set up any reusable `TNLRequestOperationQueue` objects once (ex: one for API requests, one for image requests, etc.)\n    - `[TNLRequestOperationQueue initWithIdentifier:]`\n3. Generate and enqueue any desired `TNLRequestOperation` with the following objects:\n    - `TNLRequest` conforming object (including `TNLHTTPRequest` concrete class and `NSURLRequest`)\n    - `TNLRequestConfiguration` (optional)\n    - `TNLRequestDelegate` (optional)\n4. Handle the events appropriately via the callbacks, particularly the completion callback that provides the `TNLResponse`\n    - Delegate callbacks will go to the appropriate sub-protocol in the `TNLRequestOperation`'s `TNLRequestDelegate`\n\n# HOWTO\n\n## Where To Start\n\n __Twitter Network Layer__ documentation starts with this `README.md` and progresses through the APIs via their documentation.\n\n## Overview of a Network Operation\n\n### Core Objects\n\n The core objects of a service based architecture are _request_, _response_ and _operation/task/action_ (referred to as an _operation_ from here on).\n The _request_ encapsulates data to send and is not actionable;\n the _response_ encapsulates the data received and is not actionable;\n and the _operation_ is the object that delivers the _request_ and retrieves the _response_ and\n is the only actionable object in the set of core objects.\n\n This high level concept translates directly into a network architecture as we will have _requests_\n that encapsulate the data of an _HTTP request_ which are _Headers_ and a _Body_, _responses_ that\n encapsulate the data of an _HTTP response_ which are _Headers_ and a _Body_, and last the _operation_\n that executes delivering the _request_ and retrieving the _response_.\n\n### Core Object Breakdown\n\n - _request_\n   - encapsulates data to send\n   - immutability provides stability\n   - not actionable, just data\n   - `TNLRequest` is the protocol for requests in __TNL__\n   - `TNLHTTPRequest` and `NSURLRequest` are concrete classes (both are immutable/mutable pairs)\n - _response_\n   - encapsulates data received\n   - immutability provides stability\n   - not actionable, just data\n   - `TNLResponse` is the object for responses in __TNL__ (composite object that includes an `NSHTTPURLResponse`)\n - _operation_\n   - the executing object\n   - delivers the _request_\n   - retrieves the _response_\n   - actionable (e.g. starting, canceling, priotiziation, modifying dependencies)\n   - `TNLRequestOperation` is the operation in __TNL__ (subclasses `NSOperation`) and is backed by `NSURLSessionTask`\n\n### Support Objects\n\n In addition to a service architecture having _requests_, _operations_ and _responses_;\n support objects are often present that aid in the management of the executing _operations_,\n configuration of their behavior and delegation of decisions or events.\n\n The _configuration_ object encapsulates __how__ an _operation_ behaves.  It will have no impact on\n what is sent in the _operation_ (that's the _request_), but can modify how it is sent.\n For instance, the _configuration_ can indicate a maximum duration that the _operation_ can take\n before it should fail.\n\n The _delegate_ object provides the extensibility of on demand decision making when prudent as well\n as the delivery of events as the _operation_ executes.\n\n The _manager_ object coordinates the execution of multiple _operations_ within a logical grouping.\n\n\n### Support Object Breakdown\n\n - _configuration_\n   - encapsulation of behavior settings\n   - `TNLRequestConfiguration` is the config in __TNL__ (applied per _operation_)\n   - `NSURLSessionConfiguration` is the config in __NSURL__ stack (applied per _manager_)\n - _delegate_\n   - provides extensibility\n   - has callbacks for on demand decisions\n   - has callbacks for events as they occur\n   - `TNLRequestDelegate` is the delegate in __TNL__ (provided per _operation_)\n   - `NSURLSessionDelegate` is the delegate in __NSURL__ stack (provided per _manager_)\n - _manager_\n   - coordinator of multiple _operations_\n   - permits grouping of _operations_\n   - `TNLRequestOperationQueue` is the manager object in __TNL__\n   - `NSURLSession` is the manager object in __NSURL__ stack\n\n Note: You can already see there is a fundamental architecture difference between `NSURLSession` networking\n and __Twitter Network Layer__.  The _configuration_ and _delegate_ per _operation_ approach in __TNL__\n is much more scalable when dealing with dozens or hundreds of unique configurations and/or delegates\n given a plethora of requests and their needs.  Coupling the _configuration_ and/or _delegate_ to the\n reusable _manager_ object(s) is unwieldy and can lead to mistakes w.r.t. correct configuration and event\n on a per request basis.\n\n## Building a Request\n\n __TNL__ uses the `TNLRequest` as the interface for all network requests.  In practice, the protocol\n is used in one of 3 ways:\n\n 1. Concrete `TNLHTTPRequest`\n   - Configuring a concrete `TNLHTTPRequest` object (or `TNLMutableHTTPRequest`)\n 2. `NSURLRequest`\n   - `NSURLRequest` explicitely conforms to `TNLRequest` protocol via a category in __TNL__ making it supported as _request_ object.\n   - However, since __TNL__ rigidly segregates _configuration_ from _request_, only the _request_ properties on `NSURLRequest` are observed and the _configuration_ properties of `NSURLRequest` are ignored.\n 3. Implementing a custom `TNLRequest`\n   - __TNL__ supports having anything that conforms to `TNLRequest` as an _original request_ for an _operation_.\n   - Makes it simple for an object that encapsulates the minimal amount of information necessary to take the place as the _original request_.\n     - You could have a `APPRetrieveBlobRequest` that has 1 property, the identifier for the \"Blob\" call _blobIdentifier_.\n     - That object doesn't need to have any methods that actually represent anything related to an _HTTP request_ and that's ok.  However, in order for the _operation_ to send the _original request_, it needs to be able to be treated as an __HTTP request__, which is to say it must conform to `TNLRequest`.  This can be done in 2 ways:\n       1. have the object implement `TNLRequest` and have its methods that populate the values by the relevant properties (in our example, the blob identifier)\n       2. have the _delegate_ implement the _request hydration_ callback to convert the opaque request into a well formed `TNLRequest` ready for __HTTP__ transport.\n     - See _Custom TNLRequest_ examples\n\n When it comes to making the choice, it can boil down to convenience vs simplicity of reuse.\n If you have a one shot request that has no reuse, options __1__ and __2__ will suffice.\n If you have a request that can be reused throughout the code base, option __3__ clearly offers the cleanest interface.\n By having the caller only need to know the class of the request and the relevant values for populating the request,\n any concern over the _HTTP_ structure is completely eliminated.\n\n### Concrete TNLRequest with TNLHTTPRequest\n\n __TNLHTTPRequest:__\n\n```\nNSString *URLString = [NSString stringWithFormat:@\"http://api.myapp.com/blob/%tu\", blobIdentifier];\nNSURL *URL = [NSURL URLWithString:URLString];\nTNLHTTPRequest *request = [TNLHTTPRequest GETRequestWithURL:URL\n                                           HTTPHeaderFields:@{@\"User-Agent\": [MYAPPDELEGATE userAgentString]}];\n```\n\n __TNLMutableHTTPRequest:__\n\n```\nNSString *URLString = [NSString stringWithFormat:@\"http://api.myapp.com/blob/%tu\", blobIdentifier];\nNSURL *URL = [NSURL URLWithString:URLString];\nTNLMutableHTTPRequest *mRequest = [[TNLMutableHTTPRequest alloc] init];\nmRequest.HTTPMethodValue = TNLHTTPMethodValueGET;\nmRequest.URL = URL;\n[mRequest setValue:[MYAPPDELEGATE userAgentString] forHTTPHeaderField:@\"User-Agent\"];\n```\n\n### NSURLRequest\n\n```\nNSString *URLString = [NSString stringWithFormat:@\"http://api.myapp.com/blob/%tu\", blobIdentifier];\nNSURL *URL = [NSURL URLWithString:URLString];\nNSMutableURLRequest *mRequest = [[NSMutableURLRequest alloc] init];\nmRequest.HTTPMethod = @\"GET\";\nmRequest.URL = URL;\n[mRequest setValue:[MYAPPDELEGATE userAgentString] forHTTPHeaderField:@\"User-Agent\"];\n```\n\n### Custom TNLRequest\n\n __1) Request Hydration__\n\n```\nAPPRetrieveBlobRequest *request = [[APPRetrieveBlobRequest alloc] initWithBlobIdentifier:blobIdentifier];\n\n// ... elsewhere ...\n\n- (void)tnl_requestOperation:(TNLRequestOperation *)op\n              hydrateRequest:(APPRetrieveBlobRequest *)request // we know the type\n                  completion:(TNLRequestHydrateCompletionBlock)complete\n{\n     NSString *URLString = [NSString stringWithFormat:@\"http://api.myapp.com/blob/%tu\", blobRequest.blobIdentifier];\n     NSURL *URL = [NSURL URLWithString:URLString];\n     TNLHTTPRequest *newReq = [TNLHTTPRequest GETRequestWithURL:URL\n                                               HTTPHeaderFields:@{@\"User-Agent\": [MYAPPDELEGATE userAgentString]}];\n     complete(newReq);\n}\n```\n\n __2) Request with HTTP support__\n\n```\nAPPRetrieveBlobRequest *request = [[APPRetrieveBlobRequest alloc] initWithBlobIdentifier:blobIdentifier];\n\n// ... elsewhere ...\n\n@implementation APPRetrieveBlobRequest\n\n- (NSURL *)URL\n{\n    NSString *URLString = [NSString stringWithFormat:@\"http://api.myapp.com/blob/%tu\", self.blobIdentifier];\n    return [NSURL URLWithString:URLString];\n}\n\n- (NSDictionary *)allHTTPHeaderFields\n{\n    return @{@\"User-Agent\":[MYAPPDELEGATE userAgentString]};\n}\n\n// POINT OF IMPROVEMENT:\n// utilize polymorphism and have an APPBaseRequest class that implements\n// the \"allHTTPHeaderFields\" so that all subclasses (including APPRetrieveBlobRequest)\n// will inherit the desired defaults.\n// This can apply to a wide range of HTTP related TNLHTTPRequest properties\n// or even composition of subcomponents that are aggregated to a single property.\n// For example: the host of the URL (api.myapp.com) could be provided\n// as a property on APPBaseRequest that permits subclasses to override the host, and then\n// the construction of the `URL` uses composition of variation properites that the subclasses\n// can provide.\n\n@end\n```\n\n## Inspecting a Response\n\n When an _operation_ completes, a _TNLResponse_ is populated and provided to the completion block\n or completion callback (depending on if you use a _delegate_ or not).  The _TNLResponse_ has all\n the information necessary to understand how the _operation_ completed, as well as what information\n was retrieve.  Additionally, with _response polymorphism_, the _response_ can be extended to\n provide better contextual information regarding the result, such as parsing the _response_ body as\n JSON or converting the _response_ body into a `UIImage`.\n\n The way you deal with a _TNLResponse_ should be systematic and straighforward:\n\n 1. deal with any errors on the _response_\n    - _TNLResponse_ has an _operationError_ property but custom subclasses could expose other errors too.\n    - Subclass _response_ objects that have extra errors should consider having an `anyError` property for quick access to any error in the response.\n 2. deal with the status code of the _response_\n    - It is important to know that a 404 is not an _operation_ error so it won't be set as an error.\n    - It is actually the status of the successful operation and needs to be handled accordingly.\n    - For designs that want to treat HTTP Status codes that are not success as errors, they should expose an HTTP error on their _response_ subclass(es).\n 3. deal with the _response_ payload\n    - This could be the _response_ HTTP headers, the _response_ body (as `NSData` or a file on disk), etc\n    - _response_ subclasses should consider deserializing their response's body into a model object and exposing it as a property for concrete interactions.\n\n One benefit to using _response polymorphism_ is the ability to handle the _response_ and populate\n the _hydrated response_ with the information that's pertinent to the caller.\n For example: if your network operation yields JSON, and all you care about is if that JSON came\n through or not, at hydration time you could check for any error conditions then parse out the JSON\n and if everything is good have a property on the custom `TNLResponse` subclasss that holds the\n `NSDictionary` _result_ property (or `nil` if anything along the way prevented success).\n\n Things you can inspect on a _response_ by default:\n\n - the _operation_ error (if one occurred)\n - the _original request_\n - the _response_ info\n   - this object encapsulates the information of the _HTTP response_ including:\n     - the source of the _response_ (local cache or network load)\n     - the _response_ body (as `data` or `temporarySavedFile` if the _operation_ was configured to maintain the data)\n     - the final `NSURLRequest` that loaded the _response_\n     - the final `NSURLResponse` object\n   - it also provides convenience accessors\n     - the _response_'s _HTTP_ status code\n     - the final `NSURL`\n     - all the _HTTP_ header fields\n - the _response_ metrics\n   - detailed metric information such as execution timings, durations, bytes sent/received, attempt counts, etc.\n   - this is the detail that __TNL__ exposes for every _request_/_operation_/_response_ that really empowers programmers to maximize impact with their networking.\n\n## Simple Network Operations\n\n __Twitter Network Layer__ provides a highly robust API for building network operations with a great\n deal of flexibility and extensibility.  However, there are often occasions when you just need to\n execute an _operation_ and need things to be as simple as possible.  __Twitter Network Layer__\n provides all the convenience necessary for getting what needs to be done as simply as possible.\n\n```\n NSString *URLString = [NSURL URLWithString:@\"http://api.myapp.com/settings\"];\n NSURLRequest *request = [NSURLRequest requestWithURL:URLString];\n [[TNLRequestOperationQueue defaultOperationQueue] enqueueRequest:request\n                                                       completion:^(TNLRequestOperation *op, TNLResponse *response) {\n     NSDictionary *json = nil;\n     if (!response.operationError \u0026\u0026 response.info.statusCode == 200) {\n         json = [NSJSONSerialization JSONObjectWithData:response.info.data options:0 error:NULL];\n     }\n     if (json) {\n        [self _myapp_didCompleteSettingsRequestWithJSON:json];\n     } else {\n        [self _myapp_didFailSettingsRequest];\n     }\n }];\n```\n\n ## Configuring Behavior\n\n Often the vanila _configuration_ for an _operation_ will suffice, however it is common to need\n particular behaviors in order to get specific use cases to work.  Let's take, as an example, firing\n _network operation_ when a specific metric is hit.  In this case, we don't care about storing the\n _response_ body and we also want to avoid having a cache that could get in the way.\n\n```\n NSURL *URL = [NSURL URLWithString:@\"http://api.myapp.com/recordMetric?hit=true\"];\n TNLHTTPRequest *request = [TNLHTTPRequest GETRequestWithURL:URL HTTPHeaderFields:nil];\n TNLMutableRequestConfiguration *config = [TNLMutableRequestConfiguration defaultConfiguration];\n config.responseDataConsumptionMode = TNLResponseDataConsumptionModeNone;\n config.URLCache = nil; // Note: 'URLCache' is now 'nil' by default in TNL, but the illustration still works\n TNLRequestOperation *op = [TNLRequestOperation operationWithRequest:request\n                                                       configuration:config\n                                                          completion:^(TNLRequestOperation *o, TNLResponse *response) {\n     assert(response.info.source != TNLResponseSourceLocalCache);\n     const BOOL success = response.info.statusCode == 202;\n     [self didSendMetric:success];\n }];\n [[TNLRequestOperationQueue defaultOperationQueue] enqueueOperation:op];\n```\n\n Now, sometimes, you may want to have the same defaults for certain kinds of _operations_.  That can\n easily be accomplished with a category or some other shared accessor.\n\n```\n @interface TNLRequestConfiguration (APPAdditions)\n + (instancetype)configurationForMetricsFiring;\n @end\n\n @implementation TNLRequestConfiguration (APPAdditions)\n\n + (instancetype)configurationForMetricsFiring\n {\n     static TNLRequestConfiguration* sConfig;\n     static dispatch_once_t onceToken;\n     dispatch_once(\u0026onceToken, ^{\n         TNLMutableRequestConfiguration *mConfig = [TNLMutableRequestConfiguration defaultConfiguration];\n         mConfig.URLCache = nil; // Note: 'URLCache' is now 'nil' by default in TNL, but the illustration still works\n         mConfig.responseDataConsumptionMode = TNLResponseDataConsumptionModeNone;\n         sConfig = [mConfig copy];\n     });\n     return sConfig;\n }\n\n @end\n\n @implementation TNLMutableRequestConfiguration (APPAdditions)\n\n + (instancetype)configurationForMetricsFiring\n {\n     return [[TNLRequestConfiguration configurationForMetricsFiring] mutableCopy];\n }\n\n @end\n```\n\n## Building an Advanced API Layer\n\n __Twitter Network Layer__ was designed from the ground up with REST APIs in mind.  From simple APIs\n to complex API layers that require a complicated system for managing all _operations_, __TNL__\n provides the foundation needed.\n\n As a pattern for creating concrete _API operations_, one of the first places to extend __TNL__ for\n your API layer is by concretely building API _requests_ and _responses_.  For _requests_, you\n implement a `TNLRequest` for every _request_ your API provides with properties that configure each\n request appropriately.  Those _requests_ should be subclassing a _base request_ that does the busy\n work of pulling together the generic properties that the subclasses can override to construct the\n _HTTP_ properties of the _request_.  Each subclassed _request_ then overrides only what is\n necessary to form the valid _HTTP_ request.  For things that are context or time sensitive, such as\n _request signing_, _request hydration_ should be used to fully saturate the custom _API request_ at\n the time the _request_ is going to sent (vs at the time it was enqueued).\n\n Following from custom _API requests_ are custom _API responses_.  At a minimum, it makes sense to\n have an _API response_ that subclasses `TNLResponse`.   To provide an even simpler interface to\n callers, you can implement a _response_ per _request_.  For _response hydration_, you merely\n extract whatever contextually relevant information is valuable for an _API response_ and set those\n properties on you custom subclass of `TNLResponse` (such as API error, JSON result, etc).\n\n If the API layer is advanced enough, it may warrant further encapsulation with a managing object\n which is often referred to as an _API client_.  The _API client_ would manage the queuing of\n _requests_, the delegate implementation for _operations_ (including _hydration_ for _requests_ and\n subclassing _responses_ so they hydrate too), the vending of _operations_, authentication/signing\n of _requests_, high level retry plus timeout behavior, custom _configurations_ and oberving\n _responses_ for custom handling.\n\n### Client Architecture\n\n With an _API client_ architecture, the entire _HTTP_ structure is encapsulated and callers can deal\n with things just the objects they care about.  No converting or validating.  No configuring.  The\n power of __TNL__ is completely utilized by _API client_ freeing the caller of any burden.\n\n```\n APISendMessageRequest *request = [[APISendMessageRequest alloc] init];\n request.sender = self.user;\n request.receiver = otherUser;\n request.message = message;\n self.sendOp = [[APIClient sharedInstance] enqueueRequest:request\n                                           completion:^(TNLRequestOperation *op, APIResponse *response) {\n    [weakSelf messageSendDidComplete:op withResponse:(id)response];\n }];\n\n // ... elsewhere ...\n\n - (void)messageSendDidComplete:(TNLRequestOperation *)op withResponse:(APISendMessageResponse *)response\n {\n     assert(self.sendOp == op);\n     self.sendOp = nil;\n     if (!sendMessageResponse.wasCancelled) {\n        if (sendMessageResponse.succeeded) {\n            [self updateUIForCompletedMessageSendWithId:sendMessageResponse.messageId];\n        } else {\n            [self updateUIForFailedMessageSendWithUserErrorTitle:sendMessageResponse.errorTitle\n                                                    errorMessage:sendMessageResponse.errorMessage];\n        }\n     }\n }\n\n // Insight:\n // Presumably, APISendMessageResponse would subclass a base response like APIBaseResponse.\n // Following that presumption, it would make sense that APIBaseResponse would expose\n // wasCancelled, succeeded, errorTitle and errorMessage while APISendMessageResponse would\n // expose messageId (since that is part of the response payload that is specific to the request).\n // It would likely make sense that if the API used JSON response bodies,\n // the base response would also expose a \"result\" property (NSDictionary) and\n // APISendMessageResponse's implementation for messageId is just:\n //    return self.result[@\"newMessageId\"];\n```\n\n## Using the Command-Line-Interface\n\n__Twitter Network Layer__ includes a target for building a _macOS_ tool called `tnlcli`.  You can build this tool\nrun it from _Terminal_ from you _Mac_, similar to _cURL_ or other networking command line utilities.\n\n### Usage\n\n```\nUsage: tnlcli [options] url\n\n    Example: tnlcli --request-method HEAD --response-header-mode file,print --response-header-file response_headers.json https://google.com\n\nArgument Options:\n-----------------\n\n    --request-config-file \u003cfilepath\u003e     TNLRequestConfiguration as a json file\n    --request-headers-file \u003cfilepath\u003e    json file of key-value-pairs for using as headers\n    --request-body-file \u003cfilepath\u003e       file for the HTTP body\n\n    --request-header \"Field: Value\"      A header to provide with the request (will override the header if also in the request header file). Can provide multiple headers.\n    --request-config \"config: value\"     A config setting for the TNLRequestConfiguration of the request (will override the config if also in the request config file). Can provide multiple configs.\n    --request-method \u003cmethod\u003e            HTTP Method from Section 9 in HTTP/1.1 spec (RFC 2616), such as GET, POST, HEAD, etc\n\n    --response-body-mode \u003cmode\u003e          \"file\" or \"print\" or a combo using commas\n    --response-body-file \u003cfilepath\u003e      file for the response body to save to (requires \"file\" for --response-body-mode\n    --response-headers-mode \u003cmode\u003e       \"file\" or \"print\" or a combo using commas\n    --response-headers-file \u003cfilepath\u003e   file for the response headers to save to (as json)\n\n    --dump-cert-chain-directory \u003cdir\u003e    directory for the certification chain to be dumped to (as DER files)\n\n    --verbose                            Will print verbose information and force the --response-body-mode and --responde-headers-mode to have \"print\".\n    --version                            Will print ther version information.\n```\n\n# License\n\nCopyright 2014-2020 Twitter, Inc.\n\nLicensed under the Apache License, Version 2.0: https://www.apache.org/licenses/LICENSE-2.0\n\n# Security Issues?\n\nPlease report sensitive security issues via Twitter's bug-bounty program (https://hackerone.com/twitter) rather than GitHub.\n","funding_links":[],"categories":["HarmonyOS","Objective-C"],"sub_categories":["Windows Manager"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwitter%2Fios-twitter-network-layer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftwitter%2Fios-twitter-network-layer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwitter%2Fios-twitter-network-layer/lists"}