Skip to content

Commit

Permalink
Ignore autocomplete errors
Browse files Browse the repository at this point in the history
  • Loading branch information
mohamede1945 committed Dec 16, 2023
1 parent cc49b3c commit 67a472a
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,19 @@ public struct CompositeSearcher {

// MARK: Public

public func autocomplete(term: String, quran: Quran) async throws -> [String] {
public func autocomplete(term: String, quran: Quran) async -> [String] {
guard let term = SearchTerm(term) else {
return []
}
logger.info("Autocompleting term: \(term.compactQuery)")

let autocompletions = try await simpleSearchers.asyncMap { searcher in
try await searcher.autocomplete(term: term, quran: quran)
let autocompletions = await simpleSearchers.asyncMap { searcher in
try? await searcher.autocomplete(term: term, quran: quran)
}
var results = autocompletions.flatMap { $0 }
var results = autocompletions.compactMap { $0 }.flatMap { $0 }

if shouldPerformTranslationSearch(simpleSearchResults: results, term: term.compactQuery) {
results += try await translationsSearcher.autocomplete(term: term, quran: quran)
results += (try? await translationsSearcher.autocomplete(term: term, quran: quran)) ?? []
}
if !results.contains(term.compactQuery) {
results.insert(term.compactQuery, at: 0)
Expand Down
35 changes: 35 additions & 0 deletions Features/SearchFeature/SearchTypes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// SearchTypes.swift
//
//
// Created by Mohamed Afifi on 2023-12-16.
//

import Foundation

enum SearchUIState {
case entry
case autocomplete
case loading
case searchResults
}

enum SearchTerm {
case autocomplete(_ term: String)
case noAction(_ term: String)

// MARK: Internal

var term: String {
switch self {
case .autocomplete(let term), .noAction(let term): return term
}
}

var isAutocomplete: Bool {
switch self {
case .autocomplete: return true
case .noAction: return false
}
}
}
22 changes: 11 additions & 11 deletions Features/SearchFeature/SearchView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ struct SearchView: View {
var body: some View {
SearchViewUI(
error: $viewModel.error,
type: viewModel.searchType,
state: viewModel.uiState,
term: viewModel.searchTerm.term,
recents: viewModel.recents,
populars: viewModel.populars,
autocompletions: viewModel.autocompletions,
searchResults: viewModel.searchResults,
start: { await viewModel.start() },
search: { await viewModel.search(searchTerm: $0) },
search: { viewModel.search(for: $0) },
selectSearchResult: { viewModel.select(searchResult: $0.result, source: $0.source) }
)
}
Expand All @@ -34,7 +34,7 @@ struct SearchView: View {
private struct SearchViewUI: View {
@Binding var error: Error?

let type: SearchUIType
let state: SearchUIState

let term: String
let recents: [String]
Expand All @@ -48,7 +48,7 @@ private struct SearchViewUI: View {

var body: some View {
Group {
switch type {
switch state {
case .entry:
entry
case .autocomplete:
Expand Down Expand Up @@ -186,14 +186,14 @@ struct SearchView_Previews: PreviewProvider {
])

@State var results = [populatedResults]
@State var type = SearchUIType.searchResults
@State var state = SearchUIState.searchResults
@State var error: Error?

var body: some View {
NavigationView {
SearchViewUI(
error: $error,
type: type,
state: state,
term: "is",
recents: ["Recent 1", "Recent 2"],
populars: ["Popular 1", "Popular 2"],
Expand All @@ -207,17 +207,17 @@ struct SearchView_Previews: PreviewProvider {
.toolbar {
ScrollView(.horizontal) {
HStack {
Button("Entry") { type = .entry }
Button("Autocomplete") { type = .autocomplete }
Button("Entry") { state = .entry }
Button("Autocomplete") { state = .autocomplete }
Button("Search") {
type = .searchResults
state = .searchResults
results = [Self.populatedResults]
}
Button("No Results") {
type = .searchResults
state = .searchResults
results = []
}
Button("Loading") { type = .loading }
Button("Loading") { state = .loading }
Button("Error") { error = URLError(.notConnectedToInternet) }
}
}
Expand Down
6 changes: 2 additions & 4 deletions Features/SearchFeature/SearchViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,12 @@ final class SearchViewController: UIViewController, UISearchResultsUpdating, UIS
func updateSearchResults(for searchController: UISearchController) {
let term = searchController.searchBar.text ?? ""
if viewModel.searchTerm.term != term {
viewModel.searchTerm = .autocomple(term)
viewModel.searchTerm = .autocomplete(term)
}
}

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
Task {
await viewModel.search()
}
viewModel.searchForUserTypedTerm()
}

// MARK: Private
Expand Down
89 changes: 34 additions & 55 deletions Features/SearchFeature/SearchViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,6 @@ import ReadingService
import TranslationService
import VLogging

enum SearchUIType {
case entry
case autocomplete
case loading
case searchResults
}

struct SearchTerm {
let term: String
let autocomplete: Bool

static func autocomple(_ term: String) -> Self {
SearchTerm(term: term, autocomplete: true)
}

static func noAction(_ term: String) -> Self {
SearchTerm(term: term, autocomplete: false)
}
}

@MainActor
final class SearchViewModel: ObservableObject {
// MARK: Lifecycle
Expand All @@ -49,9 +29,9 @@ final class SearchViewModel: ObservableObject {

@Published var error: Error? = nil

@Published var searchType = SearchUIType.entry
@Published var uiState = SearchUIState.entry

@Published var searchTerm = SearchTerm(term: "", autocomplete: false)
@Published var searchTerm = SearchTerm.noAction("")
@Published var autocompletions: [String] = []
@Published var searchResults: [SearchResults] = []
@Published var recents: [String] = []
Expand All @@ -63,7 +43,7 @@ final class SearchViewModel: ObservableObject {
func start() async {
async let reading: () = observeReadingChanges()
async let autocomplete: () = observeSearchTermChanges()
async let recents: () = observeRecentChanges()
async let recents: () = observeRecentSearchItemsChanges()
_ = await [reading, autocomplete, recents]
}

Expand All @@ -86,14 +66,32 @@ final class SearchViewModel: ObservableObject {
navigateTo(searchResult.ayah)
}

func search(searchTerm term: String) async {
func searchForUserTypedTerm() {
search(for: searchTerm.term)
}

func search(for term: String) {
resignSearchBar.send()
searchTerm = .noAction(term)
await search()

Task {
await search()
}
}

func search() async {
searchType = .loading
// MARK: Private

private let analytics: AnalyticsLibrary
private let searchService: CompositeSearcher
private let navigateTo: (AyahNumber) -> Void

private let recentsService = SearchRecentsService.shared
private let readingPreferences = ReadingPreferences.shared
private let contentStatePreferences = QuranContentStatePreferences.shared
private let selectedTranslationsPreferences = SelectedTranslationsPreferences.shared

private func search() async {
uiState = .loading

let term = searchTerm.term
do {
Expand All @@ -105,7 +103,7 @@ final class SearchViewModel: ObservableObject {
searchResults = results
recentsService.addToRecents(term)

searchType = .searchResults
uiState = .searchResults
}
} catch {
logger.error("Error while searching. Error: \(error)")
Expand All @@ -114,22 +112,11 @@ final class SearchViewModel: ObservableObject {
}
searchResults = []
self.error = error
searchType = .searchResults
uiState = .searchResults
}
}

// MARK: Private

private let analytics: AnalyticsLibrary
private let searchService: CompositeSearcher
private let navigateTo: (AyahNumber) -> Void

private let recentsService = SearchRecentsService.shared
private let readingPreferences = ReadingPreferences.shared
private let contentStatePreferences = QuranContentStatePreferences.shared
private let selectedTranslationsPreferences = SelectedTranslationsPreferences.shared

private func observeRecentChanges() async {
private func observeRecentSearchItemsChanges() async {
let recentsSequence = recentsService.$recentSearchItems
.prepend(recentsService.recentSearchItems)
.values()
Expand All @@ -149,38 +136,30 @@ final class SearchViewModel: ObservableObject {
private func observeSearchTermChanges() async {
let searchTermSequence = $searchTerm
.dropFirst()
.filter(\.autocomplete)
.filter(\.isAutocomplete)
.map(\.term)
.throttle(for: .milliseconds(300), scheduler: DispatchQueue.main, latest: true)
.values()
for await term in searchTermSequence {
if searchType == .loading {
if uiState == .loading {
continue
}

let autocompletions = await autocomplete(term)
if searchTerm.term == term {
self.autocompletions = autocompletions
if !autocompletions.isEmpty {
searchType = .autocomplete
uiState = .autocomplete
} else {
searchType = .entry
uiState = .entry
}
}
}
}

private func autocomplete(_ term: String) async -> [String] {
if term.trimmingCharacters(in: .whitespaces).isEmpty {
return []
}
do {
let quran = readingPreferences.reading.quran
return try await searchService.autocomplete(term: term, quran: quran)
} catch {
logger.error("Error while trying to autocomplete. Error: \(error)")
return []
}
let quran = readingPreferences.reading.quran
return await searchService.autocomplete(term: term, quran: quran)
}
}

Expand Down

0 comments on commit 67a472a

Please sign in to comment.