Skip to content
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

Custom storage for shortcuts #139

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
160 changes: 95 additions & 65 deletions Sources/KeyboardShortcuts/KeyboardShortcuts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Global keyboard shortcuts for your macOS app.
*/
public enum KeyboardShortcuts {
public static var storageProvider: StorageProvider?
private static var registeredShortcuts = Set<Shortcut>()

private static var legacyKeyDownHandlers = [Name: [() -> Void]]()
Expand Down Expand Up @@ -137,7 +138,11 @@
guard !isInitialized else {
return
}


Check warning on line 141 in Sources/KeyboardShortcuts/KeyboardShortcuts.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
if storageProvider == nil {

Choose a reason for hiding this comment

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

I suggest that a 'else' statement should be added to this 'if' just in case. This will help cover border effects.

storageProvider = UserDefaultsStorage()
}

Check warning on line 145 in Sources/KeyboardShortcuts/KeyboardShortcuts.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
openMenuObserver = NotificationCenter.default.addObserver(forName: NSMenu.didBeginTrackingNotification, object: nil, queue: nil) { _ in
isMenuOpen = true
}
Expand Down Expand Up @@ -277,29 +282,28 @@
You would usually not need this as the user would be the one setting the shortcut in a settings user-interface, but it can be useful when, for example, migrating from a different keyboard shortcuts package.
*/
public static func setShortcut(_ shortcut: Shortcut?, for name: Name) {

Choose a reason for hiding this comment

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

Adding comments about the parameters will help the understanding of this method.
This has been correctly done in 'Name.swift' 👍🏻

if let shortcut {
userDefaultsSet(name: name, shortcut: shortcut)
} else {
if name.defaultShortcut != nil {
userDefaultsDisable(name: name)
} else {
userDefaultsRemove(name: name)
}
}
if let shortcut {
storageProviderSet(name: name, shortcut: shortcut)
} else {
if name.defaultShortcut != nil {
storageProviderDisable(name: name)
} else {
storageProviderRemove(name: name)
}
}
}

/**
Get the keyboard shortcut for a name.
*/
public static func getShortcut(for name: Name) -> Shortcut? {
guard
let data = UserDefaults.standard.string(forKey: userDefaultsKey(for: name))?.data(using: .utf8),
let decoded = try? JSONDecoder().decode(Shortcut.self, from: data)
else {
return nil
}

return decoded
guard
let data = storageProvider?.get(forKey: name.rawValue)?.data(using: .utf8),
let decoded = try? JSONDecoder().decode(Shortcut.self, from: data)
else {
return nil
}
return decoded
}

private static func handleOnKeyDown(_ shortcut: Shortcut) {
Expand Down Expand Up @@ -406,53 +410,48 @@
registerShortcutIfNeeded(for: name)
}

private static let userDefaultsPrefix = "KeyboardShortcuts_"

private static func userDefaultsKey(for shortcutName: Name) -> String { "\(userDefaultsPrefix)\(shortcutName.rawValue)"
}

static func userDefaultsDidChange(name: Name) {
// TODO: Use proper UserDefaults observation instead of this.
NotificationCenter.default.post(name: .shortcutByNameDidChange, object: nil, userInfo: ["name": name])
}

static func userDefaultsSet(name: Name, shortcut: Shortcut) {
guard let encoded = try? JSONEncoder().encode(shortcut).toString else {
return
}

if let oldShortcut = getShortcut(for: name) {
unregister(oldShortcut)
}

register(shortcut)
UserDefaults.standard.set(encoded, forKey: userDefaultsKey(for: name))
userDefaultsDidChange(name: name)
}

static func userDefaultsDisable(name: Name) {
guard let shortcut = getShortcut(for: name) else {
return
}

UserDefaults.standard.set(false, forKey: userDefaultsKey(for: name))
unregister(shortcut)
userDefaultsDidChange(name: name)
}

static func userDefaultsRemove(name: Name) {
guard let shortcut = getShortcut(for: name) else {
return
}

UserDefaults.standard.removeObject(forKey: userDefaultsKey(for: name))
unregister(shortcut)
userDefaultsDidChange(name: name)
}

static func userDefaultsContains(name: Name) -> Bool {
UserDefaults.standard.object(forKey: userDefaultsKey(for: name)) != nil
}
static func shortcutDidChange(name: Name) {
NotificationCenter.default.post(name: .shortcutByNameDidChange, object: nil, userInfo: ["name": name])
}

static func storageProviderSet(name: Name, shortcut: Shortcut) {
guard let encoded = try? JSONEncoder().encode(shortcut).toString else {
return
}

if let oldShortcut = getShortcut(for: name) {
unregister(oldShortcut)
}

register(shortcut)
storageProvider?.set(encoded, forKey: name.rawValue)
shortcutDidChange(name: name)
}

static func storageProviderDisable(name: Name) {
guard let shortcut = getShortcut(for: name) else {
return
}

storageProvider?.set(nil, forKey: name.rawValue)
unregister(shortcut)
shortcutDidChange(name: name)
}

static func storageProviderRemove(name: Name) {
guard let shortcut = getShortcut(for: name) else {
return
}

Check warning on line 445 in Sources/KeyboardShortcuts/KeyboardShortcuts.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
storageProvider?.remove(forKey: name.rawValue)
unregister(shortcut)
shortcutDidChange(name: name)
}

static func storageProviderContains(name: Name) -> Bool {
return storageProvider?.get(forKey: name.rawValue) != nil

Check warning on line 452 in Sources/KeyboardShortcuts/KeyboardShortcuts.swift

View workflow job for this annotation

GitHub Actions / lint

Implicit Return Violation: Prefer implicit returns in closures, functions and getters (implicit_return)
}

Check warning on line 454 in Sources/KeyboardShortcuts/KeyboardShortcuts.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
}

extension KeyboardShortcuts {
Expand Down Expand Up @@ -589,3 +588,34 @@
extension Notification.Name {
static let shortcutByNameDidChange = Self("KeyboardShortcuts_shortcutByNameDidChange")
}

class UserDefaultsStorage: StorageProvider {

Check warning on line 592 in Sources/KeyboardShortcuts/KeyboardShortcuts.swift

View workflow job for this annotation

GitHub Actions / lint

final_class Violation: Classes should be marked as final whenever possible. If you actually need it to be subclassable, just add `// swiftlint:disable:next final_class`. (final_class)
private static let userDefaultsPrefix = "KeyboardShortcuts_"
private static func userDefaultsKey(for shortcutName: String) -> String { "\(userDefaultsPrefix)\(shortcutName)"}

Check warning on line 595 in Sources/KeyboardShortcuts/KeyboardShortcuts.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
func set(_ value: String?, forKey defaultName: String) {
UserDefaults.standard.set(value, forKey: Self.userDefaultsKey(for: defaultName))
}

func remove(forKey defaultName: String) {
UserDefaults.standard.removeObject(forKey: Self.userDefaultsKey(for: defaultName))
}

func disable(forKey defaultName: String) {
UserDefaults.standard.set(false, forKey: Self.userDefaultsKey(for: defaultName))
}

func get(forKey defaultName: String) -> String? {
guard
let data = UserDefaults.standard.string(forKey: Self.userDefaultsKey(for: defaultName))
else {
return nil
}
return data
}

func contains(forKey defaultName: String) -> Bool {
return UserDefaults.standard.object(forKey: Self.userDefaultsKey(for: defaultName)) != nil

Check warning on line 618 in Sources/KeyboardShortcuts/KeyboardShortcuts.swift

View workflow job for this annotation

GitHub Actions / lint

Implicit Return Violation: Prefer implicit returns in closures, functions and getters (implicit_return)
}

}
22 changes: 12 additions & 10 deletions Sources/KeyboardShortcuts/Name.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,18 @@
- Parameter name: Name of the shortcut.
- Parameter default: Optional default key combination. Do not set this unless it's essential. Users find it annoying when random apps steal their existing keyboard shortcuts. It's generally better to show a welcome screen on the first app launch that lets the user set the shortcut.
*/
public init(_ name: String, default initialShortcut: Shortcut? = nil) {
public init(_ name: String, default initialShortcut: Shortcut? = nil) {
self.rawValue = name
self.defaultShortcut = initialShortcut

if
let initialShortcut,
!userDefaultsContains(name: self)
{
setShortcut(initialShortcut, for: self)
}

KeyboardShortcuts.initialize()

Check warning on line 40 in Sources/KeyboardShortcuts/Name.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
if
let initialShortcut,
!storageProviderContains(name: self)
{
setShortcut(initialShortcut, for: self)
}

KeyboardShortcuts.initialize()
}
}
}
Expand All @@ -56,3 +56,5 @@
self.init(rawValue)
}
}


Check warning on line 60 in Sources/KeyboardShortcuts/Name.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Newline Violation: Files should have a single trailing newline (trailing_newline)
9 changes: 9 additions & 0 deletions Sources/KeyboardShortcuts/Storage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Foundation

public protocol StorageProvider {
func get(forKey defaultName: String) -> String?
mutating func set(_ value: String?, forKey defaultName: String)
mutating func disable(forKey defaultName: String)
mutating func remove(forKey defaultName: String)
func contains(forKey defaultName: String) -> Bool
}