Uncategorized

Last week, we took a custom look at the keyboard library that displays pre-Vietnam currency suggestions. Today I will show you how to create a library to call the network as a restful API. The library is called Qnetwork. You can download it on github at the link:

https://github.com/lexuanquynh/QNetwork

What is this library for?

First before proceeding with construction, download my demo section at the link:

https://github.com/lexuanquynh/QNetworkDemo

First, you need to know that I built this library best for MVVM architecture. However, you can still use it for other architectures you want. If you understand in a simple way, you use this library, you create services to call apIs. Then you use it to call wherever you want.

So how to use it? Take a look at my demo code. In this example I will proceed to call the GITHUB API:

curl 
  -H "Accept: application/vnd.github.v3+json" 
  https://api.github.com/search/repositories

In a previous article , Iintroduced quite carefully how to write an API for it. The steps are as follows:

  • Create a Github module. Each module will serve a separate screen. In MVVM design I often do so, modules include View, Model, servive call API and view model. You can open the above souce code.
  • Create a GithubAPI file. This file contains the API cluster to call. Usually in my application there will be many files in *API format. Each file contains 1 cluster, for example, AuthenAPI will contain a logon and logout cluster. FeedAPI contains feed-related API clusters. The naming will kill what it does. The description of this file is as follows:
//
// 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
}
}
  • Then you create the GithubSearchService file again. Here you need to transfer 1 Codable struct so that when the API has a response, you need to insert it and transfer json to this struct. Specifically, 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"
}
}

Many of you ask me how I can go from json to struct as above. I'm a lazy person, so I used
to go on this page to https://app.quicktype.io

Then you just need to edit the name to make sense. You do a lot of things that will get used to.

  • After you've finished the service cluster above, you need to create 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]
}
}

Many of you will be curious why I wrote the above. Similarly, I often download other people's sources. Then I clone exactly the same, what they write, I write. Gradually accustomed to hands, with reflexes. Finally, understand what people do. Practice is still the best way to learn programming anyway.

How to build QNetwork?

This is quite obscure without videos. To be honest, I watched another developer's video tutorial on this link:
https://www.youtube.com/watch?v=xu9oeCAS8aA

You watch the video carefully, follow him, you will create a swift package and can give it to others to use through a link. With Qnetwork, simply add this path to your swift package(Xcode ➞ File ➞ New ➞ Swift Package):

https://github.com/lexuanquynh/QNetwork.git

From now on, I will only need a link, then add, and can easily deploy my network service class.

If you have any questions, go to the page's fan page at:

https://www.facebook.com/codetoanbug

Share it with someone if you find the post helpful.

Code Toàn Bug

Code nhiều bug nhưng biết cách giấu!

Leave a Reply

Your email address will not be published. Required fields are marked *