diff --git a/EmojiArt/Constants.swift b/EmojiArt/Constants.swift index a1907e2..9e74756 100644 --- a/EmojiArt/Constants.swift +++ b/EmojiArt/Constants.swift @@ -11,4 +11,5 @@ import SwiftUI struct const { static let defaultEmojiFontSize: CGFloat = 40 + static let coalescingInterval = 5.0 } diff --git a/EmojiArt/EmojiArtDocument.swift b/EmojiArt/EmojiArtDocument.swift index dcc3745..bb697a4 100644 --- a/EmojiArt/EmojiArtDocument.swift +++ b/EmojiArt/EmojiArtDocument.swift @@ -10,14 +10,55 @@ import SwiftUI class EmojiArtDocument: ObservableObject { @Published private(set) var emojiArt: EmojiArtModel { didSet { + scheduleAutosave() if emojiArt.background != oldValue.background { fetchBackgoundImageDataIfNecessary() } } } + + private var autosaveTimer: Timer? + + private func scheduleAutosave() { + autosaveTimer?.invalidate() + autosaveTimer = Timer.scheduledTimer(withTimeInterval: const.coalescingInterval, repeats: false) { _ in + self.autoSave() + } + } + + private enum Autosave { + static let filename = "Autosaved.emojiart" + static var url: URL? { + let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first + return documentDirectory?.appendingPathComponent(filename) + } + } + + private func autoSave() { + if let url = Autosave.url { + save(to: url) + } + } + + private func save(to url: URL) { + let thisFunction = "\(String(describing: self)).\(#function)" + do { + let data: Data = try emojiArt.json() + print("\(thisFunction) json = \(String(data: data, encoding: .utf8) ?? "nil")") + try data.write(to: url) + print("\(thisFunction) success") + } catch { + print("\(thisFunction) error = \(error)") + } + } init() { - emojiArt = EmojiArtModel() + if let url = Autosave.url, let autosavedEmojiArt = try? EmojiArtModel(url: url) { + emojiArt = autosavedEmojiArt + fetchBackgoundImageDataIfNecessary() + } else { + emojiArt = EmojiArtModel() + } } var emojis: [EmojiArtModel.Emoji] { emojiArt.emojis } diff --git a/EmojiArt/EmojiArtModel.Background.swift b/EmojiArt/EmojiArtModel.Background.swift index 6b5b7d7..3c12412 100644 --- a/EmojiArt/EmojiArtModel.Background.swift +++ b/EmojiArt/EmojiArtModel.Background.swift @@ -8,11 +8,36 @@ import Foundation extension EmojiArtModel { - enum Background: Equatable { + enum Background: Equatable, Codable { case blank case url(URL) case imageData(Data) - + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + if let url = try? container.decode(URL.self, forKey: .url) { + self = .url(url) + } else if let imageData = try? container.decode(Data.self, forKey: .imageData) { + self = .imageData(imageData) + } else { + self = .blank + } + } + + enum CodingKeys: String, CodingKey { + case url = "theURL" + case imageData + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + switch self { + case .url(let url): try container.encode(url, forKey: .url) + case .imageData(let data): try container.encode(data, forKey: .imageData) + case .blank: break + } + } + var url: URL? { switch self { case .url(let url): return url diff --git a/EmojiArt/EmojiArtModel.swift b/EmojiArt/EmojiArtModel.swift index 98fd475..eb57a96 100644 --- a/EmojiArt/EmojiArtModel.swift +++ b/EmojiArt/EmojiArtModel.swift @@ -7,11 +7,11 @@ import Foundation -struct EmojiArtModel { +struct EmojiArtModel: Codable { var background: Background = EmojiArtModel.Background.blank var emojis = [Emoji]() - struct Emoji: Identifiable { + struct Emoji: Identifiable, Hashable, Codable { let text: String var x: Int var y: Int @@ -26,15 +26,26 @@ struct EmojiArtModel { self.id = id } } - - init() { - + + func json() throws -> Data { + return try JSONEncoder().encode(self) } + + init(json: Data) throws { + self = try JSONDecoder().decode(EmojiArtModel.self, from: json) + } + + init(url: URL) throws { + let data = try Data(contentsOf: url) + self = try EmojiArtModel.init(json: data) + } + + init() {} + private var uniqueEmojiId = 0 mutating func addEmoji(_ text: String, at location: (x: Int, y: Int), size: Int) { uniqueEmojiId += 1 emojis.append(Emoji(text: text, x: location.x, y: location.y, size: size, id: uniqueEmojiId)) } - }