Commit fddfb1cbb5
Unsigned
Layout: unified · split
Hutch.xcodeproj/project.pbxproj +8 −8
| @@ -539,7 +539,7 @@ | |||
| 539 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; | 539 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; |
| 540 | CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; | 540 | CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; |
| 541 | CODE_SIGN_STYLE = Automatic; | 541 | CODE_SIGN_STYLE = Automatic; |
| 542 | CURRENT_PROJECT_VERSION = 22; | 542 | CURRENT_PROJECT_VERSION = 23; |
| 543 | DEVELOPMENT_TEAM = ZCNAX3VL9D; | 543 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 544 | ENABLE_PREVIEWS = YES; | 544 | ENABLE_PREVIEWS = YES; |
| 545 | GENERATE_INFOPLIST_FILE = YES; | 545 | GENERATE_INFOPLIST_FILE = YES; |
| @@ -556,7 +556,7 @@ | |||
| 556 | "$(inherited)", | 556 | "$(inherited)", |
| 557 | "@executable_path/Frameworks", | 557 | "@executable_path/Frameworks", |
| 558 | ); | 558 | ); |
| 559 | MARKETING_VERSION = 2.9.0; | 559 | MARKETING_VERSION = 2.9.1; |
| 560 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; | 560 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; |
| 561 | PRODUCT_NAME = "$(TARGET_NAME)"; | 561 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 562 | STRING_CATALOG_GENERATE_SYMBOLS = YES; | 562 | STRING_CATALOG_GENERATE_SYMBOLS = YES; |
| @@ -576,7 +576,7 @@ | |||
| 576 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; | 576 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; |
| 577 | CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; | 577 | CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; |
| 578 | CODE_SIGN_STYLE = Automatic; | 578 | CODE_SIGN_STYLE = Automatic; |
| 579 | CURRENT_PROJECT_VERSION = 22; | 579 | CURRENT_PROJECT_VERSION = 23; |
| 580 | DEVELOPMENT_TEAM = ZCNAX3VL9D; | 580 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 581 | ENABLE_PREVIEWS = YES; | 581 | ENABLE_PREVIEWS = YES; |
| 582 | GENERATE_INFOPLIST_FILE = YES; | 582 | GENERATE_INFOPLIST_FILE = YES; |
| @@ -593,7 +593,7 @@ | |||
| 593 | "$(inherited)", | 593 | "$(inherited)", |
| 594 | "@executable_path/Frameworks", | 594 | "@executable_path/Frameworks", |
| 595 | ); | 595 | ); |
| 596 | MARKETING_VERSION = 2.9.0; | 596 | MARKETING_VERSION = 2.9.1; |
| 597 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; | 597 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; |
| 598 | PRODUCT_NAME = "$(TARGET_NAME)"; | 598 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 599 | STRING_CATALOG_GENERATE_SYMBOLS = YES; | 599 | STRING_CATALOG_GENERATE_SYMBOLS = YES; |
| @@ -656,7 +656,7 @@ | |||
| 656 | APPLICATION_EXTENSION_API_ONLY = YES; | 656 | APPLICATION_EXTENSION_API_ONLY = YES; |
| 657 | CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; | 657 | CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; |
| 658 | CODE_SIGN_STYLE = Automatic; | 658 | CODE_SIGN_STYLE = Automatic; |
| 659 | CURRENT_PROJECT_VERSION = 22; | 659 | CURRENT_PROJECT_VERSION = 23; |
| 660 | DEVELOPMENT_TEAM = ZCNAX3VL9D; | 660 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 661 | GENERATE_INFOPLIST_FILE = NO; | 661 | GENERATE_INFOPLIST_FILE = NO; |
| 662 | INFOPLIST_FILE = HutchWidgetExtension/Info.plist; | 662 | INFOPLIST_FILE = HutchWidgetExtension/Info.plist; |
| @@ -666,7 +666,7 @@ | |||
| 666 | "@executable_path/Frameworks", | 666 | "@executable_path/Frameworks", |
| 667 | "@executable_path/../../Frameworks", | 667 | "@executable_path/../../Frameworks", |
| 668 | ); | 668 | ); |
| 669 | MARKETING_VERSION = 2.8.3; | 669 | MARKETING_VERSION = 2.9.1; |
| 670 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; | 670 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; |
| 671 | PRODUCT_NAME = "$(TARGET_NAME)"; | 671 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 672 | SKIP_INSTALL = YES; | 672 | SKIP_INSTALL = YES; |
| @@ -685,7 +685,7 @@ | |||
| 685 | APPLICATION_EXTENSION_API_ONLY = YES; | 685 | APPLICATION_EXTENSION_API_ONLY = YES; |
| 686 | CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; | 686 | CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; |
| 687 | CODE_SIGN_STYLE = Automatic; | 687 | CODE_SIGN_STYLE = Automatic; |
| 688 | CURRENT_PROJECT_VERSION = 22; | 688 | CURRENT_PROJECT_VERSION = 23; |
| 689 | DEVELOPMENT_TEAM = ZCNAX3VL9D; | 689 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 690 | GENERATE_INFOPLIST_FILE = NO; | 690 | GENERATE_INFOPLIST_FILE = NO; |
| 691 | INFOPLIST_FILE = HutchWidgetExtension/Info.plist; | 691 | INFOPLIST_FILE = HutchWidgetExtension/Info.plist; |
| @@ -695,7 +695,7 @@ | |||
| 695 | "@executable_path/Frameworks", | 695 | "@executable_path/Frameworks", |
| 696 | "@executable_path/../../Frameworks", | 696 | "@executable_path/../../Frameworks", |
| 697 | ); | 697 | ); |
| 698 | MARKETING_VERSION = 2.8.3; | 698 | MARKETING_VERSION = 2.9.1; |
| 699 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; | 699 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; |
| 700 | PRODUCT_NAME = "$(TARGET_NAME)"; | 700 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 701 | SKIP_INSTALL = YES; | 701 | SKIP_INSTALL = YES; |
Hutch/App/AppStorageKeys.swift +1
| @@ -3,4 +3,5 @@ enum AppStorageKeys { | |||
| 3 | static let swipeActionsEnabled = "swipeActionsEnabled" | 3 | static let swipeActionsEnabled = "swipeActionsEnabled" |
| 4 | static let activeAccountID = "activeAccountID" | 4 | static let activeAccountID = "activeAccountID" |
| 5 | static let wrapRepositoryFileLines = "wrapRepositoryFileLines" | 5 | static let wrapRepositoryFileLines = "wrapRepositoryFileLines" |
| 6 | static let lookupHistory = "lookupHistory" | ||
| 6 | } | 7 | } |
Hutch/Views/Lookup/LookupHistoryStore.swift added +60
| @@ -0,0 +1,60 @@ | |||
| 1 | import Foundation | ||
| 2 | |||
| 3 | struct LookupHistoryEntry: Codable, Hashable, Identifiable, Sendable { | ||
| 4 | let type: LookupType | ||
| 5 | let query: String | ||
| 6 | let createdAt: Date | ||
| 7 | |||
| 8 | var id: String { | ||
| 9 | "\(type.rawValue):\(query)" | ||
| 10 | } | ||
| 11 | } | ||
| 12 | |||
| 13 | enum LookupHistoryStore { | ||
| 14 | private static let maximumEntries = 20 | ||
| 15 | |||
| 16 | static func load(defaults: UserDefaults = .standard) -> [LookupHistoryEntry] { | ||
| 17 | guard let data = defaults.data(forKey: AppStorageKeys.lookupHistory) else { | ||
| 18 | return [] | ||
| 19 | } | ||
| 20 | |||
| 21 | do { | ||
| 22 | return try JSONDecoder().decode([LookupHistoryEntry].self, from: data) | ||
| 23 | } catch { | ||
| 24 | defaults.removeObject(forKey: AppStorageKeys.lookupHistory) | ||
| 25 | return [] | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | static func record( | ||
| 30 | type: LookupType, | ||
| 31 | query: String, | ||
| 32 | defaults: UserDefaults = .standard, | ||
| 33 | now: Date = .now | ||
| 34 | ) { | ||
| 35 | let normalizedQuery = query.trimmingCharacters(in: .whitespacesAndNewlines) | ||
| 36 | guard !normalizedQuery.isEmpty else { return } | ||
| 37 | |||
| 38 | var entries = load(defaults: defaults) | ||
| 39 | entries.removeAll { $0.type == type && $0.query == normalizedQuery } | ||
| 40 | entries.insert( | ||
| 41 | LookupHistoryEntry(type: type, query: normalizedQuery, createdAt: now), | ||
| 42 | at: 0 | ||
| 43 | ) | ||
| 44 | |||
| 45 | if entries.count > maximumEntries { | ||
| 46 | entries = Array(entries.prefix(maximumEntries)) | ||
| 47 | } | ||
| 48 | |||
| 49 | save(entries, defaults: defaults) | ||
| 50 | } | ||
| 51 | |||
| 52 | static func clear(defaults: UserDefaults = .standard) { | ||
| 53 | defaults.removeObject(forKey: AppStorageKeys.lookupHistory) | ||
| 54 | } | ||
| 55 | |||
| 56 | private static func save(_ entries: [LookupHistoryEntry], defaults: UserDefaults) { | ||
| 57 | guard let data = try? JSONEncoder().encode(entries) else { return } | ||
| 58 | defaults.set(data, forKey: AppStorageKeys.lookupHistory) | ||
| 59 | } | ||
| 60 | } | ||
Hutch/Views/Lookup/LookupView.swift +55 −2
| @@ -3,7 +3,7 @@ import os | |||
| 3 | 3 | ||
| 4 | private let lookupLogger = Logger(subsystem: "net.cleberg.Hutch", category: "Lookup") | 4 | private let lookupLogger = Logger(subsystem: "net.cleberg.Hutch", category: "Lookup") |
| 5 | 5 | ||
| 6 | enum LookupType: String, CaseIterable, Identifiable { | 6 | enum LookupType: String, CaseIterable, Identifiable, Codable, Sendable { |
| 7 | case user = "User" | 7 | case user = "User" |
| 8 | case gitRepo = "Git Repo" | 8 | case gitRepo = "Git Repo" |
| 9 | case hgRepo = "Hg Repo" | 9 | case hgRepo = "Hg Repo" |
| @@ -74,10 +74,12 @@ final class LookupViewModel { | |||
| 74 | var inputText: String = "" | 74 | var inputText: String = "" |
| 75 | private(set) var result: LookupResult? | 75 | private(set) var result: LookupResult? |
| 76 | private(set) var isLooking = false | 76 | private(set) var isLooking = false |
| 77 | private(set) var history: [LookupHistoryEntry] | ||
| 77 | var error: String? | 78 | var error: String? |
| 78 | 79 | ||
| 79 | private let client: SRHTClient | 80 | private let client: SRHTClient |
| 80 | private let appState: AppState | 81 | private let appState: AppState |
| 82 | private let defaults: UserDefaults | ||
| 81 | 83 | ||
| 82 | var resultBinding: Binding<LookupResult?> { | 84 | var resultBinding: Binding<LookupResult?> { |
| 83 | Binding( | 85 | Binding( |
| @@ -90,9 +92,11 @@ final class LookupViewModel { | |||
| 90 | ) | 92 | ) |
| 91 | } | 93 | } |
| 92 | 94 | ||
| 93 | init(client: SRHTClient, appState: AppState) { | 95 | init(client: SRHTClient, appState: AppState, defaults: UserDefaults = .standard) { |
| 94 | self.client = client | 96 | self.client = client |
| 95 | self.appState = appState | 97 | self.appState = appState |
| 98 | self.defaults = defaults | ||
| 99 | self.history = LookupHistoryStore.load(defaults: defaults) | ||
| 96 | } | 100 | } |
| 97 | 101 | ||
| 98 | func lookup() async { | 102 | func lookup() async { |
| @@ -123,6 +127,17 @@ final class LookupViewModel { | |||
| 123 | } | 127 | } |
| 124 | } | 128 | } |
| 125 | 129 | ||
| 130 | func rerun(_ entry: LookupHistoryEntry) async { | ||
| 131 | selectedType = entry.type | ||
| 132 | inputText = entry.query | ||
| 133 | await lookup() | ||
| 134 | } | ||
| 135 | |||
| 136 | func clearHistory() { | ||
| 137 | LookupHistoryStore.clear(defaults: defaults) | ||
| 138 | history = [] | ||
| 139 | } | ||
| 140 | |||
| 126 | private func parseOwnerAndName() -> (owner: String, name: String)? { | 141 | private func parseOwnerAndName() -> (owner: String, name: String)? { |
| 127 | let trimmed = inputText.trimmingCharacters(in: .whitespacesAndNewlines) | 142 | let trimmed = inputText.trimmingCharacters(in: .whitespacesAndNewlines) |
| 128 | let normalized = trimmed.hasPrefix("~") ? String(trimmed.dropFirst()) : trimmed | 143 | let normalized = trimmed.hasPrefix("~") ? String(trimmed.dropFirst()) : trimmed |
| @@ -161,6 +176,7 @@ final class LookupViewModel { | |||
| 161 | 176 | ||
| 162 | private func lookupUser() async throws -> LookupResult { | 177 | private func lookupUser() async throws -> LookupResult { |
| 163 | guard let username = parseUsername() else { throw LookupError.invalidInput } | 178 | guard let username = parseUsername() else { throw LookupError.invalidInput } |
| 179 | recordHistory(type: .user, query: "~\(username)") | ||
| 164 | 180 | ||
| 165 | struct Response: Decodable, Sendable { | 181 | struct Response: Decodable, Sendable { |
| 166 | let user: User | 182 | let user: User |
| @@ -216,6 +232,8 @@ final class LookupViewModel { | |||
| 216 | 232 | ||
| 217 | private func lookupRepository(service: SRHTService) async throws -> LookupResult { | 233 | private func lookupRepository(service: SRHTService) async throws -> LookupResult { |
| 218 | guard let (owner, name) = parseOwnerAndName() else { throw LookupError.invalidInput } | 234 | guard let (owner, name) = parseOwnerAndName() else { throw LookupError.invalidInput } |
| 235 | let type: LookupType = service == .git ? .gitRepo : .hgRepo | ||
| 236 | recordHistory(type: type, query: "~\(owner)/\(name)") | ||
| 219 | 237 | ||
| 220 | let repository = try await appState.resolveRepository(owner: owner, name: name, service: service) | 238 | let repository = try await appState.resolveRepository(owner: owner, name: name, service: service) |
| 221 | let resolvedRepository = RepositorySummary( | 239 | let resolvedRepository = RepositorySummary( |
| @@ -235,6 +253,7 @@ final class LookupViewModel { | |||
| 235 | 253 | ||
| 236 | private func lookupMailingList() async throws -> LookupResult { | 254 | private func lookupMailingList() async throws -> LookupResult { |
| 237 | guard let (owner, name) = parseOwnerAndName() else { throw LookupError.invalidInput } | 255 | guard let (owner, name) = parseOwnerAndName() else { throw LookupError.invalidInput } |
| 256 | recordHistory(type: .mailingList, query: "~\(owner)/\(name)") | ||
| 238 | 257 | ||
| 239 | struct Response: Decodable, Sendable { | 258 | struct Response: Decodable, Sendable { |
| 240 | let user: UserWithList | 259 | let user: UserWithList |
| @@ -266,12 +285,14 @@ final class LookupViewModel { | |||
| 266 | 285 | ||
| 267 | private func lookupTracker() async throws -> LookupResult { | 286 | private func lookupTracker() async throws -> LookupResult { |
| 268 | guard let (owner, name) = parseOwnerAndName() else { throw LookupError.invalidInput } | 287 | guard let (owner, name) = parseOwnerAndName() else { throw LookupError.invalidInput } |
| 288 | recordHistory(type: .tracker, query: "~\(owner)/\(name)") | ||
| 269 | let tracker = try await appState.resolveTracker(owner: owner, name: name) | 289 | let tracker = try await appState.resolveTracker(owner: owner, name: name) |
| 270 | return .tracker(tracker) | 290 | return .tracker(tracker) |
| 271 | } | 291 | } |
| 272 | 292 | ||
| 273 | private func lookupBuildJob() async throws -> LookupResult { | 293 | private func lookupBuildJob() async throws -> LookupResult { |
| 274 | guard let jobId = parseBuildJobId() else { throw LookupError.invalidInput } | 294 | guard let jobId = parseBuildJobId() else { throw LookupError.invalidInput } |
| 295 | recordHistory(type: .buildJob, query: String(jobId)) | ||
| 275 | 296 | ||
| 276 | struct Response: Decodable, Sendable { | 297 | struct Response: Decodable, Sendable { |
| 277 | let job: JobIdOnly | 298 | let job: JobIdOnly |
| @@ -300,6 +321,11 @@ final class LookupViewModel { | |||
| 300 | private enum LookupError: Error { | 321 | private enum LookupError: Error { |
| 301 | case invalidInput | 322 | case invalidInput |
| 302 | } | 323 | } |
| 324 | |||
| 325 | private func recordHistory(type: LookupType, query: String) { | ||
| 326 | LookupHistoryStore.record(type: type, query: query, defaults: defaults) | ||
| 327 | history = LookupHistoryStore.load(defaults: defaults) | ||
| 328 | } | ||
| 303 | } | 329 | } |
| 304 | 330 | ||
| 305 | struct LookupView: View { | 331 | struct LookupView: View { |
| @@ -361,6 +387,33 @@ struct LookupView: View { | |||
| 361 | } | 387 | } |
| 362 | } | 388 | } |
| 363 | } | 389 | } |
| 390 | |||
| 391 | if !vm.history.isEmpty { | ||
| 392 | Section("Recent Searches") { | ||
| 393 | ForEach(vm.history) { entry in | ||
| 394 | Button { | ||
| 395 | Task { await vm.rerun(entry) } | ||
| 396 | } label: { | ||
| 397 | HStack { | ||
| 398 | VStack(alignment: .leading, spacing: 2) { | ||
| 399 | Text(entry.query) | ||
| 400 | .foregroundStyle(.primary) | ||
| 401 | Text(entry.type.rawValue) | ||
| 402 | .font(.caption) | ||
| 403 | .foregroundStyle(.secondary) | ||
| 404 | } | ||
| 405 | Spacer() | ||
| 406 | } | ||
| 407 | } | ||
| 408 | .disabled(vm.isLooking) | ||
| 409 | } | ||
| 410 | |||
| 411 | Button("Clear History", role: .destructive) { | ||
| 412 | vm.clearHistory() | ||
| 413 | } | ||
| 414 | .disabled(vm.isLooking) | ||
| 415 | } | ||
| 416 | } | ||
| 364 | } | 417 | } |
| 365 | .formStyle(.grouped) | 418 | .formStyle(.grouped) |
| 366 | .srhtErrorBanner(error: $vm.error) | 419 | .srhtErrorBanner(error: $vm.error) |
HutchTests/LookupHistoryStoreTests.swift added +75
| @@ -0,0 +1,75 @@ | |||
| 1 | import Foundation | ||
| 2 | import Testing | ||
| 3 | @testable import Hutch | ||
| 4 | |||
| 5 | struct LookupHistoryStoreTests { | ||
| 6 | |||
| 7 | @Test | ||
| 8 | func recordsMostRecentUniqueSearchFirst() { | ||
| 9 | let suiteName = "LookupHistoryStoreTests-\(UUID().uuidString)" | ||
| 10 | let defaults = UserDefaults(suiteName: suiteName)! | ||
| 11 | defer { defaults.removePersistentDomain(forName: suiteName) } | ||
| 12 | |||
| 13 | LookupHistoryStore.record( | ||
| 14 | type: .user, | ||
| 15 | query: "~alice", | ||
| 16 | defaults: defaults, | ||
| 17 | now: Date(timeIntervalSince1970: 100) | ||
| 18 | ) | ||
| 19 | LookupHistoryStore.record( | ||
| 20 | type: .gitRepo, | ||
| 21 | query: "~alice/hutch", | ||
| 22 | defaults: defaults, | ||
| 23 | now: Date(timeIntervalSince1970: 200) | ||
| 24 | ) | ||
| 25 | LookupHistoryStore.record( | ||
| 26 | type: .user, | ||
| 27 | query: "~alice", | ||
| 28 | defaults: defaults, | ||
| 29 | now: Date(timeIntervalSince1970: 300) | ||
| 30 | ) | ||
| 31 | |||
| 32 | let history = LookupHistoryStore.load(defaults: defaults) | ||
| 33 | |||
| 34 | #expect(history.count == 2) | ||
| 35 | #expect(history.map(\.type) == [.user, .gitRepo]) | ||
| 36 | #expect(history.map(\.query) == ["~alice", "~alice/hutch"]) | ||
| 37 | #expect(history.first?.createdAt == Date(timeIntervalSince1970: 300)) | ||
| 38 | } | ||
| 39 | |||
| 40 | @Test | ||
| 41 | func trimsHistoryToMaximumSize() { | ||
| 42 | let suiteName = "LookupHistoryStoreTests-\(UUID().uuidString)" | ||
| 43 | let defaults = UserDefaults(suiteName: suiteName)! | ||
| 44 | defer { defaults.removePersistentDomain(forName: suiteName) } | ||
| 45 | |||
| 46 | for index in 0..<25 { | ||
| 47 | LookupHistoryStore.record( | ||
| 48 | type: .buildJob, | ||
| 49 | query: "\(index)", | ||
| 50 | defaults: defaults, | ||
| 51 | now: Date(timeIntervalSince1970: TimeInterval(index)) | ||
| 52 | ) | ||
| 53 | } | ||
| 54 | |||
| 55 | let history = LookupHistoryStore.load(defaults: defaults) | ||
| 56 | |||
| 57 | #expect(history.count == 20) | ||
| 58 | #expect(history.first?.query == "24") | ||
| 59 | #expect(history.last?.query == "5") | ||
| 60 | } | ||
| 61 | |||
| 62 | @Test | ||
| 63 | func clearsPersistedHistory() { | ||
| 64 | let suiteName = "LookupHistoryStoreTests-\(UUID().uuidString)" | ||
| 65 | let defaults = UserDefaults(suiteName: suiteName)! | ||
| 66 | defer { defaults.removePersistentDomain(forName: suiteName) } | ||
| 67 | |||
| 68 | LookupHistoryStore.record(type: .tracker, query: "~owner/todo", defaults: defaults) | ||
| 69 | #expect(!LookupHistoryStore.load(defaults: defaults).isEmpty) | ||
| 70 | |||
| 71 | LookupHistoryStore.clear(defaults: defaults) | ||
| 72 | |||
| 73 | #expect(LookupHistoryStore.load(defaults: defaults).isEmpty) | ||
| 74 | } | ||
| 75 | } | ||