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 5eb26f7
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 98 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
46 changes: 23 additions & 23 deletions Domain/QuranTextKit/Tests/CompositeSearcherTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ class CompositeSearcherTests: XCTestCase {
}

func testNumbers() async throws {
try await autocompleteNumber("4")
try await autocompleteNumber("44")
try await autocompleteNumber("444")
try await autocompleteNumber("4444")
try await autocompleteNumber("3:4")
await autocompleteNumber("4")
await autocompleteNumber("44")
await autocompleteNumber("444")
await autocompleteNumber("4444")
await autocompleteNumber("3:4")

try await testSearch(term: "1")
try await testSearch(term: "33")
Expand All @@ -59,52 +59,52 @@ class CompositeSearcherTests: XCTestCase {
}

func testMatchOneSura() async throws {
try await testAutocomplete(term: "Al-Ahzab")
await testAutocomplete(term: "Al-Ahzab")
try await testSearch(term: "Al-Ahzab")
}

func testMatchMultipleSuras() async throws {
try await testAutocomplete(term: "Yu")
await testAutocomplete(term: "Yu")
try await testSearch(term: "Yu")
}

func testMatchSuraAndQuran() async throws {
try await testAutocomplete(term: "الفات") // Al-fatiha
try await testAutocomplete(term: "الاحۡ") // Al-Ahzab
try await testAutocomplete(term: "الأَعۡلَ") // Al-A'la
await testAutocomplete(term: "الفات") // Al-fatiha
await testAutocomplete(term: "الاحۡ") // Al-Ahzab
await testAutocomplete(term: "الأَعۡلَ") // Al-A'la

try await testSearch(term: "الفات") // Al-fatiha
try await testSearch(term: "الاحۡ") // Al-Ahzab
try await testSearch(term: "الأَعۡلَ") // Al-A'la
}

func testMatchArabicSuraName() async throws {
try await testAutocomplete(term: "النحل")
await testAutocomplete(term: "النحل")
try await testSearch(term: "النحل")
}

func testMatchSuraAndQuranWithIncorrectTashkeel() async throws {
try await testAutocomplete(term: "فُتح")
await testAutocomplete(term: "فُتح")
try await testSearch(term: "فُتح")
}

func testMatchArabicQuran() async throws {
let term = "لكنا"
try await testAutocomplete(term: term)
await testAutocomplete(term: term)
try await testSearch(term: term)
}

func testMatchTranslation() async throws {
try await testAutocomplete(term: "All")
await testAutocomplete(term: "All")
try await testSearch(term: "All")
}

func test_autocomplete_allSuras_prefixed() async throws {
try await enumerateAllSuras { sura, language in
func test_autocomplete_allSuras_prefixed() async {
await enumerateAllSuras { sura, language in
// Autocomplete sura name unchanged.
let suraName = sura.localizedName(withPrefix: false, language: language)
let suraNamePrefix = prefixSuraName(suraName)
let result = try await searcher.autocomplete(term: suraNamePrefix, quran: quran)
let result = await searcher.autocomplete(term: suraNamePrefix, quran: quran)
.map { $0.removeInvalidSearchCharacters() }
let strippedSuraName = suraName.removeInvalidSearchCharacters()
XCTAssertTrue(result.contains(strippedSuraName), "[\(language)] \(result) doesn't contain \(strippedSuraName)")
Expand All @@ -119,7 +119,7 @@ class CompositeSearcherTests: XCTestCase {
let cleanedSuraNamePrefix = prefixSuraName(cleanedSuraName)

// Check autocomplete partial pure letters and spaces sura name results contains the full sura name.
let autocompleteResult = try await searcher.autocomplete(term: cleanedSuraNamePrefix, quran: quran)
let autocompleteResult = await searcher.autocomplete(term: cleanedSuraNamePrefix, quran: quran)
.map { $0.removeInvalidSearchCharacters() }
XCTAssertTrue(autocompleteResult.contains(cleanedSuraName), "[\(language)] \(autocompleteResult) doesn't contain \(cleanedSuraName)")

Expand All @@ -141,13 +141,13 @@ class CompositeSearcherTests: XCTestCase {
TestData.sahihTranslation,
]

private func autocompleteNumber(_ number: String) async throws {
let result = try await searcher.autocomplete(term: number, quran: quran)
private func autocompleteNumber(_ number: String) async {
let result = await searcher.autocomplete(term: number, quran: quran)
XCTAssertEqual(result, [number])
}

private func testAutocomplete(term: String, record: Bool = false, testName: String = #function) async throws {
let result = try await searcher.autocomplete(term: term, quran: quran)
private func testAutocomplete(term: String, record: Bool = false, testName: String = #function) async {
let result = await searcher.autocomplete(term: term, quran: quran)

// assert the text
assertSnapshot(matching: result.sorted(), as: .json, record: record, testName: testName)
Expand All @@ -160,7 +160,7 @@ class CompositeSearcherTests: XCTestCase {
assertSnapshot(matching: result, as: .json, record: record, testName: testName)
}

private func enumerateAllSuras(_ block: (Sura, Language) async throws -> Void) async throws {
private func enumerateAllSuras(_ block: (Sura, Language) async throws -> Void) async rethrows {
for language in [Language.arabic, Language.english] {
for sura in quran.suras {
try await block(sura, language)
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
Loading

0 comments on commit 5eb26f7

Please sign in to comment.