SPB Git

spb/focale Public

Swift 100%
2.0 KB · 82 lines swift
Raw Blame History
1//2//  LivingAlbum.swift3//  Focale4//5//  Author: Simon-Pierre Boucher6//  Contact: contact@spboucher.ai7//8//  An album defined by a query, not a selection (CLAUDE.md §7).9//  It fills itself as you shoot.10//1112import Foundation13import Observation1415struct LivingAlbum: Codable, Identifiable, Sendable {16    let id: UUID17    var name: String18    var filter: SearchFilter1920    init(id: UUID = UUID(), name: String, filter: SearchFilter) {21        self.id = id22        self.name = name23        self.filter = filter24    }25}2627@MainActor28@Observable29final class LivingAlbumStore {30    private(set) var albums: [LivingAlbum] = []31    private let fileURL: URL3233    init(directory: URL = .applicationSupportDirectory) {34        fileURL = directory.appending(path: "living-albums.json")35        load()36        if albums.isEmpty {37            albums = Self.starters38            save()39        }40    }4142    func add(_ album: LivingAlbum) {43        albums.append(album)44        save()45    }4647    func remove(_ id: LivingAlbum.ID) {48        albums.removeAll { $0.id == id }49        save()50    }5152    // User-facing names in French.53    private static var starters: [LivingAlbum] {54        var receipts = SearchFilter.empty55        receipts.kinds = [PhotoKind.receipt.rawValue]5657        var actionable = SearchFilter.empty58        actionable.actionableOnly = true5960        return [61            LivingAlbum(name: "Tous mes reçus", filter: receipts),62            LivingAlbum(name: "Infos à conserver", filter: actionable),63        ]64    }6566    private func load() {67        guard let data = try? Data(contentsOf: fileURL),68              let decoded = try? JSONDecoder().decode([LivingAlbum].self, from: data)69        else { return }70        albums = decoded71    }7273    private func save() {74        guard let data = try? JSONEncoder().encode(albums) else { return }75        try? FileManager.default.createDirectory(76            at: fileURL.deletingLastPathComponent(),77            withIntermediateDirectories: true78        )79        try? data.write(to: fileURL, options: .atomic)80    }81}82