12/08/2018, 14:48

Alamofire – Design Pattern in Swift 3

Alamofire là một thư viện thực hiện các phương thức kết nối Client - Server được viết bằng swift. Sau khi chuyển sang code swift thì có khá nhiều thay đổi, một trong số đó là các thư viện thường dùng trong objective-C Thì trên swift không còn nữa hoặc được xây dựng lại với nhiều thay đổi về format. ...

Alamofire là một thư viện thực hiện các phương thức kết nối Client - Server được viết bằng swift. Sau khi chuyển sang code swift thì có khá nhiều thay đổi, một trong số đó là các thư viện thường dùng trong objective-C Thì trên swift không còn nữa hoặc được xây dựng lại với nhiều thay đổi về format. Trong bài này mình sẽ cố gắng xây dựng một pattern cho Alamofire để việc kết nối client server một cách đơn giản nhất có thể.

Cài đặt Alamofire

Để cài đặt Alamofire, mở terminal di chuyển đến thư mục chứa project của bạn. Nếu bạn đã sử dụng pod trước đó thì chỉ cần thêm Alamofire vào Podfile và install lại là được, còn không bạn thực hiện các lệnh sau.

tienpm$ pod init
tienpm$ nano Podfile

Sau khi chạy lệnh trên một màn hình GNU nano hiện lên cho phép bạn chỉnh sửa Podfile. Sau khi thêm pod 'Alamofire' vào bạn save lại và chạy pod install để cài đặt Alamofire vào project của mình.

Xây dưng một lớp APIEngine

open class APIEngine {
    static let sharedInstance = APIEngine()
    func getDefaultParams() -> [String:Any] {}
    
    open func requestURL(withURL url: String, withHeader _headers:[String:String]!, withParams params:[String:Any]!, withHash hash:Bool, usingCache cache:Bool) -> MTResult {
        var headers = [String:String]()
        headers = ["Content-Type": "application/json"]
        if _headers != nil {
            for key in _headers.keys {
                headers[key] = _headers[key]
            }
        }
    
        var _params = self.getDefaultParams()
        if params != nil {
            for key in params.keys {
                _params[key] = params[key]
            }
        }
        if hash {   ....  }
        if(cache){ 
                // Get data from cache 
                
        }
        // Load new data
        let result = MTResult()
        result._usingCache = cache
        let strURL:String = self.getFullLinkWithSubPath(url)
        result.request = Alamofire.request(strURL, method: .get, parameters: _params, encoding: URLEncoding())
            .downloadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
                print("Progress: (progress.fractionCompleted)")
            }.validate { request, response, data in
                return .success
            }
        debugPrint(result.request)
        return result;
    }
}

Mình xây dựng một lớp khá đơn giản trong đó chứa một function requestURL có truyền các giá trị headers : Có config thêm gì không nếu có thì append thêm vào headers mặc định trong function mà đã config. url : Có thể là API URL hoặc subpath. (Thông thường root url chúng ta config riêng mỗi khi thay đổi domain chúng ta chỉ cần thay đổi root url mà thôi). params : params của API. Trong lớp APIEngine mình cũng đã xây dụng một function getDefaultParams để get cách giá trị default của params, thông thường sẽ có nhiều trường mặc định trong params sẽ được sử dụng cho tất cả các API (appversion, build, timestamp, ...) hash : Một phương thức authentication phụ thuộc vào dự án của bạn dùng loại nào. Ở đây mình hay dùng md5 hoặc sha1. cache: Có sử dụng cache không. Nếu có thì trả về cache response sau đó request lên server lấy dữ liệu mới nhật trả về.

Xong phần này khá simple. Tiếp theo mình xấy dụng một class MTResult để handling response trả về từ Alamofire.

Handling the response from the Alamofire

Để xử lý response trả về cũng như xử lý các trường hợp lỗi khi request client-server mình xây dụng thêm một lớp nữa đặt tên là MTResult.

open class MTResult {
    var rq: DataRequest?
    var _onComplete:CompletionBlock?
    var _onError:ErrorBlock?
    var _usingCache:Bool = false
 
    func completeBlock(onComplete:@escaping CompletionBlock)->Self{ ...}
    func errorBlock(onError:@escaping ErrorBlock){...}
   
    var request:DataRequest!{...}
    func cacheAPI() {...}
    func loadAPIFromCache(){...}
}

Để sử lý action khi request success hoặc error mình sẽ khai báo hai block trong MTResult. var _onComplete:CompletionBlock?
var _onError:ErrorBlock? Đồng thời viết thêm 2 function để đăng kí các action

    func completeBlock(onComplete:@escaping CompletionBlock)->Self{
        _onComplete = onComplete
        return self 
    }
    
    func errorBlock(onError:@escaping ErrorBlock){
        _onError = onError
    }

Tiếp theo khái báo một attribute request có kiểu DataRequest và sử dụng syntax để detect response từ Alamofire

var request:DataRequest {
       get{ return rq! }
       set (newVal){
           newVal.responseJSON { (response) in
               switch response.result {
               case .success(let result):
                   // Nếu success thì callback về thông qua block onComplete
                   self._onComplete?(JSON(result), false)
               case .failure(let error):
                   print("Request failed with error: (error)")
                   // Nếu error thì callback về thông qua block onError
                   self._onError?(error)
               }
           }
           rq = newVal
       }
   }

Về cơ bản là xong nhìn cũng khá clear không có chỗ nào quá khó cả             </div>
            
            <div class=

0