Using Data Task 2

  1. API End point
//
//  APIEndpoint.swift
//  DataTaskAPIDemo2
//
//  Created by Joynal Abedin on 1/11/23.
//

import Foundation

///APIEndpoint
protocol APIEndpoint {
    
    var baseURL: URL { get }
    var path: String { get }
    var method: HTTPMethod { get }
    var headers: [String: String]? { get }
    var parameters: [String: String]? { get }
}

///HTTPMethod
enum HTTPMethod: String {
    case get = "GET"
    case post = "POST"
    case put = "PUT"
    case patch = "PATCH"
    case delete = "DELETE"
}

//MARK: - UserEndpoint
enum URLEndpoint: APIEndpoint {
    
    ///all api` cases`
    case bookList
    case searchBook(name: String)
    
    ///ap `baseURL`
    var baseURL: URL {
        guard let url = URL(string: "https://devilarticle.com") else {
            fatalError()
        }
        return url
    }
    
    ///api `endPoint`
    var path: String {
        switch self {
        case .bookList:
            return "/api/LibraryService/GetAllBook"
        case .searchBook:
            return "/api/LibraryService/SearchBook"
        }
    }
    
    ///api `method`
    var method: HTTPMethod {
        switch self {
        case .bookList:
            return .get
        case .searchBook:
            return .get
        }
    }
    
    ///api `headers`
    var headers: [String: String]? {
        switch self {
        case .bookList:
            return ["Content-Type": "application/json"]
        case .searchBook(name: _):
            return ["Content-Type": "application/json"]
        }
    }
    
    ///api `parameters`
    var parameters: [String: String]? {
        switch self {
        case .bookList:
            return nil
        case .searchBook(name: let name):
            return ["name": name]
        }
    }
    
}

2. API Service

//
//  NetworkManager.swift
//  DataTaskAPIDemo
//
//  Created by Joynal Abedin on 1/11/23.
//

import Foundation
import Combine

///API Response errors
enum APIError: Error {
    case invalidResponse
    case invalidData
}

///protocol
protocol APIClient {
    associatedtype EndpointType: APIEndpoint
    func request<T: Codable>(_ endpoint: EndpointType, completion: @escaping (Result<T, Error>) -> Void)
}

//MARK: - APIService
class APIService<EndpointType: APIEndpoint>: APIClient {
    
    func request<T: Codable>(_ endpoint: EndpointType, completion: @escaping (Result<T, Error>) -> Void) {
        
        let baseAppend = endpoint.baseURL.appendingPathComponent(endpoint.path).absoluteString.removingPercentEncoding
        guard let baseAppend = baseAppend else { return }
        let url = URL(string: baseAppend)
        
        guard let url = url else { return }
        var request = URLRequest(url: url)
        
        ///added api endpoint
        request.httpMethod = endpoint.method.rawValue
        ///Set up any request `headers` or `parameters` here
        endpoint.headers?.forEach { request.addValue($0.value, forHTTPHeaderField: $0.key) }
        endpoint.parameters?.forEach {
            request.url?.append(queryItems: [URLQueryItem(name: $0.key, value: $0.value)])
        }
        
        ///fetch data
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            ///check error
            if let error = error {
                completion(.failure(error))
            }
            
            ///check status code
            guard let response = response as? HTTPURLResponse,
                  (200...299).contains(response.statusCode) else {
                completion(.failure(APIError.invalidResponse))
                return
            }
            
            ///decode data
            if let data = data{
                DispatchQueue.main.async {
                    do {
                        let object = try JSONDecoder().decode(T.self, from: data)
                        completion(.success(object))
                    } catch let decoderError {
                        completion(.failure(decoderError))
                    }
                }
            }
        }.resume()
        ///end URLSession
    }
}

3. Response Model

//
//  Response.swift
//  DataTaskAPIDemo
//
//  Created by Joynal Abedin on 1/11/23.
//

import Foundation

struct Response: Codable {
    var responseCode: Int
    var result: String
    var errormessage: String?
    var data: [BookList]
}

struct BookList: Codable {
    var id: Int
    var bookId: Int
    var bookName: String
    var availableCopyNumber: Int
}

4. Get Data

//
//  ContentVM.swift
//  DataTaskAPIDemo2
//
//  Created by Joynal Abedin on 4/11/23.
//

import Foundation
import Combine

class ContentVM: ObservableObject {
    
    enum ViewState {
        case START
        case LOADING
        case SUCCESS(books: Response)
        case FAILURE(error: String)
    }
    
    @Published var currentState: ViewState = .START
    let apiClient = APIService<URLEndpoint>()
    
    init() {
        getBookList()
    }
    
    func getBookList(){
        let apiClient = APIService<URLEndpoint>()
        apiClient.request(URLEndpoint.searchBook(name: "code")) { (result: Result<Response, Error>) in
            switch result {
            case .success(let books):
                self.currentState = .SUCCESS(books: books)
            case .failure(let error):
                /// A failure, please handle
                self.currentState = .FAILURE(error: error.localizedDescription)
            }
        }
    }
    
}