Write an iOS network request library yourself - reduce coupling

Write an iOS network request library yourself - reduce coupling

Open source project: Pitaya, an HTTP request library suitable for uploading large files: https://github.com/johnlui/Pitaya

In this article, we will work together to reduce the coupling of the previous code and use the adapter pattern to implement a network API independent of the underlying structure to create a real network request "library".

Reduce coupling

How to reduce coupling

The current clear soup noodle style code is easy to understand, but the function is single and the code is messy. Let's analyze the use process of NSURLSession:

Constructing NSURLRequest

Determine the URL

Determine the HTTP method (GET, POST, etc.)

Adding specific HTTP headers

Filling HTTP Body

Drive the session.dataTaskWithRequest method to start the request

Specific implementation

Create another NetworkManager class under Network, set URL, params, files, etc. as member variables, and initialize them in the constructor:

  1. class NetworkManager {
  2.  
  3. let method: String!
  4. let params: Dictionary let callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void
  5.  
  6. let session = NSURLSession.sharedSession()
  7. let url: String!
  8. var request: NSMutableURLRequest!
  9. var task: NSURLSessionTask!
  10.  
  11. init(url: String, method: String, params: Dictionary = Dictionary(), callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {
  12. self.url = url
  13. self.request = NSMutableURLRequest(URL: NSURL(string: url)!)
  14. self.method = method
  15. self.params = params
  16. self.callback = callback
  17. }
  18. }

Afterwards, the above analysis

1. Determine the URL

2. Determine the HTTP method (GET, POST, etc.)

3. Add specific HTTP headers

4. Fill in the HTTP Body

Encapsulate the first three steps into one function, encapsulate the last step into one function, and then encapsulate the code that drives session.dataTaskWithRequest into one function:

  1. func buildRequest() {
  2. if self.method == "GET" && self.params.count > 0 {
  3. self.request = NSMutableURLRequest(URL: NSURL(string: url + "?" + buildParams(self.params))!)
  4. }
  5.  
  6. request.HTTPMethod = self.method
  7.  
  8. if self.params.count > 0 {
  9. request.addValue( "application/x-www-form-urlencoded" , forHTTPHeaderField: "Content-Type" )
  10. }
  11. }
  12. func buildBody() {
  13. if self.params.count > 0 && self.method != "GET" {
  14. request.HTTPBody = buildParams(self.params).nsdata
  15. }
  16. }
  17. func fireTask() {
  18. task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
  19. self.callback(data: data, response: response, error: error)
  20. })
  21. task.resume()
  22. }

Then use a unified method to drive the above three functions to complete the request:

  1. func fire() {
  2. buildRequest()
  3. buildBody()
  4. fireTask()
  5. }

At the same time, don't forget the three parse params functions stolen from Alamofire, and put them in this class. So far, the work of reducing coupling is basically completed, and then we start to encapsulate the "network API".

Encapsulating "network API" using the adapter pattern

Understanding the Adapter Pattern

The adapter pattern is a design pattern that is easy to understand: my app needs a function to get the string returned by a URL. I currently choose Alamofire, but the developing Pitaya looks good. I want to replace it with Pitaya in the future, so I encapsulate a layer of my own network interface to shield the underlying details. When the time comes, I only need to modify this class, and there is no need to go deeper into the project to change so many interface calls.

The adapter pattern sounds high-sounding, but it is actually a design pattern that we use very commonly in daily coding.

Do it!

Modify the code of the Network class to:

  1. class Network{
  2. static func request(method: String, url: String, params: Dictionary = Dictionary(), callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {
  3. let manager = NetworkManager(url: url, method: method, params: params, callback: callback)
  4. manager.fire()
  5. }
  6. }

Done!

Encapsulating multi-level interfaces

Interface without params:

  1. static func request(method: String, url: String, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {
  2. let manager = NetworkManager(url: url, method: method, callback: callback)
  3. manager.fire()
  4. }

Two get interfaces (with and without params):

  1. static func get(url: String, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {
  2. let manager = NetworkManager(url: url, method: "GET" , callback: callback)
  3. manager.fire()
  4. }
  5. static func get(url: String, params: Dictionary, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {
  6. let manager = NetworkManager(url: url, method: "GET" , params: params, callback: callback)
  7. manager.fire()
  8. }

Two post interfaces (with and without params):

  1. static func post(url: String, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {
  2. let manager = NetworkManager(url: url, method: "POST" , callback: callback)
  3. manager.fire()
  4. }
  5. static func post(url: String, params: Dictionary, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {
  6. let manager = NetworkManager(url: url, method: "POST" , params: params, callback: callback)
  7. manager.fire()
  8. }

Test interface

Modify the calling code in ViewController to test the multi-level API:

  1. @IBAction func mainButtonBeTapped(sender: AnyObject) {
  2. let url = "http://pitayaswift.sinaapp.com/pitaya.php"  
  3.  
  4. Network.post(url, callback: { (data, response, error) -> Void in
  5. println( "POST 1 request successful" )
  6. })
  7. Network.post(url, params: [ "post" : "POST Network" ], callback: { (data, response, error) -> Void in
  8. let string = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
  9. println( "POST 2 request successful " + string)
  10. })
  11.  
  12. Network.get(url, callback: { (data, response, error) -> Void in
  13. println( "GET 1 request successful" )
  14. })
  15. Network.get(url, params: [ "get" : "POST Network" ], callback: { (data, response, error) -> Void in
  16. let string = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
  17. println( "GET 2 request successful " + string)
  18. })
  19.  
  20. Network.request( "GET" , url: url, params: [ "get" : "Request Network" ]) { (data, response, error) -> Void in
  21. let string = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
  22. println( "Request successful " + string)
  23. }
  24. }

Run the project and click the button to see the effect:

Multi-level API encapsulation is successful!

<<:  Write an iOS network request library by yourself - encapsulation interface

>>:  Software Engineer Entrepreneurship Trap - Taking Private Jobs

Recommend

FastQuery: a framework for fast data operations

FastQuery fast operation data layer framework Fas...

Hupu APP Product Analysis Report

This article conducts product experience and anal...

Methodology in the Internet Age: Where is the trend?

Many people say that we should stand on the wind,...

OLED dark room tear LCD TV: black beats the color of the real youren

The TV industry, which is used to being calm, has...

Free Qianjiang Road "Free Qianjiang Road Investment Course"

The course lecturers are researchers and fund man...

How to do website bidding promotion and how to do it well?

It has been more than a year since I switched fro...

When geometry meets physics: Is the Earth a topological insulator?

As physics developed into the 20th century, geome...

Pinduoduo platform operation and promotion strategy!

There is no doubt that price is Pinduoduo's a...

Faster acceleration than the BMW X1? GAC New Energy Trumpchi GE3 test drive

It is no exaggeration to say that I am a typical ...