v1.4.1Deployed
Vocabulary App
The updated SwiftUI source for the native vocabulary app. This release fixes the compilation error by replacing .foregroundColor(.quaternary) with .foregroundStyle(.quaternary), which is the correct modifier for hierarchical styles like .quaternary.
Source code
Swift · 319 lines
ContentView.swift
import SwiftUIimport UIKit // MARK: - Modelsstruct VocabularyWord: Identifiable, Codable, Equatable { let id: UUID let word: String let translation: String let dateAdded: Date init(word: String, translation: String = "", dateAdded: Date = Date()) { self.id = UUID() self.word = word self.translation = translation self.dateAdded = dateAdded }} // MARK: - App Stateclass AppState: ObservableObject { @Published var savedWords: [VocabularyWord] = [ VocabularyWord(word: "Vintage", translation: "经典;复古", dateAdded: Date().addingTimeInterval(-3600)), VocabularyWord(word: "Legacy", translation: "遗产;遗留", dateAdded: Date().addingTimeInterval(-1800)), VocabularyWord(word: "Optimization", translation: "优化", dateAdded: Date()) ] func addWord(_ word: String) { let trimmed = word.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return } if !savedWords.contains(where: { $0.word.lowercased() == trimmed.lowercased() }) { savedWords.insert(VocabularyWord(word: trimmed), at: 0) } } func deleteWord(id: UUID) { savedWords.removeAll { $0.id == id } } func deleteWords(at offsets: IndexSet, from filteredList: [VocabularyWord]) { for index in offsets { let wordId = filteredList[index].id deleteWord(id: wordId) } }} // MARK: - Dictionary Wrapperstruct DictionaryLookupView: UIViewControllerRepresentable { let word: String func makeUIViewController(context: Context) -> UIReferenceLibraryViewController { return UIReferenceLibraryViewController(term: word) } func updateUIViewController(_ uiViewController: UIReferenceLibraryViewController, context: Context) {}} // MARK: - Home Tabstruct HomeView: View { @EnvironmentObject var state: AppState var body: some View { NavigationStack { List { Section { VStack(alignment: .leading, spacing: 12) { Text(currentDateString) .font(.headline) .foregroundColor(.secondary) Text(greeting) .font(.system(size: 34, weight: .bold, design: .rounded)) Text("Welcome back to your vocabulary journey. Focus on your growth and keep expanding your horizons.") .font(.body) .foregroundColor(.secondary) } .padding(.vertical, 8) } Section("Quick Stats") { HStack { Label("Words Saved", systemImage: "book.fill") Spacer() Text("\(state.savedWords.count)") .foregroundColor(.secondary) } } } .navigationTitle("Home") } } private var currentDateString: String { let formatter = DateFormatter() formatter.dateFormat = "EEEE, MMMM d, yyyy" return formatter.string(from: Date()) } private var greeting: String { let hour = Calendar.current.component(.hour, from: Date()) if hour < 12 { return "Good Morning" } if hour < 18 { return "Good Afternoon" } return "Good Evening" }} // MARK: - Search Tabstruct SearchView: View { @EnvironmentObject var state: AppState @State private var searchText = "" @State private var showingLookup = false @State private var lookupWord = "" var body: some View { NavigationStack { Form { Section { TextField("Enter word to lookup...", text: $searchText) .textInputAutocapitalization(.never) .disableAutocorrection(true) .submitLabel(.search) .onSubmit { performLookup() } Button(action: performLookup) { Label("Lookup in System Dictionary", systemImage: "book.fill") } .disabled(searchText.trimmingCharacters(in: .whitespaces).isEmpty) } header: { Text("Native Lookup") } if !searchText.trimmingCharacters(in: .whitespaces).isEmpty { Section { Button(action: { state.addWord(searchText) searchText = "" }) { Label("Save to Library", systemImage: "plus.circle.fill") } } } } .navigationTitle("Search") .sheet(isPresented: $showingLookup) { if !lookupWord.isEmpty { DictionaryLookupView(word: lookupWord) .ignoresSafeArea() } } } } private func performLookup() { let trimmed = searchText.trimmingCharacters(in: .whitespaces) if !trimmed.isEmpty { lookupWord = trimmed showingLookup = true } }} // MARK: - Library Tabenum SortOption: String, CaseIterable, Identifiable { case dateAdded = "Date Added" case alphabetical = "Alphabetical" var id: String { self.rawValue }} struct LibraryView: View { @EnvironmentObject var state: AppState @State private var librarySearch = "" @State private var sortOption: SortOption = .dateAdded @State private var selectedWordForLookup: VocabularyWord? var filteredAndSortedWords: [VocabularyWord] { var result = state.savedWords if !librarySearch.isEmpty { result = result.filter { $0.word.localizedCaseInsensitiveContains(librarySearch) } } switch sortOption { case .dateAdded: result.sort { $0.dateAdded > $1.dateAdded } case .alphabetical: result.sort { $0.word.localizedCaseInsensitiveCompare($1.word) == .orderedAscending } } return result } var body: some View { NavigationStack { List { if filteredAndSortedWords.isEmpty { Text(librarySearch.isEmpty ? "No words saved yet." : "No results for \"\(librarySearch)\"") .foregroundColor(.secondary) } else { ForEach(filteredAndSortedWords) { word in Button { selectedWordForLookup = word } label: { HStack { VStack(alignment: .leading) { Text(word.word) .font(.headline) .foregroundColor(.primary) if !word.translation.isEmpty { Text(word.translation) .font(.subheadline) .foregroundColor(.secondary) } } Spacer() Image(systemName: "chevron.right") .font(.footnote.bold()) .foregroundStyle(.quaternary) } } } .onDelete { offsets in state.deleteWords(at: offsets, from: filteredAndSortedWords) } } } .navigationTitle("Library") .searchable(text: $librarySearch, placement: .navigationBarDrawer(displayMode: .always), prompt: "Search saved words") .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Menu { Picker("Sort By", selection: $sortOption) { ForEach(SortOption.allCases) { option in Text(option.rawValue).tag(option) } } } label: { Label("Sort", systemImage: "arrow.up.arrow.down") } } ToolbarItem(placement: .navigationBarLeading) { EditButton() } } .sheet(item: $selectedWordForLookup) { word in DictionaryLookupView(word: word.word) .ignoresSafeArea() } } }} // MARK: - Settings Tabstruct SettingsView: View { @State private var icloudSync = true var body: some View { NavigationStack { Form { Section("Preferences") { Toggle("iCloud Sync", isOn: $icloudSync) } Section("About") { HStack { Text("App Version") Spacer() Text("1.4.1") .foregroundColor(.secondary) } Text("Optimized for iPad workflows using native system UI components and robust data management.") .font(.footnote) .foregroundColor(.secondary) } } .navigationTitle("Settings") } }} // MARK: - Main Appstruct NativeVocabularyApp: View { @StateObject private var state = AppState() var body: some View { TabView { HomeView() .tabItem { Label("Home", systemImage: "house.fill") } SearchView() .tabItem { Label("Search", systemImage: "magnifyingglass") } LibraryView() .tabItem { Label("Library", systemImage: "text.book.closed.fill") } SettingsView() .tabItem { Label("Settings", systemImage: "gear") } } .environmentObject(state) }} // MARK: - Entry Pointstruct ContentView: View { var body: some View { NativeVocabularyApp() }}What changed
- Fix
Replaced
.foregroundColor(.quaternary)with.foregroundStyle(.quaternary)on the library row chevron, resolving the build error. - Scope
Single, targeted change. All four tabs (Home, Search, Library, Settings) and the native dictionary lookup behave exactly as before.
- Build
Compiles cleanly. Drop the file into an Xcode project and set
ContentViewas the entry view.