Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/OpenFoodFactsSDK.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1540"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "OpenFoodFactsSDK"
BuildableName = "OpenFoodFactsSDK"
BlueprintName = "OpenFoodFactsSDK"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "OpenFoodFactsSDK-iosTests"
BuildableName = "OpenFoodFactsSDK-iosTests"
BlueprintName = "OpenFoodFactsSDK-iosTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "OpenFoodFactsSDK"
BuildableName = "OpenFoodFactsSDK"
BlueprintName = "OpenFoodFactsSDK"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
6 changes: 3 additions & 3 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,7 @@ You can check the terms of use here : [Terms of use](https://world.openfoodfacts
If you use this SDK, feel free to open a PR to add your application in this list.

## Authors
This project is sponsored by [FoodIntake](https://foodintake.space)

![Foodintake-AI-Calorie-Pal-Reddit](https://github.com/hrabkin/openfoodfacts-swift/assets/2230377/60a84e1e-2d16-42a2-9694-4d673cd67b95)

60 changes: 60 additions & 0 deletions Sources/Extensions/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,63 @@ public extension [String: String] {
}
}
}

extension UIImage {

func resized(toMaxSize maxSize: CGFloat = 1000.0) -> UIImage? {
guard let image = self.normalizedImage() else { return nil }

let width = image.size.width
let height = image.size.height
let aspectRatio = width / height

var newWidth: CGFloat
var newHeight: CGFloat

if width <= height {
// Portrait or square
newHeight = min(height, maxSize)
newWidth = newHeight * aspectRatio
if newWidth > maxSize {
newWidth = maxSize
newHeight = newWidth / aspectRatio
}
} else {
// Landscape
newWidth = min(width, maxSize)
newHeight = newWidth / aspectRatio
if newHeight > maxSize {
newHeight = maxSize
newWidth = newHeight * aspectRatio
}
}

let newSize = CGSize(width: newWidth, height: newHeight)
UIGraphicsBeginImageContextWithOptions(newSize, false, image.scale)
defer { UIGraphicsEndImageContext() }

image.draw(in: CGRect(origin: .zero, size: newSize))
return UIGraphicsGetImageFromCurrentImageContext()
}

/// `Re-orientate` the image to `up`.
func normalizedImage() -> UIImage?
{
if self.imageOrientation == .up
{
return self
}
else
{
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
defer
{
UIGraphicsEndImageContext()
}

self.draw(in: CGRect(origin: .zero, size: self.size))

return UIGraphicsGetImageFromCurrentImageContext()
}
}
}
2 changes: 1 addition & 1 deletion Sources/Model/OFF/OpenFoodFactsLanguage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ public enum OpenFoodFactsLanguage: String, CaseIterable, Identifiable, Equatable
case ZULU
case UNDEFINED

var info: (code: String, description: String) {
public var info: (code: String, description: String) {
switch self {
case .ENGLISH:
return ("en", "English")
Expand Down
66 changes: 53 additions & 13 deletions Sources/Model/OFF/Product.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,37 +46,68 @@ public struct Product: Codable, Equatable, Sendable {
public let brands: String?
public let lang: OpenFoodFactsLanguage?
public let quantity: String?
public let packagingQuantity: Double?
public let packagingQuantity: Double
public let packagingQuantityUnit: String
public let servingSize: String?
public let servingQuantity: Double?
public let servingQuantity: Double
public let servingQuantityUnit: String
public let dataPer: String?
public let categories: String?
public var nutriments: [String: Any]?
public let imageFront: String?
public let imageIngredients: String?
public let imageNutrition: String?
public let keywords: [String]?
public let novaGroup: Double?
public let nutriScore: String?

enum CodingKeys: String, CodingKey {
public enum CodingKeys: String, CodingKey {
case code
case lang
case brands
case quantity
case packagingQuantity = "product_quantity"
case packagingQuantityUnit = "product_quantity_unit"
case categories
case images
case productName = "product_name"
case productNameEn = "product_name_en"
case servingSize = "serving_size"
case servingQuantity = "serving_quantity"
case servingQuantityUnit = "serving_quantity_unit"
case dataPer = "nutrition_data_per"
case nutriments = "nutriments"
case imageFront = "image_front_url"
case imageIngredients = "image_ingredients_url"
case imageNutrition = "image_nutrition_url"
case novaGroup = "nova_group"
case nutriScore = "nutriscore_grade"
case keywords = "_keywords"
}

public init(code: String, productName: String? = nil, productNameEn: String? = nil, brands: String? = nil, lang: OpenFoodFactsLanguage = .ENGLISH, quantity: String? = nil, packagingQuantity: Double = 100, packagingQuantityUnit: String = "g", servingSize: String? = nil, servingQuantity: Double = 100, servingQuantityUnit: String = "g", dataPer: String? = nil, categories: String? = nil, nutriments: [String: Any]? = nil, imageFront: String? = nil, imageIngredients: String? = nil, imageNutrition: String? = nil, keywords: [String]? = nil, novaGroup: Double? = nil, nutriScore: String? = nil) {
self.code = code
self.productName = productName
self.productNameEn = productNameEn
self.brands = brands
self.lang = lang
self.quantity = quantity
self.packagingQuantity = packagingQuantity
self.packagingQuantityUnit = packagingQuantityUnit
self.servingSize = servingSize
self.servingQuantity = servingQuantity
self.servingQuantityUnit = servingQuantityUnit
self.dataPer = dataPer
self.categories = categories
self.nutriments = nutriments
self.imageFront = imageFront
self.imageIngredients = imageIngredients
self.imageNutrition = imageNutrition
self.keywords = keywords
self.novaGroup = novaGroup
self.nutriScore = nutriScore
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
code = try container.decode(String.self, forKey: .code)
Expand All @@ -91,23 +122,28 @@ public struct Product: Codable, Equatable, Sendable {
imageIngredients = try container.decodeIfPresent(String.self, forKey: .imageIngredients)
imageNutrition = try container.decodeIfPresent(String.self, forKey: .imageNutrition)
keywords = try container.decodeIfPresent([String].self, forKey: .keywords)
nutriScore = try container.decodeIfPresent(String.self, forKey: .nutriScore)
novaGroup = try container.decodeIfPresent(Double.self, forKey: .novaGroup)
servingQuantityUnit = try container.decodeIfPresent(String.self, forKey: .servingQuantityUnit) ?? "g"
packagingQuantityUnit = try container.decodeIfPresent(String.self, forKey: .packagingQuantityUnit) ?? "g"

if let packagingQuantityValue = try? container.decode(Double.self, forKey: .packagingQuantity) {
packagingQuantity = packagingQuantityValue
} else if let packagingQuantityString = try? container.decode(String.self, forKey: .packagingQuantity),
let packagingQuantityValue = Double(packagingQuantityString) {
packagingQuantity = packagingQuantityValue
} else if let packagingQuantityString = try? container.decode(String.self, forKey: .packagingQuantity) {
let cleanedString = packagingQuantityString.filter { $0.isNumber || $0 == "." || $0 == "," }
packagingQuantity = Double(cleanedString) ?? 0.0

} else {
packagingQuantity = nil
packagingQuantity = 100
}

if let servingQuantityValue = try? container.decode(Double.self, forKey: .servingQuantity) {
servingQuantity = servingQuantityValue
} else if let servingQuantityString = try? container.decode(String.self, forKey: .servingQuantity),
let servingQuantityValue = Double(servingQuantityString) {
servingQuantity = servingQuantityValue
} else if let servingQuantityString = try? container.decode(String.self, forKey: .servingQuantity) {
let cleanedString = servingQuantityString.filter { $0.isNumber || $0 == "." || $0 == "," }
servingQuantity = Double(cleanedString) ?? 0.0
} else {
servingQuantity = nil
servingQuantity = 100
}

if let nutrimentsContainer = try? container.nestedContainer(keyedBy: AnyCodingKey.self, forKey: .nutriments) {
Expand Down Expand Up @@ -136,15 +172,19 @@ public struct Product: Codable, Equatable, Sendable {
try container.encodeIfPresent(productNameEn, forKey: .productNameEn)
try container.encodeIfPresent(quantity, forKey: .quantity)
try container.encodeIfPresent(packagingQuantity, forKey: .packagingQuantity)
try container.encodeIfPresent(packagingQuantityUnit, forKey: .packagingQuantityUnit)
try container.encodeIfPresent(servingSize, forKey: .servingSize)
try container.encodeIfPresent(servingQuantity, forKey: .servingQuantity)
try container.encodeIfPresent(servingQuantityUnit, forKey: .servingQuantityUnit)
try container.encodeIfPresent(dataPer, forKey: .dataPer)
try container.encodeIfPresent(categories, forKey: .categories)
try container.encodeIfPresent(imageFront, forKey: .imageFront)
try container.encodeIfPresent(imageIngredients, forKey: .imageIngredients)
try container.encodeIfPresent(imageNutrition, forKey: .imageNutrition)
try container.encodeIfPresent(keywords, forKey: .keywords)
try container.encodeIfPresent(self.lang?.rawValue, forKey: .lang)
try container.encodeIfPresent(lang?.rawValue, forKey: .lang)
try container.encodeIfPresent(nutriScore, forKey: .nutriScore)
try container.encodeIfPresent(novaGroup, forKey: .novaGroup)

if let nutriments = self.nutriments {
var nutrimentsContainer = container.nestedContainer(keyedBy: AnyCodingKey.self, forKey: .nutriments)
Expand Down
2 changes: 1 addition & 1 deletion Sources/Model/OFF/ProductConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public struct ProductQueryConfiguration {
var languages: [OpenFoodFactsLanguage]
var fields: [ProductField]?

init(barcode: String,
public init(barcode: String,
languages: [OpenFoodFactsLanguage] = [],
country: OpenFoodFactsCountry? = nil,
fields: [ProductField]? = nil) {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Model/OFF/ProductField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

enum ProductField: String {
public enum ProductField: String {
case barcode = "code"
case name = "product_name"
case nameInLanguages = "product_name_"
Expand Down
Loading
Loading