-1

here is a simple project using a swift data where I saved over 75000 airports.

I'm trying to find the best and faster way to search on this db, right now the search works but it is very very slow and the UI frozen while searching.

How can I optimize the search?

import SwiftUI
import SwiftData
import Combine



struct Airports: View {

    @StateObject var ds = DebounceState(initialValue: "", delay: 1)
    @Environment(\.modelContext) private var mc
    @Query var apt: [Airport]

    var body: some View {
        List{
            ForEach(ds.filterApt){ apt in
                Text(apt.icao)
            }
        }
        .searchable(text: $ds.searchText, prompt: "Search apt")
        .onChange(of: ds.debouncedValue) { oldValue, newValue in
            ds.searchAirport(newValue, mc: mc)
        }
       
    }
}


class DebounceState<Value>: ObservableObject {
    @Published var searchText : Value
    @Published var debouncedValue : Value
    @Published var filterApt : [Airport] = []
   
    var subscriber : AnyCancellable?
    
    init(initialValue: Value, delay: Double) {
        _searchText =  Published(initialValue: initialValue)
        _debouncedValue = Published(initialValue: initialValue)
        subscriber = $searchText
            .debounce(for: .seconds(delay), scheduler: RunLoop.main)
            .assign(to: \.debouncedValue, on: self)
        
    }
    
    func searchAirport(_ airport: String, mc: ModelContext) {
        let uairport = airport.uppercased() // cant find the way for predicate to be case insenitive
        do {
            
            let container = try ModelContainer(for: Airport.self)
            let contex = ModelContext(container)
            
            
            
            print("serach fo \(uairport)")
            let filterPlane = FetchDescriptor<Airport>(
                predicate: #Predicate { input in
                    input.icao.contains(uairport)
            })
            filterApt = try contex.fetch(filterPlane)
           
        }catch {
            fatalError("Fail search")
        }
    }
    
  
}




@Model
class Airport {
    @Attribute(.unique) var id: UUID
    var lastPicked : Date
    var icao : String
    
    init(id: UUID, lastPicked: Date, icao: String) {
        self.id = id
        self.lastPicked = lastPicked
        self.icao = icao
    }
    
    static var all: FetchDescriptor<Airport> {
        let sort = [SortDescriptor(\Airport.icao, order: .forward)]
        var res = FetchDescriptor(sortBy: sort)
        res.fetchLimit = 100
        return res
    }
   
}

Thanks

Damiano Miazzi
  • 1,514
  • 1
  • 16
  • 38
  • Since you already have a Model you can use unit tests to measure the actual performance of both variants. If you "think" it's CoreData, your tests will show this very clearly. If not, your suspect was not the culprit. Please post your performance results, as well as the unit test. Hint: you may also move the _complete_ logic and computation into the Model. – CouchDeveloper Aug 07 '23 at 10:14
  • 1
    If I had 75K objects I would use an asynchronous search to avoid freezing the UI – Joakim Danielson Aug 07 '23 at 10:32
  • 1
    I would leave init out of filtering you are recreating everything. Try something with debounce and get rid of the computed variables. 75k is way too much for the main thread. – lorem ipsum Aug 07 '23 at 11:18
  • Thanks a lot as advised I edited my code and used debounce and it looks like the search is much more efficient and fast, can someone take a lot of my code? Is the correct approach or could I improve or change something? – Damiano Miazzi Aug 08 '23 at 09:00
  • Fetching too many models from your `modelContext` seems like a bad idea. You better configure your `FetchDescriptor` with a `fetchLimit`, and then fetch more results when the user scrolls at the end of your list by updating the `fetchOffset` parameter of your `FetchDescriptor`. – parapote Aug 13 '23 at 04:01

0 Answers0