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

How to choose a computer room for server hosting?

A data center, also known as a computer room, is ...

3 Tips to Avoid Pitfalls in Kuaishou Paid Promotion and Increasing Followers

Operators will come into contact with paid promot...

The core methodology of community fission activities

I heard that a "novice" can become a &q...

Beware! Feeling sleepy after lunch? Your body may have…

In real life, many friends must have started yawn...

4K gaming large screen TCL 55E6700 gaming TV review

In the era of comprehensive TV intelligence, the ...

Lilies from southwest China bloom in botanical gardens around the world

Glaciers, snow-capped peaks, streams, and river v...

How did ancient ships find their way?

How to find ancient ships Direction of travel? Wi...