"What to cook from what's in the fridge" — a classic problem with limited inventory. An AI assistant isn't just a recipe search engine; it's a recipe generator for a specific set of ingredients, dietary restrictions, and cooking time. We handle the end-to-end implementation of such an assistant: from design to app store publication.
In practice: a user opens the app, takes a photo of their fridge shelf, and receives three meal options considering time and diet. Time from photo to recipe: under 5 seconds. We've implemented such solutions for dozens of clients, reducing recipe search time by an average of 40%.
Problems the AI Assistant Solves
Don't know what to cook from leftovers?
Ingredients are running out, and ideas are scarce. The AI assistant scans fridge contents (text, voice, or photo) and generates a recipe using only available ingredients. Basic staples (salt, oil, water) are added automatically.
Allergies and diets: How to avoid mistakes?
A wrong dish choice can cost health. We implement a flexible filter system: users specify allergies (nuts, gluten, lactose) and diet (vegan, keto, paleo), and the prompt guarantees exclusion of forbidden ingredients. The model returns JSON with a full ingredient list for verification.
Cooking time: How to meet a 30-minute deadline?
Busy professionals value quick recipes. The assistant respects a maximum cooking time and selects dishes that can actually be prepared within that period. Every step is timed, and a timer is built into the recipe card.
How We Do It: Stack and Approach
Ingredient Recognition
Three input channels, all needed. Text is obvious. Voice via SFSpeechRecognizer on iOS or SpeechRecognizer on Android. The most useful: a photo of fridge contents with ingredient recognition.
For recognition, we use on-device models: CoreML on iOS and ML Kit on Android. This is faster and more private than cloud solutions. For example, CoreML with a model fine-tuned on Food-101 processes an image in 200-300 ms, 3× faster than Cloud Vision API (excluding network latency).
// iOS - ingredient recognition via Vision + CoreML
func recognizeIngredients(in image: UIImage) async throws -> [String] {
guard let cgImage = image.cgImage else { return [] }
let model = try FoodClassifier(configuration: .init())
let vnModel = try VNCoreMLModel(for: model.model)
let request = VNCoreMLRequest(model: vnModel)
request.imageCropAndScaleOption = .centerCrop
let handler = VNImageRequestHandler(cgImage: cgImage)
try handler.perform([request])
guard let results = request.results as? [VNClassificationObservation] else { return [] }
return results
.filter { $0.confidence > 0.6 }
.prefix(10)
.map { $0.identifier }
}
On Android, we use ML Kit ImageLabeler with a local model (RemoteModel downloaded on first launch). The choice between on-device and cloud is a speed-accuracy trade-off. We recommend on-device for basic items and cloud for rare ingredients.
Comparison of approaches:
| Parameter | On-device (CoreML/ML Kit) | Cloud (Vision API) |
|---|---|---|
| Speed | 200-300 ms | 500-1500 ms + network |
| Privacy | Data stays on device | Data sent to server |
| Accuracy | ~85% for basic items | ~95% for rare ingredients |
| Cost | Free | Pay per request |
Technical details of recognition integration
For iOS, we use Vision + CoreML with a FoodClassifier model (fine-tuned on Food-101). Privacy is ensured by on-device processing. For Android, ML Kit ImageLabeling with a local model.Recipe Generation with Constraints
The prompt is key. We build it dynamically, adding restrictions from the user profile. response_format: json_object is mandatory — parsing markdown-wrapped JSON in production is not viable.
struct RecipeRequest: Encodable {
let ingredients: [String]
let servings: Int
let maxCookingMinutes: Int
let dietaryRestrictions: [String] // "vegan", "gluten-free", "nut-allergy", ...
let difficulty: String // "easy", "medium", "hard"
let cuisinePreferences: [String] // optional
}
func buildRecipePrompt(_ req: RecipeRequest) -> String {
"""
Create a recipe using ONLY these ingredients (you may add basic pantry staples: salt, oil, water, common spices):
Available: \(req.ingredients.joined(separator: ", "))
Requirements:
- Servings: \(req.servings)
- Max cooking time: \(req.maxCookingMinutes) minutes
- Dietary: \(req.dietaryRestrictions.isEmpty ? "none" : req.dietaryRestrictions.joined(separator: ", "))
- Difficulty: \(req.difficulty)
Return JSON: {name, cookingTime, servings, ingredients: [{name, amount, unit}], steps: [{number, instruction, duration}], nutrition: {calories, protein, carbs, fat}}
"""
}
Recipe Card in UI
// Android Compose
@Composable
fun RecipeCard(recipe: Recipe) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
item {
Text(recipe.name, style = MaterialTheme.typography.headlineSmall)
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
InfoChip(Icons.Default.Timer, "${recipe.cookingTime} min")
InfoChip(Icons.Default.People, "${recipe.servings} servings")
InfoChip(Icons.Default.LocalFireDepartment, "${recipe.nutrition.calories} kcal")
}
}
item { Text("Ingredients", style = MaterialTheme.typography.titleMedium) }
items(recipe.ingredients) { ing ->
Text("• ${ing.amount} ${ing.unit} ${ing.name}")
}
item { Text("Steps", style = MaterialTheme.typography.titleMedium) }
itemsIndexed(recipe.steps) { index, step ->
StepCard(number = index + 1, instruction = step.instruction, duration = step.duration)
}
}
}
Timer for Cooking Steps
Each timed step starts a timer directly from the card. We use Timer and UNUserNotificationCenter for notifications, even when the app is in background.
class StepTimerManager: ObservableObject {
@Published var activeTimers = [Int: TimeInterval]()
private var timers = [Int: Timer]()
func startTimer(for stepIndex: Int, duration: TimeInterval) {
activeTimers[stepIndex] = duration
timers[stepIndex] = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
guard let self else { return }
if let remaining = self.activeTimers[stepIndex], remaining > 0 {
self.activeTimers[stepIndex] = remaining - 1
} else {
self.timers[stepIndex]?.invalidate()
self.notifyStepComplete(stepIndex)
}
}
}
private func notifyStepComplete(_ step: Int) {
let content = UNMutableNotificationContent()
content.title = "Step \(step + 1) done"
content.sound = .default
UNUserNotificationCenter.current().add(
UNNotificationRequest(identifier: "step-\(step)", content: content, trigger: nil)
)
}
}
Why Use JSON response_format?
Without it, the model returns markdown-wrapped code. Parsing such output reliably is nearly impossible — the model may change formatting. By specifying response_format: json_object, we guarantee clean JSON that can be decoded via Codable or Gson. This speeds up development and reduces bugs.
How Recipe Personalization Works
Each saved and cooked recipe is added to the preference profile. On the next request, we include the list of liked dishes in the prompt, increasing relevance without fine-tuning. Recipe storage: SwiftData (recent iOS) or Core Data with JSON-serialized recipe structure in an attribute. On Android: Room with TypeConverter for List<Ingredient> and List<Step>.
What's Included in the Work
| Stage | What We Do | Duration |
|---|---|---|
| Analysis | Define UX, use cases, constraints | 1–2 days |
| Design | UI kits for iOS and Android, adapt to Material You / HIG | 3–5 days |
| AI Development | Integration of recognition, generation, prompt engineering | 5–10 days |
| Frontend | SwiftUI / Compose screens, timers, animations | 5–7 days |
| Backend | GraphQL / REST API, profile storage, analytics | 4–6 days |
| Testing | Unit + UI tests, API load testing | 3–5 days |
| Deployment | App Store / Google Play publication, CI/CD setup | 2–3 days |
| Support | Documentation, training, 1-month warranty | 5 days |
Timeline Estimates
Basic assistant (text input + recipe generation): from 3 days. Full implementation with photo recognition, step timers, preference profile, and offline storage: from 4 weeks. Cost is calculated individually after project assessment.
Our team has 5+ years of mobile development experience and over 30 successful projects. We guarantee compliance with App Store and Google Play requirements. Every project undergoes code review and load testing.
Contact us to discuss your project and get a personalized estimate.







