Skip to content

Commit f6d9d45

Browse files
committed
Migrate to type safe UserDefaults
1 parent caf53e7 commit f6d9d45

File tree

13 files changed

+344
-115
lines changed

13 files changed

+344
-115
lines changed

Copilot for Xcode/LaunchAgentView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ struct LaunchAgentView: View {
77
@State var isDidRemoveLaunchAgentAlertPresented = false
88
@State var isDidSetupLaunchAgentAlertPresented = false
99
@State var isDidRestartLaunchAgentAlertPresented = false
10-
@AppStorage(SettingsKey.nodePath, store: .shared) var nodePath: String = ""
10+
@AppStorage(\.nodePath) var nodePath: String
1111

1212
var body: some View {
1313
Section {

Copilot for Xcode/SettingsView.swift

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,27 @@ import Preferences
33
import SwiftUI
44

55
final class Settings: ObservableObject {
6-
@AppStorage(SettingsKey.quitXPCServiceOnXcodeAndAppQuit, store: .shared)
7-
var quitXPCServiceOnXcodeAndAppQuit: Bool = false
8-
@AppStorage(SettingsKey.realtimeSuggestionToggle, store: .shared)
9-
var realtimeSuggestionToggle: Bool = false
10-
@AppStorage(SettingsKey.realtimeSuggestionDebounce, store: .shared)
11-
var realtimeSuggestionDebounce: Double = 0.7
12-
@AppStorage(SettingsKey.suggestionPresentationMode, store: .shared)
13-
var suggestionPresentationModeRawValue: Int = 0
14-
@AppStorage(SettingsKey.automaticallyCheckForUpdate, store: .shared)
15-
var automaticallyCheckForUpdate: Bool = false
16-
@AppStorage(SettingsKey.suggestionWidgetPositionMode, store: .shared)
17-
var suggestionWidgetPositionModeRawValue: Int = 0
18-
@AppStorage(SettingsKey.widgetColorScheme, store: .shared)
19-
var widgetColorScheme: Int = 0
6+
@AppStorage(\.quitXPCServiceOnXcodeAndAppQuit)
7+
var quitXPCServiceOnXcodeAndAppQuit: Bool
8+
@AppStorage(\.realtimeSuggestionToggle)
9+
var realtimeSuggestionToggle: Bool
10+
@AppStorage(\.realtimeSuggestionDebounce)
11+
var realtimeSuggestionDebounce: Double
12+
@AppStorage(\.suggestionPresentationMode)
13+
var suggestionPresentationMode: Preferences.PresentationMode
14+
@AppStorage(\.automaticallyCheckForUpdate)
15+
var automaticallyCheckForUpdate: Bool
16+
@AppStorage(\.suggestionWidgetPositionMode)
17+
var suggestionWidgetPositionMode: SuggestionWidgetPositionMode
18+
@AppStorage(\.widgetColorScheme)
19+
var widgetColorScheme: WidgetColorScheme
2020
init() {}
2121
}
2222

2323
struct SettingsView: View {
2424
@StateObject var settings = Settings()
2525
@State var editingRealtimeSuggestionDebounce: Double = UserDefaults.shared
26-
.value(forKey: SettingsKey.realtimeSuggestionDebounce) as? Double ?? 0.7
26+
.value(for: \.realtimeSuggestionDebounce)
2727

2828
var body: some View {
2929
Section {
@@ -38,29 +38,27 @@ struct SettingsView: View {
3838
}
3939
.toggleStyle(.switch)
4040

41-
Picker(selection: $settings.suggestionPresentationModeRawValue) {
41+
Picker(selection: $settings.suggestionPresentationMode) {
4242
ForEach(PresentationMode.allCases, id: \.rawValue) {
4343
switch $0 {
4444
case .comment:
45-
Text("Comment")
45+
Text("Comment").tag($0)
4646
case .floatingWidget:
47-
Text("Floating Widget")
47+
Text("Floating Widget").tag($0)
4848
}
4949
}
5050
} label: {
5151
Text("Present suggestions in")
5252
}
5353

54-
if settings.suggestionPresentationModeRawValue == PresentationMode.floatingWidget
55-
.rawValue
56-
{
57-
Picker(selection: $settings.suggestionWidgetPositionModeRawValue) {
54+
if settings.suggestionPresentationMode == PresentationMode.floatingWidget {
55+
Picker(selection: $settings.suggestionWidgetPositionMode) {
5856
ForEach(SuggestionWidgetPositionMode.allCases, id: \.rawValue) {
5957
switch $0 {
6058
case .fixedToBottom:
61-
Text("Fixed to Bottom")
59+
Text("Fixed to Bottom").tag($0)
6260
case .alignToTextCursor:
63-
Text("Follow Text Cursor")
61+
Text("Follow Text Cursor").tag($0)
6462
}
6563
}
6664
} label: {
@@ -71,11 +69,11 @@ struct SettingsView: View {
7169
ForEach(WidgetColorScheme.allCases, id: \.rawValue) {
7270
switch $0 {
7371
case .system:
74-
Text("System")
72+
Text("System").tag($0)
7573
case .light:
76-
Text("Light")
74+
Text("Light").tag($0)
7775
case .dark:
78-
Text("Dark")
76+
Text("Dark").tag($0)
7977
}
8078
}
8179
} label: {

Core/Sources/CopilotService/CopilotService.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public class CopilotBaseService {
5252
.createDirectory(at: supportURL, withIntermediateDirectories: false)
5353
}
5454
let executionParams = {
55-
let nodePath = UserDefaults.shared.string(forKey: SettingsKey.nodePath) ?? ""
55+
let nodePath = UserDefaults.shared.value(for: \.nodePath)
5656
return Process.ExecutionParameters(
5757
path: "/usr/bin/env",
5858
arguments: [
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import Foundation
2+
3+
#if canImport(SwiftUI)
4+
5+
import SwiftUI
6+
7+
public extension AppStorage {
8+
init<K: UserDefaultPreferenceKey>(
9+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
10+
) where K.Value == Value, Value == Bool {
11+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
12+
self.init(wrappedValue: key.defaultValue, key.key, store: .shared)
13+
}
14+
15+
init<K: UserDefaultPreferenceKey>(
16+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
17+
) where K.Value == Value, Value == String {
18+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
19+
self.init(wrappedValue: key.defaultValue, key.key, store: .shared)
20+
}
21+
22+
init<K: UserDefaultPreferenceKey>(
23+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
24+
) where K.Value == Value, Value == Double {
25+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
26+
self.init(wrappedValue: key.defaultValue, key.key, store: .shared)
27+
}
28+
29+
init<K: UserDefaultPreferenceKey>(
30+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
31+
) where K.Value == Value, Value == Int {
32+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
33+
self.init(wrappedValue: key.defaultValue, key.key, store: .shared)
34+
}
35+
36+
init<K: UserDefaultPreferenceKey>(
37+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
38+
) where K.Value == Value, Value == URL {
39+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
40+
self.init(wrappedValue: key.defaultValue, key.key, store: .shared)
41+
}
42+
43+
init<K: UserDefaultPreferenceKey>(
44+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
45+
) where K.Value == Value, Value == Data {
46+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
47+
self.init(wrappedValue: key.defaultValue, key.key, store: .shared)
48+
}
49+
50+
init<K: UserDefaultPreferenceKey>(
51+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
52+
) where K.Value == Value, Value: RawRepresentable, Value.RawValue == Int {
53+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
54+
self.init(wrappedValue: key.defaultValue, key.key, store: .shared)
55+
}
56+
57+
init<K: UserDefaultPreferenceKey>(
58+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
59+
) where K.Value == Value, Value: RawRepresentable, Value.RawValue == String {
60+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
61+
self.init(wrappedValue: key.defaultValue, key.key, store: .shared)
62+
}
63+
}
64+
65+
public extension AppStorage where Value: ExpressibleByNilLiteral {
66+
init<K: UserDefaultPreferenceKey>(
67+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
68+
) where K.Value == Value, Value == Bool? {
69+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
70+
self.init(key.key, store: .shared)
71+
}
72+
73+
init<K: UserDefaultPreferenceKey>(
74+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
75+
) where K.Value == Value, Value == String? {
76+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
77+
self.init(key.key, store: .shared)
78+
}
79+
80+
init<K: UserDefaultPreferenceKey>(
81+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
82+
) where K.Value == Value, Value == Double? {
83+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
84+
self.init(key.key, store: .shared)
85+
}
86+
87+
init<K: UserDefaultPreferenceKey>(
88+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
89+
) where K.Value == Value, Value == Int? {
90+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
91+
self.init(key.key, store: .shared)
92+
}
93+
94+
init<K: UserDefaultPreferenceKey>(
95+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
96+
) where K.Value == Value, Value == URL? {
97+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
98+
self.init(key.key, store: .shared)
99+
}
100+
101+
init<K: UserDefaultPreferenceKey>(
102+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
103+
) where K.Value == Value, Value == Data? {
104+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
105+
self.init(key.key, store: .shared)
106+
}
107+
}
108+
109+
public extension AppStorage {
110+
init<K: UserDefaultPreferenceKey, R>(
111+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
112+
) where K.Value == Value, Value == R?, R : RawRepresentable, R.RawValue == String {
113+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
114+
self.init(key.key, store: .shared)
115+
}
116+
117+
init<K: UserDefaultPreferenceKey, R>(
118+
_ keyPath: KeyPath<UserDefaultPreferenceKeys, K>
119+
) where K.Value == Value, Value == R?, R : RawRepresentable, R.RawValue == Int {
120+
let key = UserDefaultPreferenceKeys()[keyPath: keyPath]
121+
self.init(key.key, store: .shared)
122+
}
123+
}
124+
125+
#endif
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import Foundation
2+
3+
public protocol UserDefaultPreferenceKey {
4+
associatedtype Value
5+
var defaultValue: Value { get }
6+
var key: String { get }
7+
}
8+
9+
public struct UserDefaultPreferenceKeys {
10+
public init() {}
11+
12+
public struct NodePath: UserDefaultPreferenceKey {
13+
public let defaultValue: String = ""
14+
public let key = "NodePath"
15+
}
16+
17+
public var nodePath: NodePath { .init() }
18+
19+
public struct RealtimeSuggestionToggle: UserDefaultPreferenceKey {
20+
public let defaultValue: Bool = false
21+
public let key = "RealtimeSuggestionToggle"
22+
}
23+
24+
public var realtimeSuggestionToggle: RealtimeSuggestionToggle { .init() }
25+
26+
public struct RealtimeSuggestionDebounce: UserDefaultPreferenceKey {
27+
public let defaultValue: Double = 1
28+
public let key = "RealtimeSuggestionDebounce"
29+
}
30+
31+
public var realtimeSuggestionDebounce: RealtimeSuggestionDebounce { .init() }
32+
33+
public struct QuitXPCServiceOnXcodeAndAppQuit: UserDefaultPreferenceKey {
34+
public let defaultValue = true
35+
public let key = "QuitXPCServiceOnXcodeAndAppQuit"
36+
}
37+
38+
public var quitXPCServiceOnXcodeAndAppQuit: QuitXPCServiceOnXcodeAndAppQuit { .init() }
39+
40+
public struct SuggestionPresentationMode: UserDefaultPreferenceKey {
41+
public let defaultValue = PresentationMode.floatingWidget
42+
public let key = "SuggestionPresentationMode"
43+
}
44+
45+
public var suggestionPresentationMode: SuggestionPresentationMode { .init() }
46+
47+
public struct AutomaticallyCheckForUpdate: UserDefaultPreferenceKey {
48+
public let defaultValue = false
49+
public let key = "AutomaticallyCheckForUpdate"
50+
}
51+
52+
public var automaticallyCheckForUpdate: AutomaticallyCheckForUpdate { .init() }
53+
54+
public struct SuggestionWidgetPositionModeKey: UserDefaultPreferenceKey {
55+
public let defaultValue = SuggestionWidgetPositionMode.fixedToBottom
56+
public let key = "SuggestionWidgetPositionMode"
57+
}
58+
59+
public var suggestionWidgetPositionMode: SuggestionWidgetPositionModeKey { .init() }
60+
61+
public struct WidgetColorSchemeKey: UserDefaultPreferenceKey {
62+
public let defaultValue = WidgetColorScheme.dark
63+
public let key = "WidgetColorScheme"
64+
}
65+
66+
public var widgetColorScheme: WidgetColorSchemeKey { .init() }
67+
}

0 commit comments

Comments
 (0)