From 5db22ae24417b8e196b0f3fcd9091ec6c07ab791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=E2=80=99Aquino?= Date: Fri, 1 Dec 2023 21:26:14 +0000 Subject: [PATCH] Migrate setting and key stores to shared UserDefaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is needed to allow the notification extension to process push notifications, respect user's notification settings, and decrypt DMs on the push notification Signed-off-by: Daniel D’Aquino Signed-off-by: William Casarin --- damus.xcodeproj/project.pbxproj | 6 +++ damus/Models/DamusUserDefaults.swift | 67 ++++++++++++++++++++++++++++ damus/Models/UserSettingsStore.swift | 20 ++++----- damus/Util/Keys.swift | 10 ++--- 4 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 damus/Models/DamusUserDefaults.swift diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index ca3aed98..08d94170 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -590,6 +590,8 @@ D7EDED2E2B128E8A0018B19C /* CollectionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EDED2D2B128E8A0018B19C /* CollectionExtension.swift */; }; D7EDED2F2B128E8A0018B19C /* CollectionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EDED2D2B128E8A0018B19C /* CollectionExtension.swift */; }; D7EDED312B1290B80018B19C /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = D7EDED302B1290B80018B19C /* MarkdownUI */; }; + D7EDED332B12ACAE0018B19C /* DamusUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EDED322B12ACAE0018B19C /* DamusUserDefaults.swift */; }; + D7EDED342B12ACAE0018B19C /* DamusUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EDED322B12ACAE0018B19C /* DamusUserDefaults.swift */; }; D7FB10A72B0C371A00FA8D42 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C2B10272A7B0F5C008AA43E /* Log.swift */; }; D7FF94002AC7AC5300FD969D /* RelayURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FF93FF2AC7AC5200FD969D /* RelayURL.swift */; }; E4FA1C032A24BB7F00482697 /* SearchSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */; }; @@ -1343,6 +1345,7 @@ D7EDED1D2B11797D0018B19C /* LongformEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongformEvent.swift; sourceTree = ""; }; D7EDED202B117DCA0018B19C /* SequenceUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SequenceUtils.swift; sourceTree = ""; }; D7EDED2D2B128E8A0018B19C /* CollectionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionExtension.swift; sourceTree = ""; }; + D7EDED322B12ACAE0018B19C /* DamusUserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusUserDefaults.swift; sourceTree = ""; }; D7FF93FF2AC7AC5200FD969D /* RelayURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayURL.swift; sourceTree = ""; }; E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsView.swift; sourceTree = ""; }; E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = ""; }; @@ -1550,6 +1553,7 @@ D7CB5D5E2B11770C00AD4105 /* FollowState.swift */, D7EDED1B2B1178FE0018B19C /* NoteContent.swift */, D7EDED1D2B11797D0018B19C /* LongformEvent.swift */, + D7EDED322B12ACAE0018B19C /* DamusUserDefaults.swift */, ); path = Models; sourceTree = ""; @@ -3082,6 +3086,7 @@ 4C3AC7A12835A81400E1F516 /* SetupView.swift in Sources */, 4C06670128FC7C5900038D2A /* RelayView.swift in Sources */, 4C285C8C28398BC7008A31F1 /* Keys.swift in Sources */, + D7EDED332B12ACAE0018B19C /* DamusUserDefaults.swift in Sources */, 4CA352AE2A76C1AC003BB08B /* FollowedNotify.swift in Sources */, 4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */, 4CE879582996C45300F758CC /* ZapsView.swift in Sources */, @@ -3373,6 +3378,7 @@ D7CB5D412B116F0900AD4105 /* StringCodable.swift in Sources */, D7CE1B1F2B0BE1B8002EDAD4 /* damus.c in Sources */, D7CE1B1B2B0BE144002EDAD4 /* emitter.c in Sources */, + D7EDED342B12ACAE0018B19C /* DamusUserDefaults.swift in Sources */, D7CB5D562B11759900AD4105 /* MuteThreadNotify.swift in Sources */, D7EDED182B1177A00018B19C /* LNUrlPayRequest.swift in Sources */, D798D21C2B0857E400234419 /* Bech32Object.swift in Sources */, diff --git a/damus/Models/DamusUserDefaults.swift b/damus/Models/DamusUserDefaults.swift new file mode 100644 index 00000000..851dd00b --- /dev/null +++ b/damus/Models/DamusUserDefaults.swift @@ -0,0 +1,67 @@ +// +// DamusUserDefaults.swift +// damus +// +// Created by Daniel D’Aquino on 2023-11-25. +// + +import Foundation + +/// DamusUserDefaults +/// This struct acts like a drop-in replacement for `UserDefaults.standard` +/// for cases where we want to store such items in a UserDefaults that is shared among the Damus app group +/// so that they can be accessed from other target (e.g. The notification extension target). +/// +/// This struct handles migration automatically to the new shared UserDefaults +struct DamusUserDefaults { + static let shared: DamusUserDefaults = DamusUserDefaults() + private static let default_suite_name: String = "group.com.damus" // Shared defaults for this app group + + private let suite_name: String + private let defaults: UserDefaults + + // MARK: - Initializers + + init() { + self.init(suite_name: Self.default_suite_name)! // Pretty low risk to force-unwrap given that the default suite name is a constant. + } + + init?(suite_name: String = Self.default_suite_name) { + self.suite_name = suite_name + guard let defaults = UserDefaults(suiteName: suite_name) else { + return nil + } + self.defaults = defaults + } + + // MARK: - Functions for feature parity with UserDefaults.standard + + func string(forKey defaultName: String) -> String? { + if let value = self.defaults.string(forKey: defaultName) { + return value + } + let fallback_value = UserDefaults.standard.string(forKey: defaultName) + self.defaults.set(fallback_value, forKey: defaultName) // Migrate + return fallback_value + } + + func set(_ value: Any?, forKey defaultName: String) { + self.defaults.set(value, forKey: defaultName) + } + + func removeObject(forKey defaultName: String) { + self.defaults.removeObject(forKey: defaultName) + // Remove from standard UserDefaults to avoid it coming back as a fallback_value when we fetch it next time + UserDefaults.standard.removeObject(forKey: defaultName) + } + + func object(forKey defaultName: String) -> Any? { + if let value = self.defaults.object(forKey: defaultName) { + return value + } + let fallback_value = UserDefaults.standard.string(forKey: defaultName) + self.defaults.set(fallback_value, forKey: defaultName) // Migrate + return fallback_value + } + +} diff --git a/damus/Models/UserSettingsStore.swift b/damus/Models/UserSettingsStore.swift index 191591c3..68c72546 100644 --- a/damus/Models/UserSettingsStore.swift +++ b/damus/Models/UserSettingsStore.swift @@ -16,13 +16,13 @@ func setting_property_key(key: String) -> String { } func setting_get_property_value(key: String, scoped_key: String, default_value: T) -> T { - if let loaded = UserDefaults.standard.object(forKey: scoped_key) as? T { + if let loaded = DamusUserDefaults.shared.object(forKey: scoped_key) as? T { return loaded - } else if let loaded = UserDefaults.standard.object(forKey: key) as? T { + } else if let loaded = DamusUserDefaults.shared.object(forKey: key) as? T { // If pubkey-scoped setting does not exist but the deprecated non-pubkey-scoped setting does, // migrate the deprecated setting into the pubkey-scoped one and delete the deprecated one. - UserDefaults.standard.set(loaded, forKey: scoped_key) - UserDefaults.standard.removeObject(forKey: key) + DamusUserDefaults.shared.set(loaded, forKey: scoped_key) + DamusUserDefaults.shared.removeObject(forKey: key) return loaded } else { return default_value @@ -31,7 +31,7 @@ func setting_get_property_value(key: String, scoped_key: String, default_valu func setting_set_property_value(scoped_key: String, old_value: T, new_value: T) -> T? { guard old_value != new_value else { return nil } - UserDefaults.standard.set(new_value, forKey: scoped_key) + DamusUserDefaults.shared.set(new_value, forKey: scoped_key) UserSettingsStore.shared?.objectWillChange.send() return new_value } @@ -65,14 +65,14 @@ func setting_set_property_value(scoped_key: String, old_value: T, init(key: String, default_value: T) { self.key = pk_setting_key(UserSettingsStore.pubkey ?? .empty, key: key) - if let loaded = UserDefaults.standard.string(forKey: self.key), let val = T.init(from: loaded) { + if let loaded = DamusUserDefaults.shared.string(forKey: self.key), let val = T.init(from: loaded) { self.value = val - } else if let loaded = UserDefaults.standard.string(forKey: key), let val = T.init(from: loaded) { + } else if let loaded = DamusUserDefaults.shared.string(forKey: key), let val = T.init(from: loaded) { // If pubkey-scoped setting does not exist but the deprecated non-pubkey-scoped setting does, // migrate the deprecated setting into the pubkey-scoped one and delete the deprecated one. self.value = val - UserDefaults.standard.set(val.to_string(), forKey: self.key) - UserDefaults.standard.removeObject(forKey: key) + DamusUserDefaults.shared.set(val.to_string(), forKey: self.key) + DamusUserDefaults.shared.removeObject(forKey: key) } else { self.value = default_value } @@ -85,7 +85,7 @@ func setting_set_property_value(scoped_key: String, old_value: T, return } self.value = newValue - UserDefaults.standard.set(newValue.to_string(), forKey: key) + DamusUserDefaults.shared.set(newValue.to_string(), forKey: key) UserSettingsStore.shared!.objectWillChange.send() } } diff --git a/damus/Util/Keys.swift b/damus/Util/Keys.swift index 13313de6..75daee68 100644 --- a/damus/Util/Keys.swift +++ b/damus/Util/Keys.swift @@ -124,7 +124,7 @@ func privkey_to_pubkey(privkey: Privkey) -> Pubkey? { } func save_pubkey(pubkey: Pubkey) { - UserDefaults.standard.set(pubkey.hex(), forKey: "pubkey") + DamusUserDefaults.shared.set(pubkey.hex(), forKey: "pubkey") } enum Keys { @@ -141,7 +141,7 @@ func clear_saved_privkey() throws { } func clear_saved_pubkey() { - UserDefaults.standard.removeObject(forKey: "pubkey") + DamusUserDefaults.shared.removeObject(forKey: "pubkey") } func save_keypair(pubkey: Pubkey, privkey: Privkey) throws { @@ -175,7 +175,7 @@ func get_saved_keypair() -> Keypair? { } func get_saved_pubkey() -> String? { - return UserDefaults.standard.string(forKey: "pubkey") + return DamusUserDefaults.shared.string(forKey: "pubkey") } func get_saved_privkey() -> String? { @@ -198,10 +198,10 @@ func contentContainsPrivateKey(_ content: String) -> Bool { } fileprivate func removePrivateKeyFromUserDefaults() throws { - guard let privkey_str = UserDefaults.standard.string(forKey: "privkey"), + guard let privkey_str = DamusUserDefaults.shared.string(forKey: "privkey"), let privkey = hex_decode_privkey(privkey_str) else { return } try save_privkey(privkey: privkey) - UserDefaults.standard.removeObject(forKey: "privkey") + DamusUserDefaults.shared.removeObject(forKey: "privkey") }