diff options
| author | Fuwn <[email protected]> | 2025-03-15 22:19:01 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2025-03-15 22:19:01 -0700 |
| commit | 808f34a5ac6e63f6a1abeeb98c03e625caa23cae (patch) | |
| tree | a737d81d7ee343f26e41c2d47372606110976d2e | |
| parent | feat: Development commit (diff) | |
| download | sora-testing-808f34a5ac6e63f6a1abeeb98c03e625caa23cae.tar.xz sora-testing-808f34a5ac6e63f6a1abeeb98c03e625caa23cae.zip | |
feat: Development commit
| -rw-r--r-- | Localizable.xcstrings | 6 | ||||
| -rw-r--r-- | Sora/Data/Settings/SettingsManager.swift | 128 | ||||
| -rw-r--r-- | Sora/Sora.entitlements | 2 | ||||
| -rw-r--r-- | Sora/Views/Settings/Section/SettingsSettingsView.swift | 10 |
4 files changed, 136 insertions, 10 deletions
diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 8d52372..2a303c0 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -152,6 +152,9 @@ "Invalid Domain" : { }, + "Keep bookmarks, search history, and providers up to date on all your devices." : { + + }, "Loading %@…" : { }, @@ -326,6 +329,9 @@ "Suggestion Mode" : { }, + "Sync with iCloud" : { + + }, "Tags" : { "localizations" : { "ja" : { diff --git a/Sora/Data/Settings/SettingsManager.swift b/Sora/Data/Settings/SettingsManager.swift index 909016d..b2749e1 100644 --- a/Sora/Data/Settings/SettingsManager.swift +++ b/Sora/Data/Settings/SettingsManager.swift @@ -23,6 +23,11 @@ class SettingsManager: ObservableObject { @AppStorage("preloadedCarouselImages") var preloadedCarouselImages = 3 + @AppStorage("enableICloudSync") + var enableICloudSync: Bool = false + + private var iCloudSyncObservation: NSObjectProtocol? + #if os(macOS) @AppStorage("saveTagsToFile") var saveTagsToFile = false @@ -50,11 +55,32 @@ class SettingsManager: ObservableObject { // MARK: - Computed Properties var bookmarks: [SettingsBookmark] { get { - (Self.decode([SettingsBookmark].self, from: bookmarksData) ?? []) + if enableICloudSync { + if let data = NSUbiquitousKeyValueStore.default.data(forKey: "bookmarks") { + return (Self.decode([SettingsBookmark].self, from: data) ?? []) + .sorted { $0.date > $1.date } + } + + return (Self.decode([SettingsBookmark].self, from: bookmarksData) ?? []) + .sorted { $0.date > $1.date } + } + + return (Self.decode([SettingsBookmark].self, from: bookmarksData) ?? []) .sorted { $0.date > $1.date } } - set { bookmarksData = Self.encode(newValue) ?? bookmarksData } + set { + let sortedBookmarks = newValue.sorted { $0.date > $1.date } + + bookmarksData = Self.encode(sortedBookmarks) ?? Data() + + if enableICloudSync { + NSUbiquitousKeyValueStore.default.set( + Self.encode(sortedBookmarks), + forKey: "bookmarks" + ) + } + } } var displayRatings: [BooruRating] { @@ -73,11 +99,31 @@ class SettingsManager: ObservableObject { var searchHistory: [BooruSearchQuery] { get { - (Self.decode([BooruSearchQuery].self, from: searchHistoryData) ?? []) + if enableICloudSync { + if let data = NSUbiquitousKeyValueStore.default.data(forKey: "searchHistory") { + return (Self.decode([BooruSearchQuery].self, from: data) ?? []) + .sorted { $0.date > $1.date } + } + + return (Self.decode([BooruSearchQuery].self, from: searchHistoryData) ?? []) + .sorted { $0.date > $1.date } + } + + return (Self.decode([BooruSearchQuery].self, from: searchHistoryData) ?? []) .sorted { $0.date > $1.date } } - set { searchHistoryData = Self.encode(newValue) ?? searchHistoryData } + set { + let sortedHistory = newValue.sorted { $0.date > $1.date } + + searchHistoryData = Self.encode(sortedHistory) ?? Data() + + if enableICloudSync { + NSUbiquitousKeyValueStore.default.set( + Self.encode(sortedHistory), forKey: "searchHistory" + ) + } + } } var preferredBooru: BooruProvider { @@ -90,14 +136,51 @@ class SettingsManager: ObservableObject { var customProviders: [BooruProviderCustom] { get { - Self.decode( - [BooruProviderCustom].self, - from: customProvidersData - ) ?? [] + if enableICloudSync { + if let data = NSUbiquitousKeyValueStore.default.data(forKey: "customProviders") { + return Self.decode([BooruProviderCustom].self, from: data) ?? [] + } + + return Self.decode([BooruProviderCustom].self, from: customProvidersData) ?? [] + } + + return Self.decode([BooruProviderCustom].self, from: customProvidersData) ?? [] } set { - customProvidersData = Self.encode(newValue) ?? customProvidersData + customProvidersData = Self.encode(newValue) ?? Data() + + if enableICloudSync { + NSUbiquitousKeyValueStore.default.set( + Self.encode(newValue), + forKey: "customProviders" + ) + } + } + } + + // MARK: - Initialisation + init() { + iCloudSyncObservation = NotificationCenter.default.addObserver( + forName: NSUbiquitousKeyValueStore.didChangeExternallyNotification, + object: NSUbiquitousKeyValueStore.default, + queue: .main + ) { [weak self] _ in + guard let localSelf = self, localSelf.enableICloudSync else { return } // swiftlint:disable:this self_binding + + if let data = NSUbiquitousKeyValueStore.default.data(forKey: "bookmarks") { + localSelf.bookmarksData = data + } + + if let data = NSUbiquitousKeyValueStore.default.data(forKey: "searchHistory") { + localSelf.searchHistoryData = data + } + + if let data = NSUbiquitousKeyValueStore.default.data(forKey: "customProviders") { + localSelf.customProvidersData = data + } + + localSelf.objectWillChange.send() } } @@ -132,9 +215,27 @@ class SettingsManager: ObservableObject { #endif } + func syncToICloud() { + if enableICloudSync { + NSUbiquitousKeyValueStore.default.set(bookmarksData, forKey: "bookmarks") + NSUbiquitousKeyValueStore.default.set(searchHistoryData, forKey: "searchHistory") + NSUbiquitousKeyValueStore.default.set(customProvidersData, forKey: "customProviders") + } + } + // MARK: - Bookmark Management func addBookmark(provider: BooruProvider, tags: [String]) { - bookmarks.append(SettingsBookmark(provider: provider, tags: tags.map { $0.lowercased() })) + var updatedBookmarks = bookmarks + + updatedBookmarks.append( + SettingsBookmark(provider: provider, tags: tags.map { $0.lowercased() }) + ) + + if let data = Self.encode(updatedBookmarks), data.count < 1_000_000 { // 1 MB + bookmarks = updatedBookmarks + } else { + debugPrint("SettingsManager.addBookmark: iCloud data limit exceeded") + } } func removeBookmark(at offsets: IndexSet) { @@ -200,4 +301,11 @@ class SettingsManager: ObservableObject { } } #endif + + // MARK: - Deinitialisation + deinit { + if let observation = iCloudSyncObservation { + NotificationCenter.default.removeObserver(observation) + } + } } diff --git a/Sora/Sora.entitlements b/Sora/Sora.entitlements index 264aa0a..e06fcf4 100644 --- a/Sora/Sora.entitlements +++ b/Sora/Sora.entitlements @@ -2,6 +2,8 @@ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> + <key>com.apple.developer.ubiquity-kvstore-identifier</key> + <string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string> <key>com.apple.security.app-sandbox</key> <true/> <key>com.apple.security.assets.pictures.read-write</key> diff --git a/Sora/Views/Settings/Section/SettingsSettingsView.swift b/Sora/Views/Settings/Section/SettingsSettingsView.swift index 404d252..8062a90 100644 --- a/Sora/Views/Settings/Section/SettingsSettingsView.swift +++ b/Sora/Views/Settings/Section/SettingsSettingsView.swift @@ -4,6 +4,16 @@ struct SettingsSettingsView: View { @EnvironmentObject var settings: SettingsManager var body: some View { + Toggle(isOn: $settings.enableICloudSync) { + Text("Sync with iCloud") + + Text("Keep bookmarks, search history, and providers up to date on all your devices.") + .font(.caption) + } + .onChange(of: settings.enableICloudSync) { _, _ in + settings.syncToICloud() + } + Button("Reset to Defaults") { settings.resetToDefaults() } |