Generics — Part 2 using API

lyvennitha sasikumar
4 min readApr 22, 2021

In previous part, We had seen what is generic and how to use generics, also how those generics works. Here I am going to explain how to use generics for for saving API data by writing as a singleton for code compactness. Here am using openWeatherMap open API(Ref: https://openweathermap.org) and process the response with Result Protocol (Ref: https://developer.apple.com/documentation/swift/result).

API :

Here am going to list temp data from open weather map API with 5 days/3 hours endpoint. First i will start with creating APIClass.swift file for API singleton class.

Initialise the variables for shared instance. And also create variables for usage:

static let sharedInstance = APIClass()
let baseURL = “https://api.openweathermap.org/data/2.5/forecast"
let apiKey = “xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”

Here am writing a common API call method using URL sesssion. You can use whatever network call which is flexible to you. Here is the method you have to confirm T(generics) with codable protocol as it used codable confirmed modal classes. Also for returning data, as i mentioned above, am using Result protocol as completion param.

func urlsCall<T: Codable>(endpoint: String, type: T,completion: @escaping (Result<T, Error>) -> Void ){
//Do the things here (eg: API Call)
}

Now am going to compose URL using strings we need. I had form custom url for post method from openweathermap API. So use desired method as you required.

let Url = baseURL + “?q=” + endpoint + “&” + “appid=\(apiKey)”
print(URL)//URL String

Forming URL from URL String:

guard let serviceUrl = URL(string: Url) else { return }

Initiating URLRequest with the serviceURL for URLSession:

var request = URLRequest(url: serviceUrl)

Giving content type and http method to created URL request instance.

request.httpMethod = “POST”
request.setValue(“Application/json”, forHTTPHeaderField: “Content-Type”)

created http body for URL request by serialising the above data and assigned to httpbody of url request.

guard let httpBody = try? JSONSerialization.data(withJSONObject: [:], options: []) else {
return
}
request.httpBody = httpBody

API call using urlsession:

let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if
let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
do{
let decoded = try? JSONDecoder().decode(T.self, from: data)
completion(.success(decoded!))
}catch let error{
completion(.failure(error))
}
} catch {
print(error)
}
}
}.resume()

What happens in the above code!. Yes. This is happening!. We passed the URLRequest we created, to the URLSession class as param which return response. We get response, data and if error is there error from URL session result. The returned data is Data type. So we have to serialise the data.
For serialisation, We need a model class type where we save the api data. Those model classes are here:

HourlyForecastData

struct HourlyForecastData: Codable {
let cod: String?
let message, cnt: Int?
let list: [List]?
let city: City?
}

City
struct
City: Codable {
let id: Int?
let name: String?
let coord: Coord?
let country: String?
let population, timezone, sunrise, sunset: Int?
}

Coord
struct
Coord: Codable {
let lat, lon: Double?
}

List
struct
List: Codable {
let dt: Int?
let main: MainClass?
let weather: [Weather]?
let clouds: Clouds?
let wind: Wind?
let visibility: Int?
let pop: Double?
let sys: Sys?
let dtTxt: String?
enum CodingKeys: String, CodingKey {
case dt, main, weather, clouds, wind, visibility, pop, sys
case dtTxt = “dt_txt”
}
}

Clouds
struct
Clouds: Codable {
let all: Int?
}

MainClass

struct MainClass: Codable {
let temp, feelsLike, tempMin, tempMax: Double?
let pressure, seaLevel, grndLevel, humidity: Int?
let tempKf: Double?
enum CodingKeys: String, CodingKey {
case temp
case feelsLike = “feels_like”
case tempMin = “temp_min”
case tempMax = “temp_max”
case pressure
case seaLevel = “sea_level”
case grndLevel = “grnd_level”
case humidity
case tempKf = “temp_kf”
}
}

Sys
struct
Sys: Codable {
let pod: Pod?
}
enum Pod: String, Codable {
case d = “d”
case n = “n”
}

Weather
struct
Weather: Codable {
let id: Int?
let main: MainEnum?
let weatherDescription: Description?
let icon: String?
enum CodingKeys: String, CodingKey {
case id, main
case weatherDescription = “description”
case icon
}
}
enum MainEnum: String, Codable {
case clear = “Clear”
case clouds = “Clouds”
}

enum Description: String, Codable {
case brokenClouds = “broken clouds”
case clearSky = “clear sky”
case fewClouds = “few clouds”
case overcastClouds = “overcast clouds”
case scatteredClouds = “scattered clouds”
}

Wind:
struct
Wind: Codable {
let speed: Double?
let deg: Int?
let gust: Double?
}

Above is the model class for the url we used. Here Data is return from url response. We decode the data response using the generic format like below:

do{
let decoded = try? JSONDecoder().decode(T.self, from: data)
completion(.success(decoded!))
}catch let error{
completion(.failure(error))
}

How to use:

We here call API Class method using singleton instance with required param. As a result, we get the response in result type in completion block. From that we can save our data in response instance.

APIClass.sharedInstance.urlsCall(endpoint: “chennai”, type: responseWeaather, completion: { (response) in
switch
response{
case .success(let data):
self.responseWeaather = data
DispatchQueue.main.async {
self.tableView.reloadData()
}
case .failure(let error):
print(error)
}
})

By this way you can call multiple type of struct data with multiple endpoint. Your code also become compact.

Happy Coding!💚

--

--

lyvennitha sasikumar

Passionate iOS developer chasing my dreams toward success