How to read and filter large Realm dataset in SwiftUI?

▼魔方 西西 提交于 2021-02-11 12:24:47

问题


I'm storing ~100.000 dictionary entries in a realm database and would like to display them. Additionally I want to filter them by a search field. Now I'm running in a problem: The search function is really inefficient although I've tried to debounce the search.

View Model:

class DictionaryViewModel : ObservableObject {

    let realm = DatabaseManager.sharedInstance

    @Published var entries: Results<DictionaryEntry>?
    @Published var filteredEntries: Results<DictionaryEntry>?
    @Published var searchText: String = ""
    @Published var isSearching: Bool = false
    var subscription: Set<AnyCancellable> = []
    
    init() {
        $searchText
            .debounce(for: .milliseconds(800), scheduler: RunLoop.main) // debounces the string publisher, such that it delays the process of sending request to remote server.
            .removeDuplicates()
            .map({ (string) -> String? in
                if string.count < 1 {
                    self.filteredEntries = nil
                    return nil
                }

                return string
            })
            .compactMap{ $0 } 
            .sink { (_) in   
            } receiveValue: { [self] (searchField) in
                filter(with: searchField)
            }.store(in: &subscription)

        self.fetch()
}

    public func fetch(){
        self.entries = DatabaseManager.sharedInstance.fetchData(type:   DictionaryEntry.self).sorted(byKeyPath: "pinyin", ascending: true)
        self.filteredEntries = entries
    }

    public func filter(with condition: String){
        self.filteredEntries = self.entries?.filter("pinyin CONTAINS[cd] %@", searchText).sorted(byKeyPath: "pinyin", ascending: true)
    }

In my View I'm just displaying the filteredEtries in a ScrollView

The debouncing works well for short text inputs like "hello", but when I filter for "this is a very long string" my UI freezes. I'm not sure whether something with my debounce function is wrong or the way I handle the data filtering in very inefficient.

EDIT: I've noticed that the UI freezes especially when the result is empty.

EDIT 2: The .fetchData() function is just this here:

func fetchData<T: Object>(type: T.Type) -> Results<T>{
    let results: Results<T> = realm.objects(type)
    return results
}

All realm objects have a primary key. The structure looks like this:

@objc dynamic var id: String = NSUUID().uuidString
@objc dynamic var character: String = ""
@objc dynamic var pinyin: String = ""
@objc dynamic var translation: String = ""

override class func primaryKey() -> String {
    return "id"
}

EDIT 3: The filtered results are displayed this way:

ScrollView{
    LazyVGrid(columns: gridItems, spacing: 0){
                    if (dictionaryViewModel.filteredEntries != nil)  {
                                 ForEach(dictionaryViewModel.filteredEntries!){ entry in
                                    Text("\(entry.translation)")
                        
                        }
                    } else {
                        Text("No results found")
                    } 
                }

来源:https://stackoverflow.com/questions/65550898/how-to-read-and-filter-large-realm-dataset-in-swiftui

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!