Skip to content

Commit a0e8b81

Browse files
committed
Replace dictionary-based data to the list-based approach
1 parent 7ab7f96 commit a0e8b81

3 files changed

Lines changed: 246 additions & 150 deletions

File tree

WordPress/Classes/ViewRelated/Me/App Settings/Boolean User Defaults/BooleanUserDefaultsDebugView.swift

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,23 @@ struct BooleanUserDefaultsDebugView: View {
55

66
var body: some View {
77
List {
8-
ForEach(viewModel.userDefaultsSections.keys.sorted(), id: \.self) { sectionKey in
9-
let userDefaultsSection = viewModel.userDefaultsSections[sectionKey] ?? BooleanUserDefaultEntries()
10-
11-
Section(header: Text(sectionKey)
8+
ForEach(viewModel.userDefaultsSections, id: \.key) { section in
9+
Section(header: Text(section.key)
1210
.font(.caption)) {
13-
ForEach(userDefaultsSection.keys.sorted(), id: \.self) { userDefaultKey in
11+
ForEach(section.rows, id: \.key) { row in
1412
let isOn = Binding<Bool>(
15-
get: { userDefaultsSection[userDefaultKey]?.value ?? false },
16-
set: { newValue in viewModel.updateUserDefault(newValue, forSection: sectionKey, forUserDefault: userDefaultKey) }
13+
get: {
14+
row.value
15+
},
16+
set: { newValue in
17+
viewModel.updateUserDefault(
18+
newValue,
19+
forSection: section.key,
20+
forRow: row.key
21+
)
22+
}
1723
)
18-
Toggle(userDefaultsSection[userDefaultKey]?.title ?? Strings.unrecognizedEntryTitle, isOn: isOn)
24+
Toggle(row.title, isOn: isOn)
1925
.font(.caption)
2026
.toggleStyle(
2127
SwitchToggleStyle(

WordPress/Classes/ViewRelated/Me/App Settings/Boolean User Defaults/BooleanUserDefaultsDebugViewModel.swift

Lines changed: 113 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,126 @@
11
import SwiftUI
22

3-
typealias BooleanUserDefaultsSections = [String: BooleanUserDefaultEntries]
4-
typealias BooleanUserDefaultEntries = [String: BooleanUserDefault]
5-
63
final class BooleanUserDefaultsDebugViewModel: ObservableObject {
7-
@Published private var allUserDefaultsSections = BooleanUserDefaultsSections()
8-
@Published var searchQuery: String = ""
9-
10-
private var persistentRepository: UserPersistentRepository
11-
12-
var userDefaultsSections: BooleanUserDefaultsSections {
13-
return filterUserDefaults(by: searchQuery)
14-
}
15-
16-
let coreDataStack: CoreDataStack
4+
private let persistentRepository: UserPersistentRepository
5+
private let coreDataStack: CoreDataStack
176

18-
private func filterUserDefaults(by query: String) -> BooleanUserDefaultsSections {
19-
guard !query.isEmpty else {
20-
return allUserDefaultsSections
7+
private var allUserDefaultsSections = Sections() {
8+
didSet {
9+
self.reloadSections()
2110
}
11+
}
2212

23-
var filteredSections = BooleanUserDefaultsSections()
24-
allUserDefaultsSections.forEach { sectionKey, userDefaults in
25-
let filteredUserDefaults = userDefaults.filter { key, userDefault in
26-
key.localizedCaseInsensitiveContains(query) || userDefault.title.localizedCaseInsensitiveContains(query)
27-
}
28-
if sectionKey.localizedCaseInsensitiveContains(query) || !filteredUserDefaults.isEmpty {
29-
filteredSections[sectionKey] = filteredUserDefaults.isEmpty ? userDefaults : filteredUserDefaults
30-
}
13+
@Published var searchQuery: String = "" {
14+
didSet {
15+
self.reloadSections()
3116
}
32-
return filteredSections
3317
}
3418

19+
@Published var userDefaultsSections: Sections = []
20+
3521
init(coreDataStack: CoreDataStack = ContextManager.shared,
3622
persistentRepository: UserPersistentRepository = UserPersistentStoreFactory.instance()) {
3723
self.coreDataStack = coreDataStack
3824
self.persistentRepository = persistentRepository
39-
load()
4025
}
4126

4227
func load() {
4328
let allUserDefaults = persistentRepository.dictionaryRepresentation()
44-
var loadedUserDefaultsSections = BooleanUserDefaultsSections()
45-
var otherSection = BooleanUserDefaultEntries()
46-
47-
for (entryKey, entryValue) in allUserDefaults {
48-
if let groupedUserDefaults = entryValue as? [String: Bool], !isFeatureFlagsSection(entryKey) {
49-
loadedUserDefaultsSections[entryKey] = processGroupedUserDefaults(groupedUserDefaults)
50-
} else if let booleanUserDefault = entryValue as? Bool, !isGutenbergUserDefault(entryKey) {
51-
otherSection[entryKey] = BooleanUserDefault(title: entryKey, value: booleanUserDefault)
29+
var loadedUserDefaultsSections = Sections()
30+
var otherSection = [Row]()
31+
32+
for (key, value) in allUserDefaults {
33+
if let groupedUserDefaults = value as? [String: Bool], !isFeatureFlagsSection(key) {
34+
let section = Section(
35+
key: key,
36+
rows: processGroupedUserDefaults(groupedUserDefaults)
37+
)
38+
loadedUserDefaultsSections.append(section)
39+
} else if let booleanUserDefault = value as? Bool, !isGutenbergUserDefault(key) {
40+
otherSection.append(.init(key: key, title: key, value: booleanUserDefault))
5241
}
5342
}
5443
if !otherSection.isEmpty {
55-
loadedUserDefaultsSections[Strings.otherBooleanUserDefaultsSectionID] = otherSection
44+
let rows = otherSection.sorted { $0.title < $1.title }
45+
let section = Section(key: Strings.otherBooleanUserDefaultsSectionID, rows: rows)
46+
loadedUserDefaultsSections.append(section)
5647
}
5748
allUserDefaultsSections = loadedUserDefaultsSections
5849
}
5950

60-
private func processGroupedUserDefaults(_ userDefaults: [String: Bool]) -> BooleanUserDefaultEntries {
61-
userDefaults.reduce(into: BooleanUserDefaultEntries()) { result, keyValue in
51+
private func reloadSections() {
52+
self.userDefaultsSections = filterUserDefaults(by: searchQuery)
53+
}
54+
55+
private func filterUserDefaults(by query: String) -> Sections {
56+
guard !query.isEmpty else {
57+
return allUserDefaultsSections
58+
}
59+
60+
var filteredSections = Sections()
61+
allUserDefaultsSections.forEach { section in
62+
let filteredUserDefaults = section.rows.filter { entry in
63+
section.key.localizedCaseInsensitiveContains(query) || entry.title.localizedCaseInsensitiveContains(query)
64+
}
65+
if section.key.localizedCaseInsensitiveContains(query) || !filteredUserDefaults.isEmpty {
66+
let section = Section(
67+
key: section.key,
68+
rows: filteredUserDefaults.isEmpty ? section.rows : filteredUserDefaults
69+
)
70+
filteredSections.append(section)
71+
}
72+
}
73+
return filteredSections
74+
}
75+
76+
private func processGroupedUserDefaults(_ userDefaults: [String: Bool]) -> [Row] {
77+
var rows = userDefaults.reduce(into: [Row]()) { result, keyValue in
6278
let (key, value) = keyValue
63-
result[key] = processSingleUserDefault(key: key, value: value)
79+
result.append(processSingleUserDefault(key: key, value: value))
6480
}
81+
rows = rows.sorted { $0.title < $1.title }
82+
return rows
83+
}
84+
85+
private func processSingleUserDefault(key: String, value: Bool) -> Row {
86+
let title = findBlog(byID: key)?.url ?? key
87+
return Row(key: key, title: title, value: value)
6588
}
6689

67-
private func processSingleUserDefault(key: String, value: Bool) -> BooleanUserDefault {
68-
if let siteID = Int(key), let blogURL = try? Blog.lookup(withID: siteID, in: coreDataStack.mainContext)?.url {
69-
return BooleanUserDefault(title: blogURL, value: value)
90+
private func findBlog(byID id: String) -> Blog? {
91+
return try? Blog.lookup(withID: Int(id) ?? 0, in: coreDataStack.mainContext)
92+
}
93+
94+
func updateUserDefault(_ newValue: Bool, forSection targetSection: String, forRow targetRow: String) {
95+
updateAllUserDefaultsSections(newValue, forSection: targetSection, forRow: targetRow)
96+
if targetSection == Strings.otherBooleanUserDefaultsSectionID {
97+
persistentRepository.set(newValue, forKey: targetRow)
7098
} else {
71-
return BooleanUserDefault(title: key, value: value)
99+
guard let section = allUserDefaultsSections.first(where: { $0.key == targetSection }) else {
100+
return
101+
}
102+
let entries = section.rows.reduce(into: [String: Bool]()) { result, row in
103+
result[row.key] = newValue
104+
}
105+
persistentRepository.set(entries, forKey: targetSection)
72106
}
73107
}
74108

75-
func updateUserDefault(_ value: Bool, forSection sectionID: String, forUserDefault userDefaultID: String) {
76-
if sectionID == Strings.otherBooleanUserDefaultsSectionID {
77-
persistentRepository.set(value, forKey: userDefaultID)
78-
} else if var section = allUserDefaultsSections[sectionID] {
79-
section[userDefaultID] = BooleanUserDefault(title: userDefaultID, value: value)
80-
let sectionValues = section.mapValues { $0.value }
81-
persistentRepository.set(sectionValues, forKey: sectionID)
109+
func updateAllUserDefaultsSections(_ newValue: Bool, forSection targetSection: String, forRow targetRow: String) {
110+
allUserDefaultsSections = allUserDefaultsSections.map { currentSection in
111+
if currentSection.key == targetSection {
112+
let updatedRows = currentSection.rows.map { currentRow in
113+
if currentRow.key == targetRow {
114+
return Row(key: currentRow.key, title: currentRow.title, value: newValue)
115+
} else {
116+
return currentRow
117+
}
118+
}
119+
return Section(key: currentSection.key, rows: updatedRows)
120+
} else {
121+
return currentSection
122+
}
82123
}
83-
load()
84124
}
85125

86126
private func isGutenbergUserDefault(_ key: String) -> Bool {
@@ -90,13 +130,32 @@ final class BooleanUserDefaultsDebugViewModel: ObservableObject {
90130
private func isFeatureFlagsSection(_ key: String) -> Bool {
91131
key.isEqual(to: Strings.featureFlagSectionKey)
92132
}
93-
}
94133

95-
struct BooleanUserDefault: Equatable {
96-
var title: String
97-
var value: Bool
134+
// MARK: - Types
135+
136+
struct Section {
137+
let key: String
138+
let rows: [Row]
139+
}
140+
141+
final class Row {
142+
let key: String
143+
let title: String
144+
let value: Bool
145+
146+
init(key: String, title: String, value: Bool) {
147+
self.key = key
148+
self.title = title
149+
self.value = value
150+
}
151+
}
152+
153+
typealias Sections = [Section]
154+
98155
}
99156

157+
// MARK: - Constants
158+
100159
private enum Strings {
101160
static let otherBooleanUserDefaultsSectionID = "Other"
102161
static let gutenbergUserDefaultPrefix = "com.wordpress.gutenberg-"

0 commit comments

Comments
 (0)