- 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)
}
}
}
}