Initial commit

This commit is contained in:
cdricms
2023-12-14 18:32:48 +01:00
parent c3d7bb8a78
commit 7f6d38f471
6 changed files with 316 additions and 0 deletions

12
Sources/exe/main.swift Normal file
View File

@@ -0,0 +1,12 @@
import Foundation
import swift_openfoodfacts_sdk
let off = OpenFoodFactsClient()
print("Hello world")
do {
let res = try await off.getProductByBarcode("3017620422003")
print(res)
} catch {
print("\(error)")
}

View File

@@ -0,0 +1,255 @@
// The Swift Programming Language
// https://docs.swift.org/swift-book
import Foundation
public class OpenFoodFactsClient {
let version: Int = 2
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 -> Result {
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(Result.self, from: data)
}
}
public struct Result: Codable {
var code: String
var status: Int
var status_verbose: String
var product: Product
}
public struct Product: Codable {
var abbreviated_product_name: String // Abbreviated name in requested language
var code: String // barcode of the product (can be EAN-13 or internal codes for some food stores), for products without a barcode, Open Food Facts assigns a number starting with the 200 reserved prefix
var codes_tags: [String] // A value which is the type of barcode "code-13" or "code-8" and A series of mask for the barcode It helps retrieve barcodes starting by ]
var generic_name: String // Legal name of the product as regulated by the European authorities.
var id: String // internal identifier for the product, usually set to the value of code, except on the producers platform where it is prefixed by the owner
var lc: String // Main language of the product. This is a duplicate of lang property (for historical reasons).
var lang: String // Main language of the product.
// This should be the main language of product packaging (if one is predominant).
// Main language is also used to decide which ingredients list to parse.
var nova_group: Int // Nova group as an Int from 1 to 4. See https://world.openfoodfacts.org/nova
var nova_groups: String
var obsolete: String
var obsolete_since_date: String // A date at which the product was declared obsolete. This means it's not produced any more.
var product_name: String // The name of the product
var product_name_en: String // The name of the product can also be in many other languages like product_name_fr (for French).
var product_quantity: String // The size in g or ml for the whole product. It's a normalized version of the quantity field.
var quantity: String // Quantity and Unit.
var additives_n: Int // Number of food additives.
var checked: String
var complete: Int
var completeness: Float
var food_groups: String
var food_groups_tags: [String]
var nutrient_levels: NutrientLevel
var pnns_groups_1: String // Category of food according to French Nutrition and Health Program
var pnns_groups_1_tags: [String]
var pnns_groups_2: String // Sub Category of food according to French Nutrition and Health Program
var pnns_groups_2_tags: [String]
var popularity_key: Int
var popularity_tags: [String]
var scans_n: Int
var unique_scans_n: Int
var serving_quantity: String // Normalized version of serving_size.Note that this is NOT the number of servings by product.(in perl, see normalize_serving_size)
var serving_size: String // Serving size text(generally in g or ml).We expect a quantity + unit but the user is free to input any string.
var brands: String
var brands_tags: [String]
var categories: String
var categories_hierarchy: [String]
var categories_lc: String
var categories_tags: [String]
var checkers_tags: [String]
var countries: String // List of countries where the product is sold.
var countries_hierarchy: [String]
var countries_lc: String
var countries_tags: [String]
var manufacturing_places: String
var nutrient_levels_tags: [String]
var image_front_small_url: String
var image_front_thumb_url: String
var image_front_url: String
var image_nutrition_small_url: String
var image_nutrition_thumb_url: String
var image_nutrition_url: String
var image_small_url: String
var image_thumb_url: String
var image_url: String
var ingredients: [Ingredient]
var nutriments: Nutrition.Nutriments
public struct NutrientLevel: Codable {
var fat: TrafficLightIndicator
var salt: TrafficLightIndicator
var saturated_fat: TrafficLightIndicator
var sugars: TrafficLightIndicator
public enum TrafficLightIndicator: String, Codable {
case low
case moderate
case high
}
enum CodingKeys: String, CodingKey {
case fat, salt, sugars
case saturated_fat = "saturated-fat"
}
}
public class Ingredient: Codable {
var additives_tags: [String]?
var allergens: String?
var allergens_lc: String?
var allergens_hierarchy: [String]?
var allergens_tags: [String]?
var id: String
var percent_estimate: Float?
var percent_max: Float?
var percent_min: Float?
var text: String?
var vegan: String?
var vegetarian: String?
var ingredients: [Ingredient]?
var ingredients_analysis: IngredientAnalysis?
var ingredients_analysis_tags: [String]?
var ingredients_from_or_that_may_be_from_palm_oil_n: Int?
var ingredients_from_palm_oil_n: Int?
var ingredients_hierarchy: [String]?
var ingredients_n: Int?
var ingredients_n_tags: [String]?
var ingredients_original_tags: [String]?
var ingredients_percent_analysis: Int?
var ingredients_tags: [String]?
var ingredients_lc: String? // Language that was used to parse the ingredient list. If ingredients_text is available for the product main language (lang), ingredients_lc=lang, otherwise we look at ingredients_text fields for other languages and set ingredients_lc to the first non-empty ingredient_text.
var ingredients_text: String? // Raw list of ingredients. This will get automatically parsed and get used to compute the Eco-Score or find allergens, etc..
// It's a copy of ingredients_text in the main language of the product (see lang proprety).
var ingredients_text_with_allergens: String? // Same text as ingredients_text but where allergens have HTML elements around them to identify them
var ingredients_that_may_be_from_palm_oil_n: Int?
var ingredients_with_specified_percent_n: Int?
var ingredients_with_specified_percent_sum: Int?
var ingredients_with_unspecified_percent_n: Int?
var ingredients_with_unspecified_percent_sum: Int?
var known_ingredients_n: Int?
var origins: String?
var origins_lc: String?
var traces: String?
var unknown_ingredients_n: Int?
}
public struct IngredientAnalysis: Codable {
var palmOil: [String]?
var vegan: [String]?
var vegetarian: [String]?
enum CodingKeys: String, CodingKey {
case palmOil = "en:palm-oil"
case vegan = "en:vegan-status-unknown"
case vegetarian = "en:vegetarian-status-unknown"
}
}
public struct Nutrition: Codable {
var no_nutrition_data: String? // When a product does not have nutrition data displayed on the packaging, the user can check the field "Nutrition facts are not specified on the product". By doing so, the no_nutrition_data field takes the value "on". This case is frequent (thousands of products).
var nutrition_data_per: ServingKind // The nutrition data on the package can be per serving or per 100g.
// This is essential to understand if <nutrient>_value and <nutrient> values in nutriments applies for a serving or for 100g.
// IMPORTANT: When writing products, this setting applies to all existing nutrients values for the product, not only the nutrient values sent in the write request. So it should not be changed unless all nutrients values are provided with values that match the nutrition_data_per field.
// Allowed: serving100g
var nutrition_data_prepared_per: ServingKind
public enum ServingKind: String, Codable {
case serving
case hundredGrams = "100g"
}
var nutriments: Nutriments
public struct Nutriments: Codable {
var alcohol: Float // Quantity of alcohol (per 100g or per serving) in a standard unit (g or ml)
var carbohydrates: Float
var energy: Float // It is the same as energy-kj if we have it, or computed from energy-kcal otherwise (per 100g or per serving) in kj
var energy_value: Float // energy_value will be equal to energy-kj_value if we have it or to energy-kcal_value otherwise
var energy_unit: EnergyUnit // Equal to energy-kj_unit if we have it or to energy-kcal_unit otherwise Allowed: kcalkj
var energyKcal: Float // energy-kcal, energy in kcal, if it is specified (per 100g or per serving) in a standard unit (g or ml)
var energyKj: Float // energy in kj, if it is specified (per 100g or per serving) in a standard unit (g or ml)
var fat: Float
var fruitsVegetablesLegumesEstimateFromIngredients: Float // An estimate, from the ingredients list of the percentage of fruits, vegetable and legumes. This is an important information for Nutri-Score (2023 version) computation.
var fruitsVegetablesNutsEstimateFromIngredients: Float
var proteins: Float
var salt: Float
var saturatedFat: Float
var sodium: Float
var sugars: Float
var erythritol: Float?
// pattern: (?<nutrient>[\w-]+)_unit]: enum
// The unit in which the nutrient for 100g or per serving is measured.
// The possible values depends on the nutrient.
// g for grams
// mg for milligrams
// μg for micrograms
// cl for centiliters
// ml for mililiters
// dv for recommended daily intakes (aka Dietary Reference Intake)
// % vol for alcohol vol per 100 ml
// 🤓 code: see the Units module, and Food:default_unit_for_nid function
// Allowed: kgкгlлmgмгmcgµgozfl ozdlдлclклgkjгмлmlmmol/l% volph%% dv% vol (alcohol)iumol/lmval/lppm<EFBFBD>rh<EFBFBD>fh<EFBFBD>e<EFBFBD>dhgpg
// [pattern: (?<nutrient>[\w-]+)_100g]: number
// The standardized value of a serving of 100g (or 100ml for liquids) for the nutrient.
// This is computed from the nutrient property, the serving size (if needed), and the nutrient_unit field.
// Note: If you want to characterize products in a uniform way, this is the value you should use.
// [pattern: (?<nutrient>[\w-]+)_serving]: number
// The standardized value of a serving for this product.
// [pattern: (?<nutrient>[\w-]+)_value]: number
// The value input by the user / displayed on the product for the nutrient.
// per 100g or serving, depending on nutrition_data_per
// in the unit of corresponding _unit field.
// [pattern: (?<nutrient>[\w-]+)_prepared]: number
// The value for nutrient for prepared product.
// [pattern: (?<nutrient>[\w-]+)_prepared_unit]: string
// The unit in which the nutrient of prepared product is measured.
// [pattern: (?<nutrient>[\w-]+)_prepared_100g]: number
// The standardized value of a serving of 100g (or 100ml for liquids) for the nutrient, for prepared product.
// [pattern: (?<nutrient>[\w-]+)_prepared_serving]: number
// The standardized value of a serving for the prepared product.
// [pattern: (?<nutrient>[\w-]+)_prepared_value]: number
public enum EnergyUnit: String, Codable {
case kcal, kJ
}
enum CodingKeys: String, CodingKey {
case alcohol, carbohydrates, energy, energy_value, energy_unit, fat, proteins, salt, sodium, sugars, erythritol
case energyKcal = "energy-kcal"
case energyKj = "energy-kj"
case fruitsVegetablesLegumesEstimateFromIngredients = "fruits-vegetables-legumes-estimate-from-ingredients"
case fruitsVegetablesNutsEstimateFromIngredients = "fruits-vegetables-nuts-estimate-from-ingredients"
case saturatedFat = "saturated-fat"
}
}
}
}
enum OFFError: Error {
case invalidURL, invalidResponse
}
}