Store a _groceryState JSON field on each recipe to track which ingredients have been added, completed, or removed. Uses per-item last-writer-wins conflict resolution with ISO 8601 timestamps. Debounced push (2s) avoids excessive API calls; pull reconciles on recipe open and app launch. Includes a settings toggle to enable/disable sync. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
59 lines
1.5 KiB
Swift
59 lines
1.5 KiB
Swift
//
|
|
// GroceryStateModels.swift
|
|
// Nextcloud Cookbook iOS Client
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// Tracks grocery list state for a recipe, stored as `_groceryState` in the recipe JSON on the server.
|
|
struct GroceryState: Codable {
|
|
var version: Int = 1
|
|
var lastModified: String
|
|
var items: [String: GroceryItemState]
|
|
|
|
init(lastModified: String = GroceryStateDate.now(), items: [String: GroceryItemState] = [:]) {
|
|
self.version = 1
|
|
self.lastModified = lastModified
|
|
self.items = items
|
|
}
|
|
}
|
|
|
|
struct GroceryItemState: Codable {
|
|
enum Status: String, Codable {
|
|
case added
|
|
case completed
|
|
case removed
|
|
}
|
|
|
|
var status: Status
|
|
var addedAt: String
|
|
var modifiedAt: String
|
|
|
|
init(status: Status, addedAt: String = GroceryStateDate.now(), modifiedAt: String = GroceryStateDate.now()) {
|
|
self.status = status
|
|
self.addedAt = addedAt
|
|
self.modifiedAt = modifiedAt
|
|
}
|
|
}
|
|
|
|
/// ISO 8601 date helpers. Dates are stored as strings to avoid coupling to a parent encoder's date strategy.
|
|
enum GroceryStateDate {
|
|
private static let formatter: ISO8601DateFormatter = {
|
|
let f = ISO8601DateFormatter()
|
|
f.formatOptions = [.withInternetDateTime]
|
|
return f
|
|
}()
|
|
|
|
static func now() -> String {
|
|
formatter.string(from: Date())
|
|
}
|
|
|
|
static func date(from string: String) -> Date? {
|
|
formatter.date(from: string)
|
|
}
|
|
|
|
static func string(from date: Date) -> String {
|
|
formatter.string(from: date)
|
|
}
|
|
}
|