// // ManageSearchEnginesViewController.swift // App // // Created by James Magahern on 9/29/25. // import SwiftUI import UIKit @MainActor struct ManageSearchEnginesView: View { @State var model: ViewModel var body: some View { Form { Section { List($model.configuredEngines, editActions: .delete) { row in HStack { Image(uiImage: .checkmark) .opacity(row.wrappedValue.isDefault ? 1.0 : 0.0) VStack(alignment: .leading) { Text(row.wrappedValue.name) .font(.body) .bold() Text(row.wrappedValue.url) .font(.caption) } } .id(row.id) .swipeActions(edge: .leading) { Button { model.makeDefault(row.id) } label: { Label("Make Default", systemImage: "checkmark") } .tint(.blue) } .swipeActions(edge: .trailing) { Button(role: .destructive) { model.deleteEngine(row.id) } label: { Label("Delete", systemImage: "trash") } .disabled(model.configuredEngines.count <= 1) } } } Section { Button("Add Search Engine…") { model.onAddSearchEngine() } } } } // MARK: - Types @MainActor @Observable class ViewModel { var configuredEngines: [ConfiguredEngine] = [] var onAddSearchEngine: () -> Void = {} var onChangeEngines: () -> Void = {} init() { reloadConfiguredEngines() } func reloadConfiguredEngines() { let defaultEngine = Settings.shared.defaultSearchEngineName self.configuredEngines = Settings.shared.searchEngines.map { val in ConfiguredEngine( name: val.key, url: val.value, isDefault: val.key == defaultEngine ) } } func deleteEngine(_ id: ConfiguredEngine.ID) { let engine = configuredEngines.first { $0.url == id }! var engines = Settings.shared.searchEngines engines.removeValue(forKey: engine.name) Settings.shared.searchEngines = engines if engine.isDefault { // Pick another default if let firstEngine = engines.first { Settings.shared.defaultSearchEngineName = firstEngine.key } } reloadConfiguredEngines() onChangeEngines() } func makeDefault(_ id: ConfiguredEngine.ID) { let engineName = configuredEngines.first { $0.url == id }!.name Settings.shared.defaultSearchEngineName = engineName reloadConfiguredEngines() onChangeEngines() } } struct ConfiguredEngine: Identifiable { let name: String let url: String let isDefault: Bool var id: String { url } } } @MainActor class ManageSearchEnginesViewController: UIHostingController { let model: ManageSearchEnginesView.ViewModel init() { self.model = ManageSearchEnginesView.ViewModel() super.init(rootView: ManageSearchEnginesView(model: model)) } required dynamic init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } #Preview { @Previewable @State var model = ManageSearchEnginesView.ViewModel() ManageSearchEnginesView(model: model) }