Xây dựng swift package để gọi network trong swift
Bài viết được sự cho phép của tác giả Lê Xuân Quỳnh
Tuần trước, chúng ta đã xem qua 1 cách custom về thư viện bàn phím hiển thị gợi ý tiền Việt Nam. Hôm nay tôi sẽ hướng dẫn cách tạo 1 thư viện để gọi network dạng restful API. Thư viện tên là Qnetwork. Bạn có thể tải trên github tại đường dẫn:
https://github.com/lexuanquynh/QNetwork
Thư viện này dùng để làm gì?
Đầu tiên trước khi tiến hành xây dựng, hãy tải phần demo của tôi tại đường dẫn:
https://github.com/lexuanquynh/QNetworkDemo
Đầu tiên, bạn cần biết rằng tôi xây dựng thư viện này tốt nhất cho kiến trúc MVVM. Tuy nhiên bạn vẫn có thể sử dụng nó cho các architecture khác mà bạn muốn. Nếu hiểu theo cách đơn giản, bạn dùng thư viện này, bạn tạo các service để gọi API. Sau đó, bạn dùng nó để gọi bất cứ ở đâu bạn muốn.
Xem thêm các việc làm Swift lương cao trên TopDev
Vậy cách sử dụng nó như nào? Hãy xem phần code demo của tôi. Ở ví dụ này tôi sẽ tiến hành gọi API của github:
curl \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/search/repositories
Trong 1 bài viết trước đây, tôi đã giới thiệu khá kỹ càng cách viết API cho nó. Các bước tiến hành như sau:
- Tạo 1 module Github. Mỗi module sẽ phục vụ cho 1 màn hình riêng biệt. Trong thiết kế MVVM tôi hay làm như vậy, module bao gồm View, Model, servive gọi API và view model. Bạn có thể mở souce code ở trên ra xem.
- Tạo 1 file GithubAPI. File này chứa cụm API cần gọi. Thông thường trong ứng dụng của tôi sẽ có nhiều file dạng *API. Mỗi 1 file chứa 1 cụm, ví dụ AuthenAPI sẽ chứa cụm đăng nhập, đăng xuất. FeedAPI chứa cụm API liên quan đến feed. Cách đặt tên sẽ sát nghĩa nó làm gì. Mô tả file này như sau:
// // GithubAPI.swift // QNetworkDemo // // Created by Xuân Quỳnh Lê on 2021/06/26. // import Foundation import QNetwork import Moya enum GithubAPI { case searchRepositories(q: String, sort: String, order: String, page: Int) } extension GithubAPI: TargetType { var baseURL: URL { let url = URL(string: Configs.Network.baseUrl)! return url } var path: String { switch self { case .searchRepositories: return "search/repositories" } } var method: Moya.Method { return .get } var sampleData: Data { var dataUrl: URL? switch self { case .searchRepositories: if let file = Bundle.main.url(forResource: "SearchRepositoriesResponse", withExtension: "json") { dataUrl = file } } if let url = dataUrl, let data = try? Data(contentsOf: url) { return data } return Data() } var task: Task { switch self { case .searchRepositories: if let parameters = parameters { return .requestParameters(parameters: parameters, encoding: parameterEncoding) } } return .requestPlain } var headers: [String: String]? { return ["Content-type": "application/json"] } var parameters: [String: Any]? { var params: [String: Any] = [:] switch self { case .searchRepositories(let q, let sort, let order, let page): params["q"] = q params["sort"] = sort params["order"] = order params["page"] = page } return params } // For json encode. Use in post request var jsonEncoding: JSONEncoding { return JSONEncoding.default } // For param encode. Use in get request var parameterEncoding: ParameterEncoding { return URLEncoding.default } }
- Sau đó bạn tạo tiếp file GithubSearchService. Ở đây bạn cần truyền 1 struct Codable để lúc API có response, bạn cần đưa nó vào và chuyển json sang struct này. Cụ thể là struct GithubSearchResponse:
// MARK: - GithubSearchResponse struct GithubSearchResponse: Codable { let totalCount: Int? let incompleteResults: Bool let items: [GithubSearchItem]? enum CodingKeys: String, CodingKey { case totalCount = "total_count" case incompleteResults = "incomplete_results" case items } } // MARK: - Item struct GithubSearchItem: Codable { let id: Int let name: String? let htmlURL: String? let itemDescription: String? enum CodingKeys: String, CodingKey { case id, name case htmlURL = "html_url" case itemDescription = "description" } }
Nhiều bạn hỏi tôi sao có thể từ json chuyển sang struct được như trên. Tôi là 1 người lười biếng, nên tôi hay vào trang này để chuyển:
https://app.quicktype.io
Sau đó bạn chỉ cần chỉnh sửa tên cho hợp lý là được. Bạn làm nhiều sẽ quen tay thôi.
- Sau khi xong cụm service ở trên, bạn cần tạo GithubViewModel:
import Foundation class GithubViewModel { // Service call API let service: GithubSearchService! // Callback to view var needReloadTableView: (() -> Void)? var needShowError: ((String) -> Void)? var needSetStateBottomIndicatorView: ((_ show: Bool) -> Void)? private var page: Int = 0 private var language = "" private var incompleteResults = false // Datasource private var githubSearchItem: [GithubSearchItem] = [] init() { // Turn on is test is true if you need test for API self.service = GithubSearchService(isTest: false) } /// Clear tableview data source func clearTableView() { self.page = 0 self.incompleteResults = false self.githubSearchItem.removeAll() self.needReloadTableView?() } /// Request repositories func requestRepositories(language: String, loadMore: Bool = false) { // Check when load more if self.incompleteResults { return } if !loadMore { self.page = 0 self.githubSearchItem.removeAll() } self.language = language // Default param let sort = "stars" let order = "desc" self.service.searchRepositories(language: language, sort: sort, order: order, page: self.page) { [weak self] result in guard let strongSelf = self else { return } // Check when load more if loadMore { strongSelf.needSetStateBottomIndicatorView?(false) } switch result { case .success(let githubResponse): strongSelf.incompleteResults = githubResponse.incompleteResults if let items = githubResponse.items { items.forEach( {strongSelf.githubSearchItem.append( $0 )}) } strongSelf.needReloadTableView?() case .failure(let error): strongSelf.needShowError?(error.description) } } } func numberOfRowsInSection(section: Int) -> Int { return githubSearchItem.count } func cellForRowAt(indexPath: IndexPath) -> GithubSearchItem { // Check if the last row number is the same as the last current data element if indexPath.row == self.githubSearchItem.count - 1 { self.page += 1 self.requestRepositories(language: language, loadMore: true) self.needSetStateBottomIndicatorView?(true) } return githubSearchItem[indexPath.row] } }
Nhiều bạn sẽ tò mò tại sao tôi lại viết được như trên. Cũng tương tự, tôi hay tải source của người khác về xem. Sau đó tôi clone y hệt, người ta viết gì, tôi viết nấy. Lâu dần quen tay, có phản xạ. Cuối cùng là hiểu được ý nghĩa mà người ta làm. Dù sao thì thực hành vẫn là cách tốt nhất để học lập trình.
Xây dựng QNetwork như nào?
Việc này khá là mù mờ nếu như không có video. Thú thật tôi đã xem video hướng dẫn của 1 developer khác ở đường dẫn này:
https://www.youtube.com/watch?v=xu9oeCAS8aA
Bạn xem kỹ video, làm theo anh ấy, bạn sẽ tạo được 1 swift package và có thể đưa cho người khác sử dụng thông qua 1 đường dẫn. Với Qnetwork, đơn giản bạn chỉ cần thêm đường dẫn này vào phần swift package của bạn(Xcode ➞ File ➞ New ➞ Swift Package):
https://github.com/lexuanquynh/QNetwork.git
Từ nay tôi sẽ chỉ cần 1 đường link, sau đó thêm vào, và có thể dễ dàng triển khai lớp network service của mình.
Nếu bạn có câu hỏi gì, hãy vào fan page của trang tại địa chỉ:
https://www.facebook.com/codetoanbug
Hãy chia sẻ cho ai đó nếu bạn thấy bài viết có ích.
Bài viết gốc được đăng tải tại codetoanbug.com
Có thể bạn quan tâm:
- Ghi chú file package.json của node module
- Hằng và biến trong Swift
- Kiểu dữ liệu, biến, hằng trong ngôn ngữ Swift
Xem thêm Việc làm IT hấp dẫn trên TopDev
- B BenQ RD Series – Dòng Màn Hình Lập Trình 4k+ Đầu Tiên Trên Thế Giới
- i iOS 18 có gì mới? Có nên cập nhật iOS 18 cho iPhone của bạn?
- G Gamma AI là gì? Cách tạo slide chuyên nghiệp chỉ trong vài phút
- P Power BI là gì? Vì sao doanh nghiệp nên sử dụng PBI?
- K KICC HCMC x TOPDEV – Bước đệm nâng tầm sự nghiệp cho nhân tài IT Việt Nam
- T Trello là gì? Cách sử dụng Trello để quản lý công việc
- T TOP 10 SỰ KIỆN CÔNG NGHỆ THƯỜNG NIÊN KHÔNG NÊN BỎ LỠ
- T Tìm hiểu Laptop AI – So sánh Laptop AI với Laptop thường
- M MySQL vs MS SQL Server: Phân biệt hai RDBMS phổ biến nhất
- S SearchGPT là gì? Công cụ tìm kiếm mới có thể đánh bại Google?