- API End point
//
// APIEndpoint.swift
// FutureAPIDemo
//
// Created by Joynal Abedin on 3/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: - URLEndpoint
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
//
// APIService.swift
// CombineAPIDemo1
//
// Created by Joynal Abedin on 4/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: Decodable>(_ endpoint: EndpointType) -> AnyPublisher<T, Error>
}
//MARK: - APIService
class APIService<EndpointType: APIEndpoint>: APIClient {
func request<T: Decodable>(_ endpoint: EndpointType) -> AnyPublisher<T, Error> {
let baseAppend = endpoint.baseURL.appendingPathComponent(endpoint.path).absoluteString.removingPercentEncoding
var request = URLRequest(url: URL(string: baseAppend!)!)
///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)])
}
return URLSession.shared.dataTaskPublisher(for: request)
.tryMap { data, response -> Data in
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw APIError.invalidResponse
}
return data
}
.decode(type: T.self, decoder: JSONDecoder())
.subscribe(on: DispatchQueue.global(qos: .background))
.eraseToAnyPublisher()
}
}
3. API Model
//
// Response.swift
// CombineAPIDemo1
//
// Created by Joynal Abedin on 31/10/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
// CombineAPIDemo1
//
// Created by Joynal Abedin on 31/10/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
private var cancelables = Set<AnyCancellable>()
let apiClient = APIService<URLEndpoint>()
init() {
getBookList()
}
func getBookList(){
apiClient.request(URLEndpoint.searchBook(name: "code"))
.receive(on: DispatchQueue.main)
.sink { completion in
switch completion {
case .finished:
print("Execution Finihsed.")
case .failure(let error):
self.currentState = .FAILURE(error: error.localizedDescription)
}
} receiveValue: { books in
self.currentState = .SUCCESS(books: books)
}.store(in: &cancelables)
}
}