1import Foundation23enum WallhavenError: Error {4 case invalidURL5 case networkError(Error)6 case decodingError(Error)7 case noData8 case apiError(String)9}10111213enum WallhavenSorting: String {14 case dateAdded = "date_added"15 case toplist16 case random17}1819@MainActor20protocol WallhavenSearching {21 func searchWallpapers(22 query: String, categories: String, purity: String, sorting: WallhavenSorting,23 resolutions: [String], ratios: [String], limit: Int, page: Int24 ) async throws -> ([Wallpaper], Meta?)25}2627class WallhavenAPI: WallhavenSearching {28 private let session: URLSession2930 init(session: URLSession = .shared) {31 self.session = session32 }3334 func searchWallpapers(35 query: String,36 categories: String = "111",37 purity: String = "100",38 sorting: WallhavenSorting = .dateAdded,39 resolutions: [String] = [],40 ratios: [String] = [],41 limit: Int = 24,42 page: Int = 143 ) async throws -> ([Wallpaper], Meta?) {44 var queryItems = [45 URLQueryItem(name: "q", value: query),46 URLQueryItem(name: "categories", value: categories),47 URLQueryItem(name: "purity", value: purity),48 URLQueryItem(name: "sorting", value: sorting.rawValue),49 URLQueryItem(name: "limit", value: String(limit)),50 URLQueryItem(name: "page", value: String(page)),51 ]5253 if !resolutions.isEmpty {54 queryItems.append(55 URLQueryItem(name: "resolutions", value: resolutions.joined(separator: ",")))56 }5758 if !ratios.isEmpty {59 queryItems.append(URLQueryItem(name: "ratios", value: ratios.joined(separator: ",")))60 }6162 return try await performSearch(queryItems: queryItems)63 }6465 private func performSearch(queryItems: [URLQueryItem]) async throws -> ([Wallpaper], Meta?) {66 var urlComponents = URLComponents(67 url: Configuration.baseURL.appendingPathComponent("search"), resolvingAgainstBaseURL: true)!68 urlComponents.queryItems = queryItems6970 let url = urlComponents.url!7172 let (data, response) = try await session.data(from: url)7374 guard let httpResponse = response as? HTTPURLResponse,75 (200...299).contains(httpResponse.statusCode)76 else {77 throw WallhavenError.apiError("Server returned an error")78 }7980 do {81 let decodedResponse = try JSONDecoder().decode(WallhavenResponse.self, from: data)82 return (decodedResponse.data, decodedResponse.meta)83 } catch {84 throw WallhavenError.decodingError(error)85 }86 }87}