// The Swift Programming Language // https://docs.swift.org/swift-book import Foundation public class OpenFoodFactsClient { let version: Int = 0 public var prod: Bool = false var baseURL: URL? { if prod { return URL( string: "https://world.openfoodfacts.org/api/v\(version)") } else { return URL( string: "https://world.openfoodfacts.net/api/v\(version)") } } public init() {} public func getProductByBarcode(_ barcode: String) async throws -> ProductResponse { guard let endpoint = baseURL?.appendingPathComponent("product/\(barcode)") else { throw OFFError.invalidURL } var request = URLRequest(url: endpoint) request.setValue("application/json", forHTTPHeaderField: "accept") let (data, response) = try await URLSession.shared.data(for: request) guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { throw OFFError.invalidResponse } do { return try JSONDecoder().decode(ProductResponse.self, from: data) } } public struct SearchQuery { public let additivesTags: String? public let allergensTags: String? public let brandsTags: String? public let categoriesTags: String? public let countriesTagsEn: String? public let embCodesTags: String? public let labelsTags: String? public let manufacturingPlacesTags: String? public let nutritionGradesTags: String? public let originsTags: String? public let packagingTagsDe: String? public let purchasePlacesTags: String? } // public func search(_ productName: String, queryParams: SearchQuery? = nil) async throws -> SearchResponse { // let qp = Mirror(reflecting: queryParams ?? {}) // var s: String = "?product_name=\(productName)&" // for case let (label?, value) in qp.children { // s += label.camelCaseToSnakeCase() + "=" + (value as! String) + "&" // } // guard let endpoint = baseURL?.appendingPathComponent("search\(s)") else { throw OFFError.invalidURL } // var request = URLRequest(url: endpoint) // request.setValue("application/json", forHTTPHeaderField: "accept") // let (data, response) = try await URLSession.shared.data(for: request) // guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { // throw OFFError.invalidResponse // } // do { // return try JSONDecoder().decode(SearchResponse.self, from: data) // } // } //https://wiki.openfoodfacts.org/API/Read/Search#Parameters public func search(query: PerlSearchQuery) async throws -> SearchResponse { let endpoint = URL( string: "https://world.openfoodfacts.org/cgi/search.pl?\(query.makeToRequest())" )! print(endpoint) let request = URLRequest(url: endpoint) // request.setValue("application/json", forHTTPHeaderField: "accept") let (data, response) = try await URLSession.shared.data(for: request) guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { throw OFFError.invalidResponse } do { return try JSONDecoder().decode(SearchResponse.self, from: data) } } } enum OFFError: Error { case invalidURL, invalidResponse }