94 lines
3.0 KiB
Swift
94 lines
3.0 KiB
Swift
//
|
|
// DraftsManager.swift
|
|
// ComposeUI
|
|
//
|
|
// Created by Shadowfacts on 10/22/18.
|
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import Combine
|
|
|
|
public class DraftsManager: Codable, ObservableObject {
|
|
|
|
public private(set) static var shared: DraftsManager = load()
|
|
|
|
private static var appGroupDirectory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.space.vaccor.Tusker")!
|
|
private static var archiveURL = appGroupDirectory.appendingPathComponent("drafts").appendingPathExtension("plist")
|
|
|
|
private static let saveQueue = DispatchQueue(label: "DraftsManager", qos: .utility)
|
|
|
|
public static func save() {
|
|
saveQueue.async {
|
|
let encoder = PropertyListEncoder()
|
|
let data = try? encoder.encode(shared)
|
|
try? data?.write(to: archiveURL, options: .noFileProtection)
|
|
}
|
|
}
|
|
|
|
static func load() -> DraftsManager {
|
|
let decoder = PropertyListDecoder()
|
|
if let data = try? Data(contentsOf: archiveURL),
|
|
let draftsManager = try? decoder.decode(DraftsManager.self, from: data) {
|
|
return draftsManager
|
|
}
|
|
return DraftsManager()
|
|
}
|
|
|
|
private init() {}
|
|
|
|
public required init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
|
|
if let dict = try? container.decode([UUID: SafeDraft].self, forKey: .drafts) {
|
|
self.drafts = dict.compactMapValues { $0.draft }
|
|
} else if let array = try? container.decode([SafeDraft].self, forKey: .drafts) {
|
|
self.drafts = array.reduce(into: [:], { partialResult, safeDraft in
|
|
if let draft = safeDraft.draft {
|
|
partialResult[draft.id] = draft
|
|
}
|
|
})
|
|
} else {
|
|
throw DecodingError.dataCorruptedError(forKey: .drafts, in: container, debugDescription: "expected drafts to be a dict or array of drafts")
|
|
}
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(drafts, forKey: .drafts)
|
|
}
|
|
|
|
@Published private var drafts: [UUID: Draft] = [:]
|
|
var sorted: [Draft] {
|
|
return drafts.values.sorted(by: { $0.lastModified > $1.lastModified })
|
|
}
|
|
|
|
public func add(_ draft: Draft) {
|
|
drafts[draft.id] = draft
|
|
}
|
|
|
|
public func remove(_ draft: Draft) {
|
|
drafts.removeValue(forKey: draft.id)
|
|
}
|
|
|
|
public func getBy(id: UUID) -> Draft? {
|
|
return drafts[id]
|
|
}
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case drafts
|
|
}
|
|
|
|
// a container that always succeeds at decoding
|
|
// so if a single draft can't be decoded, we don't lose all drafts
|
|
struct SafeDraft: Decodable {
|
|
let draft: Draft?
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let container = try decoder.singleValueContainer()
|
|
self.draft = try? container.decode(Draft.self)
|
|
}
|
|
}
|
|
|
|
}
|