Reputation: 2279
I have an array of Habit objects which I am modifying in memory and a getter/setter to read and write it to documents storage as JSON whenever changes are made. I have split each object into its own JSON file and so far everything works, however changing one object in the array will re-write all the files. I understand this is what I instructed my setter to do, but is there a way to only write the objects in newValue
that were changed?
extension Storage {
static var habits: [Habit] {
get { ... }
set {
newValue.forEach({
let data = try! JSONEncoder().encode($0)
do { try data.write(to: Storage.habitsFolder.appendingPathComponent("habit-\($0.id).json")) }
catch let error {
print("Failed to write Habit \($0.id): \(error.localizedDescription)")
}
})
}
}
}
The way I would make a change now is Storage.habits[0].name = "Some name"
. Which calls the setter, which then re-writes the files for each habit. So I was wondering if there's some way to detect which part of newValue changed or pass an index to it and only update that file.
Is the only way to go about this to have the array be get-only and use a different method for setting each habit file?
Thank you, and apologies if this is a silly question.
Update: adding Habit class for more context
class Habit: Codable, CustomStringConvertible {
// MARK: - Properties
var id: Int
var name: String?
var notes: String?
var icon: String
var repeatPattern: RepeatPattern
var entries: [HabitEntry]
var reminders: [Reminder]
init(id: Int, name: String?, notes: String?, icon: String, entries: [HabitEntry], reminders: [Reminder]) {
self.id = id
self.name = name
self.notes = notes
self.icon = icon
self.repeatPattern = RepeatPattern(pattern: 0, startDay: calendar.today, enabledWeekdays: [1,2,3,4,5,6,7], expectedTimes: 1)
self.entries = entries
self.reminders = reminders
}
var description: String { return "Habit id: \(id), named: \(name ?? "nil"), \nnotes: \(notes ?? "nil"), \nicon: \(icon), \nrepeatPattern: \(repeatPattern), \nentries: \(entries), \nreminders: \(String(describing: reminders))" }
}
Upvotes: 2
Views: 381
Reputation: 30336
You could make a property to store the existing habits
first
class Storage {
static var existingHabits = [Habit]()
}
Then, inside the set
, see which Habit
s are new:
static var habits: [Habit] {
get { ... }
set {
var habitsToChange = [Habit]()
if existingHabits.count == 0 { /// it's empty, just write with newValue
habitsToChange = newValue
} else {
let differentIndicies = zip(existingHabits, newValue).enumerated().filter() {
$1.0 != $1.1
}.map{$0.0} /// from https://stackoverflow.com/a/30685226/14351818
for index in differentIndicies {
habitsToChange.append(newValue[index])
}
}
habitsToChange.forEach({ /// loop over the habits that have changed
do {
try JSONEncoder().encode($0).write(to: habitsFolder.appendingPathComponent("habit-\($0.id).json"))
} catch {
print("Failed to write Habit \($0.id): \(error)")
}
})
existingHabits = newValue /// set existing to the new value
}
}
Upvotes: 1