In this post, we will see how to use the searchable modifier for filtering data in a list.
We will use Core Data to manage data and, we are going to continue the project created in the post: Swift – Core Data.
The first thing that we will change in the file ContentView.swift, will be to replace the NavigationView with the new component NavigatioStack and then, we will add the searchable modifier:
[CONTENTVIEW.SWIFT]
import SwiftUI
import CoreData
struct ContentView: View {
// with Environment we can access here to the viewContext
@Environment(\.managedObjectContext) private var viewContext
// FetchRequest is used to extract data from the persistent storage
// In this case the entity User without any sort and any predicate
// In sql this would be: select * from user
@FetchRequest(entity: User.entity(), sortDescriptors: [], predicate: nil)
// We put the result of the query above in a variable called lstUser
private var lstUser: FetchedResults<User>
func UpdateUser(inputUser: User) -> Void{
// for the update we pass the object User selected
objUser = inputUser
showNewUser = true
isUpdate = true
}
init() {
_showNewUser = State(initialValue: false)
_isUpdate = State(initialValue: false)
}
// variable used to show the Form for creating a new User
@State var showNewUser: Bool
@State private var objUser: User = User()
@State private var isUpdate: Bool
@State private var searchText: String = ""
var body: some View {
NavigationStack{
List{
// we show all users
ForEach(lstUser) { user in
HStack{
VStack(alignment: .leading){
Text("\(user.name!) - \(user.surname!)")
.font(.headline)
}
Spacer()
Button(action: { self.UpdateUser(inputUser: user)}) {
Text("Update")
.foregroundColor(.blue)
}
}
}
// delete item
.onDelete {
indexSet in
for index in indexSet{
// we delete the user
viewContext.delete(lstUser[index])
}
do {
// save the operation
try viewContext.save()
} catch {
print(error.localizedDescription)
}
}
}
.navigationTitle("Users")
.navigationBarItems(trailing: Button(action: {
isUpdate = false
objUser = User()
showNewUser = true
}, label: {
Image(systemName: "plus.circle")
.imageScale(.large)
}))
.sheet(isPresented: $showNewUser) {
NewUser(inputUser: objUser , isUpdate: isUpdate)
}
// It will add a search bar.
.searchable(text: $searchText, prompt: "Name to search")
}
}
}
If we run the application, the following will be the result:
Now, we will add a method called SearchByNameOrSurname used to filter the list of User by Name or Surname:
private func SearchByNameOrSurname() {
let predicate: NSPredicate?
// If there is something in the search bar, the system will create a predicate
if !searchText.isEmpty {
// We create two predicates: one used to search by name and the second used to
// search by surname
let namePredicate = NSPredicate(format: "name CONTAINS %@", searchText)
let surnamePredicate = NSPredicate(format: "surname CONTAINS %@", searchText)
// The NSCompoundPredicate combines these two predicates using an OR condition, so the
// results will include users that match either the name or surname field.
predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [namePredicate, surnamePredicate])
} else {
predicate = nil
}
// Finally, we will use the predicate to filter the list of Users
lstUser.nsPredicate = predicate
}
Finally, we modify the code in order to call this method when the value in the Search Bar changes:
.searchable(text: $searchText, prompt: "Name to search")
.onChange(of: searchText, perform: { _ in
SearchByNameOrSurname()
})
We have done and now, if we run the application, the following will be the result:
[CONTENTVIEW.SWIFT]
import SwiftUI
import CoreData
struct ContentView: View {
// with Environment we can access here to the viewContext
@Environment(\.managedObjectContext) private var viewContext
// FetchRequest is used to extract data from the persistent storage
// In this case the entity User without any sort and any predicate
// In sql this would be: select * from user
@FetchRequest(entity: User.entity(), sortDescriptors: [], predicate: nil)
// We put the result of the query above in a variable called lstUser
private var lstUser: FetchedResults<User>
func UpdateUser(inputUser: User) -> Void{
// for the update we pass the object User selected
objUser = inputUser
showNewUser = true
isUpdate = true
}
init() {
_showNewUser = State(initialValue: false)
_isUpdate = State(initialValue: false)
}
// variable used to show the Form for creating a new User
@State var showNewUser: Bool
@State private var objUser: User = User()
@State private var isUpdate: Bool
@State private var searchText: String = ""
var body: some View {
NavigationStack{
List{
// we show all users
ForEach(lstUser) { user in
HStack{
VStack(alignment: .leading){
Text("\(user.name!) - \(user.surname!)")
.font(.headline)
}
Spacer()
Button(action: { self.UpdateUser(inputUser: user)}) {
Text("Update")
.foregroundColor(.blue)
}
}
}
// delete item
.onDelete {
indexSet in
for index in indexSet{
// we delete the user
viewContext.delete(lstUser[index])
}
do {
// save the operation
try viewContext.save()
} catch {
print(error.localizedDescription)
}
}
}
.navigationTitle("Users")
.navigationBarItems(trailing: Button(action: {
isUpdate = false
objUser = User()
showNewUser = true
}, label: {
Image(systemName: "plus.circle")
.imageScale(.large)
}))
.sheet(isPresented: $showNewUser) {
NewUser(inputUser: objUser , isUpdate: isUpdate)
}
.searchable(text: $searchText, prompt: "Name to search")
.onChange(of: searchText, perform: { _ in
SearchByNameOrSurname()
})
}
}
private func SearchByNameOrSurname() {
let predicate: NSPredicate?
// If there is somethingin the search bar the system will create
// a predicate
if !searchText.isEmpty {
// We create two predicates: one used to seacrh the name and the second
// used to search for surname
let namePredicate = NSPredicate(format: "name CONTAINS %@", searchText)
let surnamePredicate = NSPredicate(format: "surname CONTAINS %@", searchText)
// The NSCompoundPredicate combines these two predicates using an OR condition, so the
// results will include users that match either the name or surname field.
predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [namePredicate, surnamePredicate])
} else {
predicate = nil
}
// we will use the predicate to filter the list of Users
lstUser.nsPredicate = predicate
}
}