A complete iOS SwiftUI implementation for extracting recipes from images using Claude's vision API.
- ImagePreprocessor.swift - Image enhancement for better OCR
- ClaudeAPIClient.swift - Claude API integration
- RecipeExtractorViewModel.swift - State management
- RecipeExtractorView.swift - Main UI
- RecipeDetailView.swift - Recipe display
- ImagePicker.swift - Camera/photo library
- RecipeExtractorConfig.swift - Configuration & API key management
- ExampleAppIntegration.swift - Full app integration example
- RecipeExtractorTests.swift - Testing utilities
Drag all .swift files into your Xcode project.
<key>NSCameraUsageDescription</key>
<string>Take photos of recipe cards</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Select recipe images</string>Option A - Keychain (Recommended)
// First time setup
KeychainManager.shared.save(key: "claudeAPIKey", value: "sk-ant-api03-...")
// Use in app
if let apiKey = KeychainManager.shared.get(key: "claudeAPIKey") {
RecipeExtractorView(apiKey: apiKey)
}Option B - Environment Variable (Development)
Xcode → Edit Scheme → Run → Environment Variables
Name: CLAUDE_API_KEY
Value: sk-ant-api03-...
TabView {
RecipeListView()
.tabItem { Label("Recipes", systemImage: "book.fill") }
if let apiKey = APIKeyHelper.getAPIKey() {
RecipeExtractorView(apiKey: apiKey)
.tabItem { Label("Extract", systemImage: "camera.fill") }
}
}User selects image → Image preprocessed → Sent to Claude →
Recipe extracted → Displayed in RecipeDetailView
✅ Recipe title & description
✅ Multiple ingredient sections
✅ Multiple instruction sections
✅ Yield/servings
✅ Metric conversions
✅ Preparation notes
✅ Recipe notes & tips
✅ References
- Grayscale - Reduces noise
- Contrast - Makes text clearer (+50%)
- Sharpening - Better character recognition
- Noise Reduction - Cleans faded images
struct ContentView: View {
var body: some View {
if let apiKey = APIKeyHelper.getAPIKey() {
RecipeExtractorView(apiKey: apiKey)
}
}
}struct MyRecipeExtractorView: View {
@EnvironmentObject var recipeStore: RecipeStore
let apiKey: String
var body: some View {
RecipeExtractorView(apiKey: apiKey)
.toolbar {
Button("Save") {
// Save extracted recipe
}
}
}
}// In RecipeExtractorConfig.swift
static let contrastLevel: Float = 1.8 // More contrast
static let sharpnessLevel: Float = 0.9 // More sharpening
static let autoExtractOnImageSelection = false // Manual triggerClaude Sonnet 4 Pricing:
- ~$0.02 per recipe extraction
- Based on ~2,000 input tokens + ~1,000 output tokens
Tips to Minimize Costs:
- Cache commonly used recipes
- Batch process when possible
- Use preprocessing to reduce retries
- ✅ Enable preprocessing
- ✅ Ensure text is legible
- ✅ Check image quality
- ✅ Verify API key is correct
- ✅ Check key starts with
sk-ant- - ✅ Ensure key is properly stored
- ✅ Rate limit hit
- ✅ Wait and retry
- ✅ Implement request throttling
- ✅ Disable for high-quality images
- ✅ Try lightweight preprocessing
- ✅ Adjust contrast/sharpness levels
[ ] Camera opens
[ ] Photo library opens
[ ] Image displays correctly
[ ] Preprocessing toggle works
[ ] Extraction completes
[ ] Recipe displays properly
[ ] Can save recipe
[ ] Error handling works
Use TestImageGenerator.generateTestRecipeImage() for basic testing.
- Image preprocessing: < 2 seconds
- API request: 10-30 seconds
- Total extraction: 15-35 seconds
[ ] API key NOT in source code
[ ] API key NOT in version control
[ ] Using Keychain or environment variable
[ ] Info.plist descriptions added
[ ] Rate limiting considered
[ ] Monitoring API usage
Edit ImagePreprocessor.swift:
filter.contrast = 1.8 // Adjust contrast
filter.sharpness = 0.9 // Adjust sharpnessEdit ClaudeAPIClient.swift system prompt to:
- Add custom fields
- Change extraction rules
- Adjust JSON schema
Modify RecipeExtractorView.swift and RecipeDetailView.swift for:
- Custom colors
- Different layouts
- Additional features
Your existing RecipeModel.swift already supports:
- Multiple ingredient sections
- Multiple instruction sections
- Various note types
- Metric conversions
- All necessary fields
Enhances images before sending to Claude. Two modes:
preprocessForOCR()- Full enhancement (grayscale + contrast + sharpen)preprocessLightweight()- Color-preserving (contrast + sharpen)
Handles API communication. Key methods:
extractRecipe()- Main extraction methodextractJSON()- Parses Claude's response- Converts API response to
RecipeModel
Manages app state:
- Loading states
- Error handling
- Image selection
- Preprocessing toggle
Main UI with:
- Image picker integration
- Preprocessing controls
- Loading indicators
- Error displays
- Navigation to detail view
Displays extracted recipe with:
- Formatted sections
- Ingredient lists
- Numbered instructions
- Color-coded notes
- Share functionality
struct EditableRecipeView: View {
@State var recipe: RecipeModel
@State var isEditing = false
var body: some View {
if isEditing {
RecipeEditorView(recipe: $recipe)
} else {
RecipeDetailView(recipe: recipe)
}
}
}func extractMultipleRecipes(_ images: [UIImage]) async -> [RecipeModel] {
await withTaskGroup(of: RecipeModel?.self) { group in
for image in images {
group.addTask {
try? await viewModel.extractRecipe(from: image)
}
}
var recipes: [RecipeModel] = []
for await recipe in group {
if let recipe = recipe {
recipes.append(recipe)
}
}
return recipes
}
}extension RecipeModel {
func toCoreData(context: NSManagedObjectContext) -> RecipeEntity {
let entity = RecipeEntity(context: context)
entity.id = self.id
entity.title = self.title
// ... map other fields
return entity
}
}- Claude API Docs: https://docs.anthropic.com
- SwiftUI: https://developer.apple.com/xcode/swiftui/
- Image Processing: https://developer.apple.com/documentation/coreimage
Common issues and solutions are in:
IMPLEMENTATION_GUIDE.md- Detailed setupRecipeExtractorTests.swift- Testing examplesExampleAppIntegration.swift- Integration patterns
Enhance your implementation:
- Add Core Data persistence
- Implement recipe editing
- Add recipe sharing
- Create recipe collections
- Add nutrition info extraction
- Implement recipe search
- Add favorites/ratings
Built with Claude Sonnet 4
Questions? Check the implementation guide or visit https://docs.anthropic.com