Add cross-device grocery list sync via Nextcloud Cookbook API
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>
This commit is contained in:
58
Nextcloud Cookbook iOS Client/Data/GroceryStateModels.swift
Normal file
58
Nextcloud Cookbook iOS Client/Data/GroceryStateModels.swift
Normal file
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user