// // GiphyApiManager.swift // LiveLikeGiphyChallenge // // Created by Ljupco Nastevski on 30.1.22. // import Foundation enum GiphyApiError: Error { case canceledByUser case requestFailed case decodingFailed } fileprivate struct RequestParamKeys { static let ApiKey = "api_key" static let QueryKey = "q" static let OffsetKey = "offset" static let LimitKey = "limit" } fileprivate struct Versions { static let v1 = "/v1" } fileprivate struct Endpoints { static let Gifs = Versions.v1 + "/gifs" static let Trending = Gifs + "/trending" static let Search = Gifs + "/search" } class GiphyApiManager { static let shared = GiphyApiManager() private var baseUrl = URLComponents(string: Constants.GiphyBaseUrl)! private let session = URLSession(configuration: .ephemeral, delegate: nil, delegateQueue: nil) // Last searchable task private var lastSearchableTask: URLSessionDataTask? init() { // Default query items baseUrl.queryItems = [ URLQueryItem(name: RequestParamKeys.ApiKey, value: Constants.GiphyApiKey), URLQueryItem(name: RequestParamKeys.LimitKey, value: String(Constants.GiphyPaginationLimit)) ] } func cancelLastSearchableTask() { lastSearchableTask?.cancel() lastSearchableTask = nil } func cancelDataTaskForUrl(url: URL) { session.getAllTasks { tasks in tasks.filter { $0.state == .running }.filter { $0.originalRequest?.url == url}.first?.cancel() } } func getTrendingGifs(offset: Int, completion: @escaping (Result) -> Void) { let offset = URLQueryItem(name: RequestParamKeys.OffsetKey, value: String(offset)) self.lastSearchableTask = self.getCodable(endpoint: Endpoints.Trending, queries: [offset], completion: completion) } func getSearchGifs(offset: Int, query: String, completion: @escaping (Result) -> Void) { let offset = URLQueryItem(name: RequestParamKeys.OffsetKey, value: String(offset)) let query = URLQueryItem(name: RequestParamKeys.QueryKey, value: query) self.lastSearchableTask = self.getCodable(endpoint: Endpoints.Search, queries: [offset, query], completion: completion) } func getData(url: URL, completion: @escaping (Data?) -> Void) -> URLSessionDataTask { let task = session.dataTask(with: url) { data, _ , _ in completion(data) } task.resume() return task } private func getCodable(endpoint: String, queries: [URLQueryItem]? = nil, completion: @escaping (Result) -> Void) -> URLSessionDataTask { var fullUrlComponent = self.baseUrl if let queries = queries { fullUrlComponent.queryItems?.append(contentsOf: queries) } fullUrlComponent.path = endpoint let task = session.dataTask(with: fullUrlComponent.url!) { d, _, e in guard let data = d else { completion(.failure(e!)); return } do { let decoded = try JSONDecoder().decode(U.self, from: data) completion(.success(decoded)) } catch { completion(.failure(GiphyApiError.decodingFailed)) } } task.resume() return task } }