Error Type '() -> ()' cannot conform to 'StringProtocol'

Bonjour I am new in swift and Xcode I want to use the search bar to fetch the titles following what is typed

I get this error:

Type '() -> ()' cannot conform to 'StringProtocol'

import SwiftUI
import SQLite3

struct ContentView: View {
    @State var cantiques = [[String: String]]()
    @State private var searchText = ""

    var body: some View {
        NavigationView {
            VStack {
                List(cantiques, id: \.self) { cantique in
                    NavigationLink(
                        destination: hymnesetlouangesafficher(cantique: cantique),
                        label: {
                            Text("\(cantique["ref"] ?? "") - \(cantique["titre"] ?? "")")
                        })
                }
            }
            .navigationTitle("Hymnes et Louanges")
            .searchable(text: $searchText) { searchText in
                fetchCantiqueTitles(searchText: searchText)
            }
        }
        .onAppear {
            fetchCantiqueTitles(searchText: "")
        }
    }

    func fetchCantiqueTitles(searchText: String) {
        cantiques.removeAll()
        
        var db: OpaquePointer?
        let dbPath = Bundle.main.path(forResource: "Hymnes_et_Louanges", ofType: "db")!
        if sqlite3_open(dbPath, &db) == SQLITE_OK {
            print("Database opened successfully")
        } else {
            print("Database not opened - problem")
            return
        }
        
        let query = "SELECT ref, titre FROM cantiques WHERE titre LIKE '%\(searchText)%'"

        var statement: OpaquePointer?
        if sqlite3_prepare_v2(db, query, -1, &statement, nil) == SQLITE_OK {
            while sqlite3_step(statement) == SQLITE_ROW {
                if let ref = sqlite3_column_text(statement, 0), let titre = sqlite3_column_text(statement, 1) {
                    let refString = String(cString: ref)
                    let titreString1 = String(cString: titre)
                    let titreString2 = titreString1.replacingOccurrences(of: "\\n", with: "")
                    let titreString3 = titreString2.replacingOccurrences(of: "`", with: "'")
                    let titreString = titreString3.replacingOccurrences(of: "...", with: "")
                    cantiques.append(["ref": refString, "titre": titreString])
                }
            }
        } else {
            print("Error preparing query")
        }

        sqlite3_finalize(statement)
        sqlite3_close(db)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Merci bien

Answered by Claude31 in 747861022

If I understand your point, you should

  • Change the func
    func fetchCantiqueTitles(searchText: String) {

to return an array [Cantique]

typealias Cantique = [String: String]
    func fetchCantiqueTitles(searchText: String) -> [Cantique] {  // <<-- ADD return value
       var fetchedCantiques = [Cantique]()  // <<-- CHANGE HERE  cantiques.removeAll()
        
        var db: OpaquePointer?
        let dbPath = Bundle.main.path(forResource: "Hymnes_et_Louanges", ofType: "db")!
        if sqlite3_open(dbPath, &db) == SQLITE_OK {
            print("Database opened successfully")
        } else {
            print("Database not opened - problem")
            return
        }
        
        let query = "SELECT ref, titre FROM cantiques WHERE titre LIKE '%\(searchText)%'"

        var statement: OpaquePointer?
        if sqlite3_prepare_v2(db, query, -1, &statement, nil) == SQLITE_OK {
            while sqlite3_step(statement) == SQLITE_ROW {
                if let ref = sqlite3_column_text(statement, 0), let titre = sqlite3_column_text(statement, 1) {
                    let refString = String(cString: ref)
                    let titreString1 = String(cString: titre)
                    let titreString2 = titreString1.replacingOccurrences(of: "\\n", with: "")
                    let titreString3 = titreString2.replacingOccurrences(of: "`", with: "'")
                    let titreString = titreString3.replacingOccurrences(of: "...", with: "")
                    fetchedCantiques.append(["ref": refString, "titre": titreString]) // <<-- CHANGE HERE cantiques.append(["ref": refString, "titre": titreString])
                }
            }
        } else {
            print("Error preparing query")
        }

        sqlite3_finalize(statement)
        sqlite3_close(db)

        return fetchedCantiques  // <<-- ADD THIS
    }
  • change the filteredCantiques var:
    var filteredCantiques: [Cantique] { 
        if searchText.isEmpty {
            return cantiques
        } else {
          return fetchCantiqueTitles(searchText: searchText)
        }
    }
  • And change the body of ContentView:
    var body: some View {
        NavigationView {
            VStack {
                List(filteredCantiques, id: \.self) { cantique in   // <<-- CHANGE HERE
                    NavigationLink(
                        destination: hymnesetlouangesafficher(cantique: cantique),
                        label: {
                            Text("\(cantique["ref"] ?? "") - \(cantique["titre"] ?? "")")
                        })
                }
            }
            .navigationTitle("Hymnes et Louanges")
            .searchable(text: $searchText) // <<-->> CHANGE { searchText in fetchCantiqueTitles(searchText: searchText) }
        }
        .onAppear {
            cantiques = fetchCantiqueTitles(searchText: "")  // <<-- CHANGE
        }

Hopefully I did not forget anything…

Où est l'erreur (quelle ligne) ? Sans doute ici : .searchable(text: $searchText) { searchText in fetchCantiqueTitles(searchText: searchText) }

Bienvenue au Forum.

What is this syntax ? What is the closure ? What do you want to do with this closure ?

.searchable(text: $searchText) { 
   searchText in fetchCantiqueTitles(searchText: searchText)
}

If you remove, it should compile:

            .searchable(text: $searchText) // { searchText in fetchCantiqueTitles(searchText: searchText) }

Note: it would be wise to define a typealias:

typealias Cantique = [String: String]

If you want to filter the List according to search, you could:

  • define a computed var with the filtered array:
    var filteredCantiques: [Cantique] { 
        if searchText.isEmpty {
            return cantiques
        } else {
            return cantiques.filter {
               // filter as appropriate :
               // what does a Cantique dictionary need to contain to correspond to the search ? 
               // May be for instance that Cantique dictionary contains an entry for searchText ? In that case:
               $0[searchText] != nil
            }
            
        }
    }
  • Modify List:
   List(filteredCantiques, id: \.self) { cantique in

Sorry I am really new in swift and Xcode... I am learning I am a php and android developper Please forgive me

when I put only .searchable(text: $searchText) the code works well, all the titles are there, but the search bar do not seems to call or change the function fetchCantiqueTitles(searchText: String)

the error is shown for this line: .navigationTitle("Hymnes et Louanges")

if I remove this line, the error will still appear but for VStack {

infos on this code, when a item is clicked, the new view destination: hymnesetlouangesafficher(cantique: cantique) is call

I pass the item id to this view

so what I want to do:

when the view starts, the function fetchCantiqueTitles(searchText: String) is called all the titles ar shown

What I want:

when something is typed in the search bar, this entry is sent to the function fetchCantiqueTitles(searchText: String) only the titles containing the entry are shown let query = "SELECT ref, titre FROM cantiques WHERE titre LIKE '%(searchText)%'"

there is a better way to do this?

Maybe my code is a strange mess

English is my second language

I tried to explain my situation the best I could

Accepted Answer

If I understand your point, you should

  • Change the func
    func fetchCantiqueTitles(searchText: String) {

to return an array [Cantique]

typealias Cantique = [String: String]
    func fetchCantiqueTitles(searchText: String) -> [Cantique] {  // <<-- ADD return value
       var fetchedCantiques = [Cantique]()  // <<-- CHANGE HERE  cantiques.removeAll()
        
        var db: OpaquePointer?
        let dbPath = Bundle.main.path(forResource: "Hymnes_et_Louanges", ofType: "db")!
        if sqlite3_open(dbPath, &db) == SQLITE_OK {
            print("Database opened successfully")
        } else {
            print("Database not opened - problem")
            return
        }
        
        let query = "SELECT ref, titre FROM cantiques WHERE titre LIKE '%\(searchText)%'"

        var statement: OpaquePointer?
        if sqlite3_prepare_v2(db, query, -1, &statement, nil) == SQLITE_OK {
            while sqlite3_step(statement) == SQLITE_ROW {
                if let ref = sqlite3_column_text(statement, 0), let titre = sqlite3_column_text(statement, 1) {
                    let refString = String(cString: ref)
                    let titreString1 = String(cString: titre)
                    let titreString2 = titreString1.replacingOccurrences(of: "\\n", with: "")
                    let titreString3 = titreString2.replacingOccurrences(of: "`", with: "'")
                    let titreString = titreString3.replacingOccurrences(of: "...", with: "")
                    fetchedCantiques.append(["ref": refString, "titre": titreString]) // <<-- CHANGE HERE cantiques.append(["ref": refString, "titre": titreString])
                }
            }
        } else {
            print("Error preparing query")
        }

        sqlite3_finalize(statement)
        sqlite3_close(db)

        return fetchedCantiques  // <<-- ADD THIS
    }
  • change the filteredCantiques var:
    var filteredCantiques: [Cantique] { 
        if searchText.isEmpty {
            return cantiques
        } else {
          return fetchCantiqueTitles(searchText: searchText)
        }
    }
  • And change the body of ContentView:
    var body: some View {
        NavigationView {
            VStack {
                List(filteredCantiques, id: \.self) { cantique in   // <<-- CHANGE HERE
                    NavigationLink(
                        destination: hymnesetlouangesafficher(cantique: cantique),
                        label: {
                            Text("\(cantique["ref"] ?? "") - \(cantique["titre"] ?? "")")
                        })
                }
            }
            .navigationTitle("Hymnes et Louanges")
            .searchable(text: $searchText) // <<-->> CHANGE { searchText in fetchCantiqueTitles(searchText: searchText) }
        }
        .onAppear {
            cantiques = fetchCantiqueTitles(searchText: "")  // <<-- CHANGE
        }

Hopefully I did not forget anything…

Super I figured out some little things but you did a great job It works

really thank you

I think I started to understand the process Coming from PHP and Android Studio Java, I think swift is really different But with hard work I will get it This is the way things works

Merci beaucoup Claude31

Error Type '() -> ()' cannot conform to 'StringProtocol'
 
 
Q