import Foundation public struct OpenFoodFactsConfig: Sendable { public let baseURL: URL public let userAgent: UserAgent public let apiURL: URL public let searchURL: URL public struct UserAgent: CustomStringConvertible, Sendable { public let appName: String public let version: String public let contactInfo: String /// AppName/Version (Contact info) public var description: String { "\(appName)/\(version) (\(contactInfo))" } public init(appName: String, version: String, contactInfo: String) { self.appName = appName self.version = version self.contactInfo = contactInfo } public init?(from userAgent: String) { guard userAgent.count > 0 else { return nil } let splitted = userAgent.split(separator: " ") guard splitted.count == 2 else { return nil } let appNameVersion = splitted[0].split(separator: "/") guard appNameVersion.count == 2 else { return nil } self.appName = String(appNameVersion[0]) self.version = String(appNameVersion[1]) self.contactInfo = String(splitted[1]) } } public enum Environment: Sendable { case production case staging var url: URL { switch self { case .production: return URL(string: "https://world.openfoodfacts.org")! case .staging: return URL(string: "https://world.openfoodfacts.net")! } } } /// - Parameters: /// - userAgent: Crucial for OFF. Format: "AppName/Version (Contact info)" public init(environment: Environment = .production, userAgent: UserAgent) { self.baseURL = environment.url self.userAgent = userAgent self.apiURL = self.baseURL.appendingPathComponent("/api/v2") self.searchURL = URL(string: "https://search.openfoodfacts.org/search")! } }