preface

In the last article, we have obtained some image resources of belletiger Enlightenment App, this article we start to encapsulate the network request. Before packaging, we need to capture the packages of the Tiger Enlightenment App. Here we use Charles’, which I believe everyone is not unfamiliar with, if you do not know, you can see the use of Charles in this article

useCharlescaught

Here’s the UI for the home page,

Let’s first try to catch the data of the home page, open the app after the request to a lot of data, but there is no recommendation at the top of the home page, singing children’s songs, watching, reading picture books, listening to stories, these five should be local write dead

Next, we switched to recommendations, singing nursery rhymes, watching activities, reading picture books and listening to stories to obtain data

Now that the data for the home page has been obtained, we can begin to encapsulate the network request

Network request encapsulation

The network in Swift asked us to use Alamofire and then encapsulated it further.

First we use CocoaPods to import Alamofire library, and then create a NetworkManager network management class, introduce Alamofire

NetworkManager is a singleton, which adds a dataRequest dictionary attribute. Its key is composed of the URL and parameters of the request interface, and its value is a request task (dataRequest). Why add this attribute? Because you are going to use chained calls so that you can retrieve the response data later

import Alamofire

class NetworkManager: NSObject {

    static let share = NetworkManager()
    private var dataRequest = [String: DataRequest]()
    
    public func request(_ url: String,
                        method: HTTPMethod = .post,
                        parameters: Parameters? = nil,
                        encoding: ParameterEncoding = JSONEncoding.default,
                        headers: HTTPHeaders? = nil) -> NetworkManager {
                        
        let key = requestKey(url, parameters)
        let request = AF.request(url,
                                 method: method,
                                 parameters: parameters,
                                 encoding: encoding,
                                 headers: headers)
        dataRequest[key] = request
        return self

    }
    
    public func responseData(completion: @escaping (Data) -> Void,
                             failure: @escaping (AFError) -> Void) {
        dataRequest.forEach { key, request in
            dataRequest.removeValue(forKey: key)
            request.responseData { response in
                switch response.result {
                case let .success(data):
                    completion(data)
                case let .failure(error):
                    failure(error)
                }
            }
        }
    }
}
Copy the code

Now that the initial encapsulation is complete, let’s see if we can request the data

NetworkManager.share
    .request("https://vd.ubestkid.com/api/v1/feature/qmtab_tj3.json", parameters: parameters)
    .responseData { data in
        debugPrint(data)
     } failure: { error in
}
Copy the code

The request is ok, and the result of the request is not shown here.

Although the network request can be used, the request parameters and data return section are not friendly enough. Next, let’s optimize the request parameters.

Since every request interface has URL, Method, parameters, Encoding, headers, etc., let’s use the protocol

Network request input processing

1. Add oneTargetTypeAgreements because eachurlThere’s a public section in there, sourlSplit intobaseURLandpath

public protocol TargetType {
    var baseURL: String { get }
    var path: String { get }
    var method: HTTPMethod { get }
    var parameters: [String: Any] { get }
    var encoding: ParameterEncoding { get }
    var headers: HTTPHeaders? { get }
}
Copy the code

Then give the extension to the TargetType, giving all the attributes a default value

extension TargetType {

    var baseURL: String {
        return "https://vd.ubestkid.com/"

    }
    
    var path: String {
        return ""
    }
    
    var method: HTTPMethod {
        return .post

    }
    
    var parameters: [String: Any] {

        return [:]

    }
    
    var encoding: ParameterEncoding {
        return JSONEncoding.default

    }
    
    var headers: HTTPHeaders? {
        return nil
    }
}
Copy the code

2, back to theNetworkManagerInside, modifyrequestMethods into the reference

public func request(_ target: TargetType) -> NetworkManager {

        let url = target.baseURL + target.path
        let key = requestKey(url, target.parameters)
        let request = AF.request(url,
                                 method: target.method,
                                 parameters: target.parameters,
                                 encoding: target.encoding,
                                 headers: target.headers)

        dataRequest[key] = request
        
        return self
    }
Copy the code

Add a request() method to the Extension of TargetType and call NetworkManager’s request() method from that method

extension TargetType {

    func request() -> NetworkManager {
        return NetworkManager.share.request(self)
    }
}
Copy the code

3. Create a new oneNetwork, interface request entry, later interfaces are divided according to module

struct Network {}
Copy the code

Add an enumeration of “Home” to the extension extension. Add a list of “Home” and “path” to the extension extension. Because recommendation, singing children’s songs, watching, reading picture books, listening to the story interface is not the same after qMTAB_, and the parameters are the same

Recommendation:… / API /v1/feature/qmtab_tj3.json… / API /v1/feature/qmtab_eg3.json… / API /v1/feature/qmtab_dh3.json… / API /v1/feature/qmtab_hb3.json /api/v1/feature/qmtab_gs3.json

extension Network { enum Home { case list(path: String) } } extension Network.Home: TargetType { var path: String { switch self { case let .list(path): return "api/v1/feature/qmtab_\(path).json" } } var parameters: [String: Any] { switch self { case .list: return ["mac": "", "exp10": 60, "exp2": 1, "ua": "", "devicetype": 1, "srcApp": "com.ubestkid.collection", "carrier": "46002", "svip_status": 2, "impsize": 1, "exp7": 55, "exp3": 73, "version": "4.0", "make" : "apple", "bannersafe" : 0, "oaid" : ""," sh: "2208," the network ": 1," VPS: "ten," sw ": 1242," cpId ": "blh", "splashsafe": 0, "channel": "c2", "exp8": 55, "pkg": "com.ubestkid.collection", "exp4": 34, "model": "" osv iPhone8, 2", ":" 14.6 ", "idfa" : ""," ppi ": 401," apiVersion ":" 1.1.0, "" exp9:37," OS ": 1," androidid ": "", "exp5": 39, "ak": "8f75a52eadde46239f2227ba64eab72b", "exp1": 58, "egvip_status": 2, "age": "1", "appver": "3.8.3", "installtime" : 1625143625930, "res_type" : 0, "IP" : ""," imei ":" ", "userId" : ""," exp6 ": 4]}}}Copy the code

Then use it like this:

Network.Home
    .list(path: "tj3")
    .request()
    .responseData { data in
            
} failure: { error in
        
}
Copy the code

Data return processing

Now you can request it normally, but what do you do with the returned data? Here we use the Apple Zone Codable protocol and recommend a CleanJSON library to convert to the Model. Based on the data returned from the home interface, we create a Response base class that is compliant to the Codable area

struct Response<T: Codable>: Codable {
    let errorCode: Int
    let errorMessage: String
    let result: T
    
    var success: Bool {
        return errorCode == 0
    }

    var appError: AppError {
        return AppError(code: errorCode, errorMessage: errorMessage)
    }
}
Copy the code

Go back to NetworkManager and modify the responseData() method

public func responseData<T: Codable>(_ type: T.Type, completion: @escaping (Response<T>) -> Void, failure: @escaping (AppError) -> Void) { dataRequest.forEach { key, request in dataRequest.removeValue(forKey: key) request.responseData { response in switch response.result { case let .success(data): if let responseData = try? CleanJSONDecoder().decode(Response<T>.self, from: data) { if ! responseData.success { failure(responseData.appError) return } completion(responseData) } else { failure(AppError(code: 0000, errorMessage: "failed to parse data "))} Case let. failure(error): failure(error.apperror)}}}}Copy the code

Now that the entire network request is encapsulated, let’s open it at last

Network.Home
    .list(path: "tj3")
    .request()
    .responseData(RecModel.self) { model in
        debugPrint(model.result)
} failure: { error in
        
}
Copy the code