- 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
// FutureAPIDemo
//
// Created by Joynal Abedin on 3/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 Future<T, Error> { promise in
URLSession.shared.dataTask(with: request) { (data, response, _) in
DispatchQueue.main.async {
do {
guard let data = data else {
return promise(.failure("Something went wrong" as! Error))
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw APIError.invalidResponse
}
let decodedData = try JSONDecoder().decode(T.self, from: data)
return promise(.success(decodedData))
} catch let error {
return promise(.failure(error))
}
}
}.resume()
}
.subscribe(on: DispatchQueue.global(qos: .background))
.eraseToAnyPublisher()
}
}
3. API 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
}
//
struct ToDo: Decodable {
let userId: Int
let id: Int
let title: String
let isComplete: Bool
enum CodingKeys: String, CodingKey {
case isComplete = "completed"
case userId, id, title
}
}
4. Get Data
//
// ContentVM.swift
// FutureAPIDemo
//
// Created by Joynal Abedin on 3/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
private var cancelables = Set<AnyCancellable>()
let apiClient = APIService<URLEndpoint>()
init() {
getBookList()
}
func getBookList() {
self.currentState = .LOADING
apiClient.request(URLEndpoint.bookList)
.sink { completion in
switch completion {
case .finished:
print("Execution Finihsed.")
case .failure(let error):
self.currentState = .FAILURE(error: error.localizedDescription)
}
} receiveValue: { users in
self.currentState = .SUCCESS(books: users)
}.store(in: &cancelables)
}
}