Skip to content

Custom UserDefaults suite #210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

Maschina
Copy link
Contributor

@Maschina Maschina commented Apr 12, 2025

This PR adds the capability to define a custom UserDefaults to support App Groups and access group containers.

I am using KeyboardShortcuts in an environment with App Groups. The shared UserDefaults shall also include the defined shortcuts.

@Maschina Maschina changed the title Custom UserDefaults for Custom UserDefaults for App Groups Apr 12, 2025
@orchetect
Copy link

Being able to substitute UserDefaults suites is valuable in other respects. In an app where I use the package, for unit tests and integration tests we use a custom suite for prefs, and it would be useful to be able to use the same suite with KeyboardShortcuts.

@Maschina Maschina changed the title Custom UserDefaults for App Groups Custom UserDefaults suite Apr 12, 2025
…otentially existing observer +++ Update observer if UserDefaults being changed
@Maschina
Copy link
Contributor Author

Maschina commented May 3, 2025

I am just realizing that event streams also require observations. Will make another commit for that.

@Maschina Maschina requested a review from sindresorhus May 4, 2025 21:43
Comment on lines +619 to +623
func update(suite new: UserDefaults) {
invalidate()
suite = new
start()
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I'm missing something, I think it would be better to just recreate observations than this update method.

Something like:

public static var userDefaults = UserDefaults.standard {
	didSet {
		for observer in userDefaultsObservers {
			observer.invalidate()
		}

		userDefaultsObservers.removeAll()

		for name in allNames {
			startObservingShortcutIfNeeded(for: name)
		}
	}
}

return
}

let encodedString = change[.newKey] as? String
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not assume String. It could be a Bool too:

Self.userDefaults.set(false, forKey: userDefaultsKey(for: name))


// check userDefaultsObservers to see if we are already observing this key
if let observer = userDefaultsObservers.first(where: { $0.key == key }) {
observer.start()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could use a better code comment because just looking at it, it's not immediate obvious why we may want to call start() on an already existing observation.

@@ -179,6 +204,12 @@ public enum KeyboardShortcuts {

legacyKeyDownHandlers = [:]
legacyKeyUpHandlers = [:]

// invalidate and remove all elements of userDefaultsObservers
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should comment that it's "defensive" since it's already done when calling removeAll() (deinit).

Comment on lines -62 to +87
UserDefaults.standard.dictionaryRepresentation()
Self.userDefaults.dictionaryRepresentation()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self. is not needed.

@sindresorhus
Copy link
Owner

Make sure you manually test it in the example app and also a real app to ensure it doesn't introduce any new bugs. I'm a little bit nervous about large changes like this as we have no real tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants