Search code examples
swiftuisearchable

SwiftUI Search bar - Please help my simplify my code


again maybe a beginner question. I've implemented a search function in my app. But I'm very unhappy with my code. I know I have to have to put my searchText into the .searchable() {...} But also with help from different tutorials my knowledge isn't good enough to do this. Could you please habe a look into my code and give me a hint so I could learn from your solution?

//
//  FavoriteView.swift
//  Blindzeln_Prototyp
//
//  Created by Michael Ecke on 25.01.22.
//

import SwiftUI

struct FavoriteView: View {
    
    @EnvironmentObject var blindzeln: BLINDzeln
    @State private var searchText = ""
    
    var body: some View {
        
        NavigationView {
            List{
                ForEach(blindzeln.favorites, id: \.entryID) { item in
                    
                    if searchText==""{
                    
                    NavigationLink(destination: FavoriteDetailView(item: item)) {
                        VStack(alignment: .leading, spacing: 20.0) {
                            Text(item.title)
                                .font(.largeTitle)
                                .foregroundColor(.primary)
                            Text(item.textBody)
                                .font(.body)
                                .foregroundColor(.secondary)
                                .lineLimit(2)
                            BigDivider()
                        }
                    }.listRowSeparatorTint(.primary)
                        .listRowSeparator(.hidden)
                }
                else {
                    if item.title.localizedCaseInsensitiveContains(searchText) || item.textBody.localizedCaseInsensitiveContains(searchText){
                        NavigationLink(destination: FavoriteDetailView(item: item)) {
                            VStack(alignment: .leading, spacing: 20.0) {
                                Text(item.title)
                                    .font(.largeTitle)
                                    .foregroundColor(.primary)
                                Text(item.textBody)
                                    .font(.body)
                                    .foregroundColor(.secondary)
                                    .lineLimit(2)
                                BigDivider()
                            }
                        }.listRowSeparatorTint(.primary)
                            .listRowSeparator(.hidden)
                    }
                }
                
                }
                .onDelete(perform: delete)
                .onMove(perform: onMove)
            }
            .searchable(text: $searchText) {}
            ```

Solution

  • Without the rest of the code to test, I can't be sure, but you should be able to condense your code like so:

    struct FavoriteView: View {
        
        @EnvironmentObject var blindzeln: BLINDzeln
        @State private var searchText = ""
        
        var body: some View {
            
            NavigationView {
                List{
                    ForEach(blindzeln.favorites.filter { searchText.isEmpty ||($0.title.localizedCaseInsensitiveContains(searchText) || $0.textBody.localizedCaseInsensitiveContains(searchText)) }, id: \.entryID){ item in
                        NavigationLink(destination: FavoriteDetailView(item: item)) {
                            VStack(alignment: .leading, spacing: 20.0) {
                                Text(item.title)
                                    .font(.largeTitle)
                                    .foregroundColor(.primary)
                                Text(item.textBody)
                                    .font(.body)
                                    .foregroundColor(.secondary)
                                    .lineLimit(2)
                                BigDivider()
                            }
                        }.listRowSeparatorTint(.primary)
                            .listRowSeparator(.hidden)
                    }
                }
                .onDelete(perform: delete)
                .onMove(perform: onMove)
            }
            .searchable(text: $searchText) {}
            // Nothing changed past here...
        }
    }
    

    Essentially the filter I set up is this:

    1. if searchText is empty, return TRUE so the item is used;
    2. if searchText is not empty, evaluate the other side of the OR which is 2 conditions with an OR. If either title OR textBody contains searchText, return TRUE so item is used;
    3. if everything returns false, don't use item.

    One last thing, rename your entryID in your model struct to id, make the model struct conform to Identifiable and then your ForEach (leaving out the .filter can be this:

    ForEach(blindzeln.favorites) { item in
    

    as an Identifiable struct does not need to use id: in a ForEach initializer.