diff --git a/Sources/OpenFoodFacts/OpenFoodFacts.swift b/Sources/OpenFoodFacts/OpenFoodFacts.swift index d9e31ad..53a034d 100644 --- a/Sources/OpenFoodFacts/OpenFoodFacts.swift +++ b/Sources/OpenFoodFacts/OpenFoodFacts.swift @@ -45,23 +45,40 @@ public class OpenFoodFactsClient { 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") + // 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) } + } } diff --git a/Sources/OpenFoodFacts/types/Ingredient.swift b/Sources/OpenFoodFacts/types/Ingredient.swift index 88182fb..db2a9fb 100644 --- a/Sources/OpenFoodFacts/types/Ingredient.swift +++ b/Sources/OpenFoodFacts/types/Ingredient.swift @@ -3,7 +3,7 @@ public struct Ingredient: Codable, ObjectDebugger { public var id: String? = nil public var origin: String? = nil public var percent: Float? = nil - public var rank: Int? = 0 + public var rank: Float? = 0 public var text: String? = nil public var vegan: String? = nil public var vegetarian: String? = nil diff --git a/Sources/OpenFoodFacts/types/LanguagesCodes.swift b/Sources/OpenFoodFacts/types/LanguagesCodes.swift index 0e51876..1658a84 100644 --- a/Sources/OpenFoodFacts/types/LanguagesCodes.swift +++ b/Sources/OpenFoodFacts/types/LanguagesCodes.swift @@ -1,6 +1,6 @@ public struct LanguagesCodes: Codable, ObjectDebugger { - public var en: Int? = nil - public var fr: Int? = nil - public var pl: Int? = nil + public var en: Float? = nil + public var fr: Float? = nil + public var pl: Float? = nil } diff --git a/Sources/OpenFoodFacts/types/Nutriments.swift b/Sources/OpenFoodFacts/types/Nutriments.swift index 0afac78..2108697 100644 --- a/Sources/OpenFoodFacts/types/Nutriments.swift +++ b/Sources/OpenFoodFacts/types/Nutriments.swift @@ -30,7 +30,7 @@ public struct Nutriments: Codable, ObjectDebugger { public var energy100G: Float? = 0 public var energyKcal100G: Float? = 0 public var energyKj100G: Float? = 0 - public var energyServing: Int? = 0 + public var energyServing: Float? = 0 public var energyKcalServing: Double? = 0.0 public var energyKjServing: Float? = 0 public var energyUnit: String? = nil @@ -115,12 +115,6 @@ public struct Nutriments: Codable, ObjectDebugger { public var vitaminDServing: Float? = 0.0 public var vitaminDUnit: String? = nil -// public var other: [String: Any] = [:] -// -// mutating func setDetail(key: String, value: Any) { -// other[key] = value -// } - private enum CodingKeys: String, CodingKey { case calcium case calciumValue = "calcium_value" diff --git a/Sources/OpenFoodFacts/types/PerlSearchQuery.swift b/Sources/OpenFoodFacts/types/PerlSearchQuery.swift new file mode 100644 index 0000000..bd33b62 --- /dev/null +++ b/Sources/OpenFoodFacts/types/PerlSearchQuery.swift @@ -0,0 +1,48 @@ +public enum PerlOperator: String { + case lt, lte, gt, gte, eq +} + +public enum PerlFormat: String { + case json, xml, jqm +} + +public struct SearchNutrimentEntry { + public let nutriment: SearchNutriment + public let op: PerlOperator + public let value: Int +} + +public struct SearchTagsEntry { + public let tag: SearchTag + public let value: String + public let contains: Bool = true +} + +//https://wiki.openfoodfacts.org/API/Read/Search#Parameters +public struct PerlSearchQuery { + public var searchTerms: String + public var searchTags: [SearchTagsEntry]? // tagtype_i=SearchTag — i as #element (starting from 0); (tag_contains_i|tag_does_not_contain_i)=String + public var searchNutriment: [SearchNutrimentEntry]? // nutriment_i=SearchNutriment — i as #element (starting from 0); nutriment_compare_i=PerlOperator; nutriment_value_i=String + public var page: Int = 1 // Pagination + public var format: PerlFormat // json=1 | xml=1 | jqm=1 + + public func makeToRequest() -> String { + var _searchTags: String? + var _searchNutriment: String? + let _format: String = "\(format.rawValue)=1" + let _page: String = "page=\(page)" + if let tags = searchTags { + _searchTags = tags.enumerated().map { i, v in + return "tagtype_\(i)=\(v.tag.rawValue)&tag_contains_\(i)=\(v.contains ? "contains" : "does_not_contain")&tag_\(i)=\(v.value)" + }.joined(separator: "&") + } + if let nutriments = searchNutriment { + _searchNutriment = nutriments.enumerated().map { i, v in + return "nutriment_\(i)=\(v.nutriment.rawValue)&nutriment_compare_\(i)=\(v.op.rawValue)&nutriment_value_\(i)=\(v.nutriment.rawValue)" + }.joined(separator: "&") + } + + return "search_terms=\(searchTerms)\(_searchTags != nil ? "&\(_searchTags!)" : "")\(_searchNutriment != nil ? "&\(_searchNutriment!)" : "")&\(_page)&\(_format)" + + } +} diff --git a/Sources/OpenFoodFacts/types/Product.swift b/Sources/OpenFoodFacts/types/Product.swift index 1bbe2ca..93d2c3d 100644 --- a/Sources/OpenFoodFacts/types/Product.swift +++ b/Sources/OpenFoodFacts/types/Product.swift @@ -6,8 +6,8 @@ public class Product: Codable, ObjectDebugger { public var nutriments: Nutriments? = Nutriments() public var selectedImages: SelectedImages? public var sources: [Source]? = [] - public var additivesN: Int? - public var additivesOldN: Int? + public var additivesN: Float? + public var additivesOldN: Float? public var additivesOriginalTags: [String]? public var additivesOldTags: [String]? public var additivesPrevOriginalTags: [String]? @@ -36,8 +36,8 @@ public class Product: Codable, ObjectDebugger { public var code: String? public var codesTags: [String]? public var comparedToCategory: String? - public var complete: Int? - public var completedT: Int? + public var complete: Float? + public var completedT: Float? public var completeness: Double? public var conservationConditions: String? public var countries: String? @@ -46,7 +46,7 @@ public class Product: Codable, ObjectDebugger { public var countriesDebugTags: [String]? public var countriesTags: [String]? public var correctorsTags: [String]? - public var createdT: Int? + public var createdT: Float? public var creator: String? public var dataQualityBugsTags: [String]? public var dataQualityErrorsTags: [String]? @@ -64,7 +64,7 @@ public class Product: Codable, ObjectDebugger { public var entryDatesTags: [String]? public var expirationDate: String? public var expirationDateDebugTags: [String]? - public var fruitsVegetablesNuts100GEstimate: Int? + public var fruitsVegetablesNuts100GEstimate: Float? public var genericName: String? public var id: String? public var imageFrontSmallUrl: String? @@ -82,24 +82,24 @@ public class Product: Codable, ObjectDebugger { public var informersTags: [String]? public var ingredientsAnalysisTags: [String]? public var ingredientsDebug: [String?]? - public var ingredientsFromOrThatMayBeFromPalmOilN: Int? + public var ingredientsFromOrThatMayBeFromPalmOilN: Float? public var ingredientsFromPalmOilTags: [String]? - public var ingredientsFromPalmOilN: Int? + public var ingredientsFromPalmOilN: Float? public var ingredientsHierarchy: [String]? public var ingredientsIdsDebug: [String]? - public var ingredientsN: Int? + public var ingredientsN: Float? public var ingredientsNTags: [String]? public var ingredientsOriginalTags: [String]? public var ingredientsTags: [String]? public var ingredientsText: String? public var ingredientsTextDebug: String? public var ingredientsTextWithAllergens: String? - public var ingredientsThatMayBeFromPalmOilN: Int? + public var ingredientsThatMayBeFromPalmOilN: Float? public var ingredientsThatMayBeFromPalmOilTags: [String]? public var interfaceVersionCreated: String? public var interfaceVersionModified: String? public var keywords: [String]? - public var knownIngredientsN: Int? + public var knownIngredientsN: Float? public var labels: String? public var labelsHierarchy: [String]? public var labelsLc: String? @@ -114,9 +114,9 @@ public class Product: Codable, ObjectDebugger { public var lastEditDatesTags: [String]? public var lastEditor: String? public var lastImageDatesTags: [String]? - public var lastImageT: Int? + public var lastImageT: Float? public var lastModifiedBy: String? - public var lastModifiedT: Int? + public var lastModifiedT: Float? public var lc: String? public var link: String? public var linkDebugTags: [String]? @@ -130,9 +130,9 @@ public class Product: Codable, ObjectDebugger { public var netWeightUnit: String? public var netWeightValue: String? public var nutritionDataPer: String? - public var nutritionScoreWarningNoFruitsVegetablesNuts: Int? + public var nutritionScoreWarningNoFruitsVegetablesNuts: Float? public var noNutritionData: String? - public var novaGroup: Int? + public var novaGroup: Float? public var novaGroups: String? public var novaGroupDebug: String? public var novaGroupTags: [String]? @@ -145,9 +145,9 @@ public class Product: Codable, ObjectDebugger { public var nutritionDataPrepared: String? public var nutritionDataPreparedPer: String? public var nutritionGrades: String? - public var nutritionScoreBeverage: Int? + public var nutritionScoreBeverage: Float? public var nutritionScoreDebug: String? - public var nutritionScoreWarningNoFiber: Int? + public var nutritionScoreWarningNoFiber: Float? public var nutritionGradesTags: [String]? public var origins: String? public var originsDebugTags: [String]? @@ -162,7 +162,7 @@ public class Product: Codable, ObjectDebugger { public var pnnsGroups2: String? public var pnnsGroups1Tags: [String]? public var pnnsGroups2Tags: [String]? - public var popularityKey: Int? + public var popularityKey: Float? public var producerVersionId: String? public var productName: String? public var productQuantity: Float? @@ -173,11 +173,11 @@ public class Product: Codable, ObjectDebugger { public var quantity: String? public var quantityDebugTags: [String]? public var recyclingInstructionsToDiscard: String? - public var rev: Int? + public var rev: Float? public var servingQuantity: String? public var servingSize: String? public var servingSizeDebugTags: [String]? - public var sortkey: Int? + public var sortkey: Float? public var states: String? public var statesHierarchy: [String]? public var statesTags: [String]? @@ -191,7 +191,7 @@ public class Product: Codable, ObjectDebugger { public var tracesFromUser: String? public var tracesLc: String? public var tracesTags: [String]? - public var unknownIngredientsN: Int? + public var unknownIngredientsN: Float? public var unknownNutrientsTags: [String]? public var updateKey: String? public var vitaminsPrevTags: [String]? @@ -405,8 +405,8 @@ public class Product: Codable, ObjectDebugger { nutriments = try container.decodeIfPresent(Nutriments.self, forKey: .nutriments) ?? Nutriments() selectedImages = try container.decodeIfPresent(SelectedImages.self, forKey: .selectedImages) sources = try container.decodeIfPresent([Source].self, forKey: .sources) - additivesN = try container.decodeIfPresent(Int.self, forKey: .additivesN) - additivesOldN = try container.decodeIfPresent(Int.self, forKey: .additivesOldN) + additivesN = try container.decodeIfPresent(Float.self, forKey: .additivesN) + additivesOldN = try container.decodeIfPresent(Float.self, forKey: .additivesOldN) additivesOriginalTags = try container.decodeIfPresent([String].self, forKey: .additivesOriginalTags) additivesOldTags = try container.decodeIfPresent([String].self, forKey: .additivesOldTags) additivesPrevOriginalTags = try container.decodeIfPresent([String].self, forKey: .additivesPrevOriginalTags) @@ -435,8 +435,8 @@ public class Product: Codable, ObjectDebugger { code = try container.decodeIfPresent(String.self, forKey: .code) codesTags = try container.decodeIfPresent([String].self, forKey: .codesTags) comparedToCategory = try container.decodeIfPresent(String.self, forKey: .comparedToCategory) - complete = try container.decodeIfPresent(Int.self, forKey: .complete) - completedT = try container.decodeIfPresent(Int.self, forKey: .completedT) + complete = try container.decodeIfPresent(Float.self, forKey: .complete) + completedT = try container.decodeIfPresent(Float.self, forKey: .completedT) completeness = try container.decodeIfPresent(Double.self, forKey: .completeness) conservationConditions = try container.decodeIfPresent(String.self, forKey: .conservationConditions) countries = try container.decodeIfPresent(String.self, forKey: .countries) @@ -445,7 +445,7 @@ public class Product: Codable, ObjectDebugger { countriesDebugTags = try container.decodeIfPresent([String].self, forKey: .countriesDebugTags) countriesTags = try container.decodeIfPresent([String].self, forKey: .countriesTags) correctorsTags = try container.decodeIfPresent([String].self, forKey: .correctorsTags) - createdT = try container.decodeIfPresent(Int.self, forKey: .createdT) + createdT = try container.decodeIfPresent(Float.self, forKey: .createdT) creator = try container.decodeIfPresent(String.self, forKey: .creator) dataQualityBugsTags = try container.decodeIfPresent([String].self, forKey: .dataQualityBugsTags) dataQualityErrorsTags = try container.decodeIfPresent([String].self, forKey: .dataQualityErrorsTags) @@ -463,7 +463,7 @@ public class Product: Codable, ObjectDebugger { entryDatesTags = try container.decodeIfPresent([String].self, forKey: .entryDatesTags) expirationDate = try container.decodeIfPresent(String.self, forKey: .expirationDate) expirationDateDebugTags = try container.decodeIfPresent([String].self, forKey: .expirationDateDebugTags) - fruitsVegetablesNuts100GEstimate = try container.decodeIfPresent(Int.self, forKey: .fruitsVegetablesNuts100GEstimate) + fruitsVegetablesNuts100GEstimate = try container.decodeIfPresent(Float.self, forKey: .fruitsVegetablesNuts100GEstimate) genericName = try container.decodeIfPresent(String.self, forKey: .genericName) id = try container.decodeIfPresent(String.self, forKey: .id) imageFrontSmallUrl = try container.decodeIfPresent(String.self, forKey: .imageFrontSmallUrl) @@ -481,21 +481,21 @@ public class Product: Codable, ObjectDebugger { informersTags = try container.decodeIfPresent([String].self, forKey: .informersTags) ingredientsAnalysisTags = try container.decodeIfPresent([String].self, forKey: .ingredientsAnalysisTags) ingredientsDebug = try container.decodeIfPresent([String?].self, forKey: .ingredientsDebug) - ingredientsFromOrThatMayBeFromPalmOilN = try container.decodeIfPresent(Int.self, forKey: .ingredientsFromOrThatMayBeFromPalmOilN) + ingredientsFromOrThatMayBeFromPalmOilN = try container.decodeIfPresent(Float.self, forKey: .ingredientsFromOrThatMayBeFromPalmOilN) ingredientsFromPalmOilTags = try container.decodeIfPresent([String].self, forKey: .ingredientsFromPalmOilTags) - ingredientsFromPalmOilN = try container.decodeIfPresent(Int.self, forKey: .ingredientsFromPalmOilN) + ingredientsFromPalmOilN = try container.decodeIfPresent(Float.self, forKey: .ingredientsFromPalmOilN) ingredientsHierarchy = try container.decodeIfPresent([String].self, forKey: .ingredientsHierarchy) ingredientsIdsDebug = try container.decodeIfPresent([String].self, forKey: .ingredientsIdsDebug) if container.contains(.ingredientsN) { if try container.decodeNil(forKey: .ingredientsN) { ingredientsN = nil } else { - if let intValue = try? container.decode(Int.self, forKey: .ingredientsN) { + if let intValue = try? container.decode(Float.self, forKey: .ingredientsN) { ingredientsN = intValue } else if let stringValue = try? container.decode(String.self, forKey: .ingredientsN) { - ingredientsN = Int(stringValue) + ingredientsN = Float(stringValue) }else { - // If decoding as both Int and String fails, handle the error accordingly + // If decoding as both Float and String fails, handle the error accordingly throw DecodingError.dataCorruptedError( forKey: .ingredientsN, in: container, @@ -513,12 +513,12 @@ public class Product: Codable, ObjectDebugger { ingredientsText = try container.decodeIfPresent(String.self, forKey: .ingredientsText) ingredientsTextDebug = try container.decodeIfPresent(String.self, forKey: .ingredientsTextDebug) ingredientsTextWithAllergens = try container.decodeIfPresent(String.self, forKey: .ingredientsTextWithAllergens) - ingredientsThatMayBeFromPalmOilN = try container.decodeIfPresent(Int.self, forKey: .ingredientsThatMayBeFromPalmOilN) + ingredientsThatMayBeFromPalmOilN = try container.decodeIfPresent(Float.self, forKey: .ingredientsThatMayBeFromPalmOilN) ingredientsThatMayBeFromPalmOilTags = try container.decodeIfPresent([String].self, forKey: .ingredientsThatMayBeFromPalmOilTags) interfaceVersionCreated = try container.decodeIfPresent(String.self, forKey: .interfaceVersionCreated) interfaceVersionModified = try container.decodeIfPresent(String.self, forKey: .interfaceVersionModified) keywords = try container.decodeIfPresent([String].self, forKey: .keywords) - knownIngredientsN = try container.decodeIfPresent(Int.self, forKey: .knownIngredientsN) + knownIngredientsN = try container.decodeIfPresent(Float.self, forKey: .knownIngredientsN) labels = try container.decodeIfPresent(String.self, forKey: .labels) labelsHierarchy = try container.decodeIfPresent([String].self, forKey: .labelsHierarchy) labelsLc = try container.decodeIfPresent(String.self, forKey: .labelsLc) @@ -533,25 +533,45 @@ public class Product: Codable, ObjectDebugger { lastEditDatesTags = try container.decodeIfPresent([String].self, forKey: .lastEditDatesTags) lastEditor = try container.decodeIfPresent(String.self, forKey: .lastEditor) lastImageDatesTags = try container.decodeIfPresent([String].self, forKey: .lastImageDatesTags) - lastImageT = try container.decodeIfPresent(Int.self, forKey: .lastImageT) + lastImageT = try container.decodeIfPresent(Float.self, forKey: .lastImageT) lastModifiedBy = try container.decodeIfPresent(String.self, forKey: .lastModifiedBy) - lastModifiedT = try container.decodeIfPresent(Int.self, forKey: .lastModifiedT) + lastModifiedT = try container.decodeIfPresent(Float.self, forKey: .lastModifiedT) lc = try container.decodeIfPresent(String.self, forKey: .lc) link = try container.decodeIfPresent(String.self, forKey: .link) linkDebugTags = try container.decodeIfPresent([String].self, forKey: .linkDebugTags) manufacturingPlaces = try container.decodeIfPresent(String.self, forKey: .manufacturingPlaces) manufacturingPlacesDebugTags = try container.decodeIfPresent([String].self, forKey: .manufacturingPlacesDebugTags) manufacturingPlacesTags = try container.decodeIfPresent([String].self, forKey: .manufacturingPlacesTags) - maxImgid = try container.decodeIfPresent(String.self, forKey: .maxImgid) + if container.contains(.maxImgid) { + if try container.decodeNil(forKey: .maxImgid) { + maxImgid = nil + } else { + if let floatValue = try? container.decode(Float.self, forKey: .maxImgid) { + maxImgid = "\(floatValue)" + } else if let stringValue = try? container.decode(String.self, forKey: .maxImgid) { + maxImgid = stringValue + }else { + // If decoding as both Float and String fails, handle the error accordingly + throw DecodingError.dataCorruptedError( + forKey: .maxImgid, + in: container, + debugDescription: "Unable to decode maxImgid" + ) + } + + } + } else { + maxImgid = nil + } mineralsPrevTags = try container.decodeIfPresent([String].self, forKey: .mineralsPrevTags) mineralsTags = try container.decodeIfPresent([String].self, forKey: .mineralsTags) miscTags = try container.decodeIfPresent([String].self, forKey: .miscTags) netWeightUnit = try container.decodeIfPresent(String.self, forKey: .netWeightUnit) netWeightValue = try container.decodeIfPresent(String.self, forKey: .netWeightValue) nutritionDataPer = try container.decodeIfPresent(String.self, forKey: .nutritionDataPer) - nutritionScoreWarningNoFruitsVegetablesNuts = try container.decodeIfPresent(Int.self, forKey: .nutritionScoreWarningNoFruitsVegetablesNuts) + nutritionScoreWarningNoFruitsVegetablesNuts = try container.decodeIfPresent(Float.self, forKey: .nutritionScoreWarningNoFruitsVegetablesNuts) noNutritionData = try container.decodeIfPresent(String.self, forKey: .noNutritionData) - novaGroup = try container.decodeIfPresent(Int.self, forKey: .novaGroup) + novaGroup = try container.decodeIfPresent(Float.self, forKey: .novaGroup) novaGroups = try container.decodeIfPresent(String.self, forKey: .novaGroups) novaGroupDebug = try container.decodeIfPresent(String.self, forKey: .novaGroupDebug) novaGroupTags = try container.decodeIfPresent([String].self, forKey: .novaGroupTags) @@ -563,9 +583,9 @@ public class Product: Codable, ObjectDebugger { nutritionDataPrepared = try container.decodeIfPresent(String.self, forKey: .nutritionDataPrepared) nutritionDataPreparedPer = try container.decodeIfPresent(String.self, forKey: .nutritionDataPreparedPer) nutritionGrades = try container.decodeIfPresent(String.self, forKey: .nutritionGrades) - nutritionScoreBeverage = try container.decodeIfPresent(Int.self, forKey: .nutritionScoreBeverage) + nutritionScoreBeverage = try container.decodeIfPresent(Float.self, forKey: .nutritionScoreBeverage) nutritionScoreDebug = try container.decodeIfPresent(String.self, forKey: .nutritionScoreDebug) - nutritionScoreWarningNoFiber = try container.decodeIfPresent(Int.self, forKey: .nutritionScoreWarningNoFiber) + nutritionScoreWarningNoFiber = try container.decodeIfPresent(Float.self, forKey: .nutritionScoreWarningNoFiber) nutritionGradesTags = try container.decodeIfPresent([String].self, forKey: .nutritionGradesTags) origins = try container.decodeIfPresent(String.self, forKey: .origins) originsDebugTags = try container.decodeIfPresent([String].self, forKey: .originsDebugTags) @@ -580,7 +600,7 @@ public class Product: Codable, ObjectDebugger { pnnsGroups2 = try container.decodeIfPresent(String.self, forKey: .pnnsGroups2) pnnsGroups1Tags = try container.decodeIfPresent([String].self, forKey: .pnnsGroups1Tags) pnnsGroups2Tags = try container.decodeIfPresent([String].self, forKey: .pnnsGroups2Tags) - popularityKey = try container.decodeIfPresent(Int.self, forKey: .popularityKey) + popularityKey = try container.decodeIfPresent(Float.self, forKey: .popularityKey) producerVersionId = try container.decodeIfPresent(String.self, forKey: .producerVersionId) productName = try container.decodeIfPresent(String.self, forKey: .productName) purchasePlaces = try container.decodeIfPresent(String.self, forKey: .purchasePlaces) @@ -590,7 +610,7 @@ public class Product: Codable, ObjectDebugger { quantity = try container.decodeIfPresent(String.self, forKey: .quantity) quantityDebugTags = try container.decodeIfPresent([String].self, forKey: .quantityDebugTags) recyclingInstructionsToDiscard = try container.decodeIfPresent(String.self, forKey: .recyclingInstructionsToDiscard) - rev = try container.decodeIfPresent(Int.self, forKey: .rev) + rev = try container.decodeIfPresent(Float.self, forKey: .rev) if container.contains(.servingQuantity) { if try container.decodeNil(forKey: .servingQuantity) { servingQuantity = nil @@ -600,7 +620,7 @@ public class Product: Codable, ObjectDebugger { } else if let stringValue = try? container.decode(String.self, forKey: .servingQuantity) { servingQuantity = stringValue }else { - // If decoding as both Int and String fails, handle the error accordingly + // If decoding as both Float and String fails, handle the error accordingly throw DecodingError.dataCorruptedError( forKey: .servingQuantity, in: container, @@ -614,7 +634,7 @@ public class Product: Codable, ObjectDebugger { } servingSize = try container.decodeIfPresent(String.self, forKey: .servingSize) servingSizeDebugTags = try container.decodeIfPresent([String].self, forKey: .servingSizeDebugTags) - sortkey = try container.decodeIfPresent(Int.self, forKey: .sortkey) + sortkey = try container.decodeIfPresent(Float.self, forKey: .sortkey) states = try container.decodeIfPresent(String.self, forKey: .states) statesHierarchy = try container.decodeIfPresent([String].self, forKey: .statesHierarchy) statesTags = try container.decodeIfPresent([String].self, forKey: .statesTags) @@ -632,12 +652,12 @@ public class Product: Codable, ObjectDebugger { if try container.decodeNil(forKey: .unknownIngredientsN) { unknownIngredientsN = nil } else { - if let intValue = try? container.decode(Int.self, forKey: .unknownIngredientsN) { + if let intValue = try? container.decode(Float.self, forKey: .unknownIngredientsN) { unknownIngredientsN = intValue } else if let stringValue = try? container.decode(String.self, forKey: .unknownIngredientsN) { - unknownIngredientsN = Int(stringValue) + unknownIngredientsN = Float(stringValue) }else { - // If decoding as both Int and String fails, handle the error accordingly + // If decoding as both Float and String fails, handle the error accordingly throw DecodingError.dataCorruptedError( forKey: .unknownIngredientsN, in: container, @@ -659,14 +679,14 @@ public class Product: Codable, ObjectDebugger { if try container.decodeNil(forKey: .productQuantity) { productQuantity = nil } else { - // Try to decode as Int + // Try to decode as Float if let floatValue = try? container.decode(Float.self, forKey: .productQuantity) { productQuantity = floatValue } else if let stringValue = try? container.decode(String.self, forKey: .productQuantity) { - // If decoding as Int fails, try to decode as String + // If decoding as Float fails, try to decode as String productQuantity = Float(stringValue) } else { - // If decoding as both Int and String fails, handle the error accordingly + // If decoding as both Float and String fails, handle the error accordingly throw DecodingError.dataCorruptedError( forKey: .productQuantity, in: container, diff --git a/Sources/OpenFoodFacts/types/SearchNutriment.swift b/Sources/OpenFoodFacts/types/SearchNutriment.swift new file mode 100644 index 0000000..e733270 --- /dev/null +++ b/Sources/OpenFoodFacts/types/SearchNutriment.swift @@ -0,0 +1,98 @@ +public enum SearchNutriment: String, Codable { + case energy // Energy + case energyFromFat = "energy-from-fat" // Energy from fat + case fat // Fat + case saturatedFat = "saturated-fat" // Saturated fat + case butyricAcid = "butyric-acid" // Butyric acid (4:0) + case caproicAcid = "caproic-acid" // Caproic acid (6:0) + case caprylicAcid = "caprylic-acid" // Caprylic acid (8:0) + case capricAcid = "capric-acid" // Capric acid (10:0) + case lauricAcid = "lauric-acid" // Lauric acid (12:0) + case myristicAcid = "myristic-acid" // Myristic acid (14:0) + case palmiticAcid = "palmitic-acid" // Palmitic acid (16:0) + case stearicAcid = "stearic-acid" // Stearic acid (18:0) + case arachidicAcid = "arachidic-acid" // Arachidic acid (20:0) + case behenicAcid = "behenic-acid" // Behenic acid (22:0) + case lignocericAcid = "lignoceric-acid" // Lignoceric acid (24:0) + case ceroticAcid = "cerotic-acid" // Cerotic acid (26:0) + case montanicAcid = "montanic-acid" // Montanic acid (28:0) + case melissicAcid = "melissic-acid" // Melissic acid (30:0) + case monounsaturatedFat = "monounsaturated-fat" // Monounsaturated fat + case polyunsaturatedFat = "polyunsaturated-fat" // Polyunsaturated fat + case omega3Fat = "omega-3-fat" // Omega 3 fatty acids + case alphaLinolenicAcid = "alpha-linolenic-acid" // Alpha-linolenic acid / ALA (18:3 n-3) + case eicosapentaenoicAcid = "eicosapentaenoic-acid" // Eicosapentaenoic acid / EPA (20:5 n-3) + case docosahexaenoicAcid = "docosahexaenoic-acid" // Docosahexaenoic acid / DHA (22:6 n-3) + case omega6Fat = "omega-6-fat" // Omega 6 fatty acids + case linoleicAcid = "linoleic-acid" // Linoleic acid / LA (18:2 n-6) + case arachidonicAcid = "arachidonic-acid" // Arachidonic acid / AA / ARA (20:4 n-6) + case gammaLinolenicAcid = "gamma-linolenic-acid" // Gamma-linolenic acid / GLA (18:3 n-6) + case dihomoGammaLinolenicAcid = "dihomo-gamma-linolenic-acid" // Dihomo-gamma-linolenic acid / DGLA (20:3 n-6) + case omega9Fat = "omega-9-fat" // Omega 9 fatty acids + case oleicAcid = "oleic-acid" // Oleic acid (18:1 n-9) + case elaidicAcid = "elaidic-acid" // Elaidic acid (18:1 n-9) + case gondoicAcid = "gondoic-acid" // Gondoic acid (20:1 n-9) + case meadAcid = "mead-acid" // Mead acid (20:3 n-9) + case erucicAcid = "erucic-acid" // Erucic acid (22:1 n-9) + case nervonicAcid = "nervonic-acid" // Nervonic acid (24:1 n-9) + case transFat = "trans-fat" // Trans fat + case cholesterol // Cholesterol + case carbohydrates // Carbohydrate + case sugars // Sugars + case sucrose // Sucrose + case glucose // Glucose + case fructose // Fructose + case lactose // Lactose + case maltose // Maltose + case maltodextrins // Maltodextrins + case starch // Starch + case polyols // Sugar alcohols (Polyols) + case fiber // Dietary fiber + case proteins // Proteins + case casein // Casein + case serumProteins = "serum-proteins" // Serum proteins + case nucleotides // Nucleotides + case salt // Salt + case sodium // Sodium + case alcohol // Alcohol + case vitaminA = "vitamin-a" // Vitamin A + case betaCarotene = "beta-carotene" // Beta carotene + case vitaminD = "vitamin-d" // Vitamin D + case vitaminE = "vitamin-e" // Vitamin E + case vitaminK = "vitamin-k" // Vitamin K + case vitaminC = "vitamin-c" // Vitamin C (ascorbic acid) + case vitaminB1 = "vitamin-b1" // Vitamin B1 (Thiamin) + case vitaminB2 = "vitamin-b2" // Vitamin B2 (Riboflavin) + case vitaminPP = "vitamin-pp" // Vitamin B3 / Vitamin PP (Niacin) + case vitaminB6 = "vitamin-b6" // Vitamin B6 (Pyridoxin) + case vitaminB9 = "vitamin-b9" // Vitamin B9 (Folic acid / Folates) + case vitaminB12 = "vitamin-b12" // Vitamin B12 (Cobalamin) + case biotin // Biotin + case pantothenicAcid = "pantothenic-acid" // Pantothenic acid / Pantothenate (Vitamin B5) + case silica // Silica + case bicarbonate // Bicarbonate + case potassium // Potassium + case chloride // Chloride + case calcium // Calcium + case phosphorus // Phosphorus + case iron // Iron + case magnesium // Magnesium + case zinc // Zinc + case copper // Copper + case manganese // Manganese + case fluoride // Fluoride + case selenium // Selenium + case chromium // Chromium + case molybdenum // Molybdenum + case iodine // Iodine + case caffeine // Caffeine + case taurine // Taurine + case pH // pH + case fruitsVegetablesNuts = "fruits-vegetables-nuts" // Fruits, vegetables, and nuts (minimum) + case collagenMeatProteinRatio = "collagen-meat-protein-ratio" // Collagen/Meat protein ratio (maximum) + case cocoa // Cocoa (minimum) + case chlorophyll = "chlorophyll" // Chlorophyll + case carbonFootprint = "carbon-footprint" // Carbon footprint / CO2 emissions + case nutritionScoreFR = "nutrition-score-fr" // Experimental nutrition score + case nutritionScoreUK = "nutrition-score-uk" // Nutrition score - UK +} diff --git a/Sources/OpenFoodFacts/types/SearchTag.swift b/Sources/OpenFoodFacts/types/SearchTag.swift new file mode 100644 index 0000000..4cc9f0b --- /dev/null +++ b/Sources/OpenFoodFacts/types/SearchTag.swift @@ -0,0 +1,17 @@ +public enum SearchTag: String, Codable { + case brands // Brands + case categories // Categories + case packaging // Packaging + case labels // Labels + case origins // Origins of ingredients + case manufacturingPlaces = "manufacturing_places" // Manufacturing or processing places + case embCodes = "emb_codes" // Packager codes + case purchasePlaces = "purchase_places" // Purchase places + case stores // Stores + case countries // Countries + case additives // Additives + case allergens // Allergens + case traces // Traces + case nutritionGrades = "nutrition_grades" // Nutrition grades + case states // States +} diff --git a/Sources/exe/main.swift b/Sources/exe/main.swift index f36b634..7422495 100644 --- a/Sources/exe/main.swift +++ b/Sources/exe/main.swift @@ -4,10 +4,10 @@ import OpenFoodFacts let off = OpenFoodFactsClient() off.prod = true -do { +// do { - let res = try await off.search("Coca-Cola") - print(res) -} catch { - print(error) -} +// // let res = try await off.search("Coca-Cola") +// // print(res) +// } catch { +// print(error) +// } diff --git a/Tests/OpenFoodFactsTests/OpenFoodFactsTests.swift b/Tests/OpenFoodFactsTests/OpenFoodFactsTests.swift index 613de3d..bbb9d87 100644 --- a/Tests/OpenFoodFactsTests/OpenFoodFactsTests.swift +++ b/Tests/OpenFoodFactsTests/OpenFoodFactsTests.swift @@ -14,4 +14,18 @@ final class swift_openfoodfacts_sdkTests: XCTestCase { } } } + + func testPerlSearch() async throws { + let off = OpenFoodFactsClient() + do { + // try await off.search(query: .init(searchTerms: "prince", format: .json)) + let _ = try await off.search(query: .init( + searchTerms: "", + searchTags: [.init(tag: .brands, value: "mondelez"), .init(tag: .countries, value: "france")], + format: .json + )) + } catch { + XCTFail("\(error)") + } + } }