UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

UI-related problems caused by filter() with a huge array.

Forums > SwiftUI

Hello all,

After having finished the 100 days of SwiftUI, I'm now working on my own little project. It's an app that should work like a Latin dictionary. It loads Latin words as a JSON file from the Latin WordNet API, then decodes them into swift structs, then shows them to the user in a list with a search bar. I use the .searchable() modifier to enable the user to search, and a computed property that uses filter() to return a modified array based on what the user typed in the search bar. It's about the same as this: https://www.hackingwithswift.com/books/ios-swiftui/making-a-swiftui-view-searchable

However, when I type a word in the search bar, the UI gets very laggy. I think this happens because the array of decoded words is very long. In fact, it contains over 70 000 words. Everytime I type a letter in the search bar, the whole array is filtered for that letter. Now my question is: is there a way to do this without messing up my UI, or should I cut the ultra-XXL-array in smaller pieces, so that those pieces all have their own list with search bar?

Thank you in advance!

2      

What you can try is to try to limit the search to at least 2 or 3 characters. So the subset should be much smaller with every search.

Additionally, you could use CoreData and put the search in a background thread so your UI isn't blocked while searching. But this is a little more complicated.

2      

@Hatsushira, thank you for your reaction!

Since many words aren't longer than 2 or 3 letters, I think the second approach in this case would be the best choice. Could you provide me with some more information on how that works? Although that approach is complicated, I at least want to try it.

Thank you in advance!

2      

You're welcome.

I can recommend "Donny Wals' Practical Core Data". When you have never worked with this framework you need to know the fundamentals before using the advanced techniques like background threads. I can't post a link to Donny's book, so please use Google and search for it.

You can start with Paul's tutorial on CoreData as well.

2      

I will add two cents from my side :) without diminishing Donny's work, his style is rather diffucult for understanding especially for a beginner. Many examples are taken out of context, and if you don't have some experience in working with real apps you can be easily lost. Also jumping back and forth between UIKit and SwiftUI is also not really clear while you are in the early stage of learning curve. But book is definitely great and in my opinion is suitable for those who wants to understand Core Data deeper. The one that really helped me to grasp the idea and how it all pieces together is BigMountainStudio CoreDataMastery. So many visuals and examples that you can see and work with them. I am not associated with the company and the author of the book in any way so just use my words as a recommendation, for beginner it is a gem i think.

And to the point of your question. Maybe as a solution you could use combine, if you have any understanding of this. If you use observable object as you datamodel you can use "searchText" as Publisher. The publisher will publish data as soon as you start typing in textfield. You can attach .debounce operator and indicate delay for publishing data. So when you type several letters it will wait for you to finish typing and only then will publish the data. So further you can .map it with filtered results and assign it to another published property instead of using your computed property. And that will display the results on UI. The combine will help you not to start searching the whole list with every typed in letter. So you type in "a" it starts search, then you type in "b" it starts search. Definitely so many searches to complete. But combine will help you to consolidate your request so you type several letters and after a short delay say 0.3 sec it starts to search for result. Well, much less load on your searching process....

Again all depends on the how you implementing you project. As @Hatsushira mentioned it is maybe better to download data, save it in CoreData and then use it for your needs. Moreover if you just download it once and save it. But in any case it requires preparatory work to make it work. Not sure which is faster CoreData or Combine way...

2      

Found an easier solution. It's possible to just make a view modifier that's like .searchable(), but has extra parameters and modifiers to control what the search action is and when it should be executed. I wrote this:

extension View {
    func searchBar<T: Equatable>(text: Binding<String>, prompt: String, performSearchOnChangeOf equatable: T = true, searchAction: @escaping () -> Void) -> some View {
        VStack {
            HStack {
                TextField(prompt, text: text)
                    .autocorrectionDisabled()

                Spacer()

                Image(systemName: "magnifyingglass")
                    .foregroundColor(.secondary)
                    .accessibilityHidden(true)
            }
            .padding(10)
            .background {
                RoundedRectangle(cornerRadius: 5)
                    .fill(.bar)
            }
            .padding(10)
            .onSubmit(searchAction)
            .onAppear(perform: searchAction)
            .onChange(of: equatable) { _ in
                searchAction()
            }

            self
                .shadow(radius: 2)

            Color.clear
                .frame(height: 1)
        }
    }
}

Now the array will only be filtered when the user presses enter, rather than every time they type a letter.

However, I'm definitely going to look at CoreData's background thread technique. I already know how to permanently save data with CoreData (Paul taught me🙂), but I don't know much about its other functionalities.

Thank you both for your help!

2      

The solution I mentioned above works, but now I'm facing another problem: loading the JSON works fine when running the app in simulator, but when running the app on my iPhone 8 it takes a lot of time. I could of course load the data when the user first starts the app, then save it somewhere (CoreData or just in the documents directory), which would be much more efficient, but wouldn't make the loading process any faster. Do you have any idea how I could load the data faster? Is it even possible?

2      

i have done this solution as i was facing the same issue but after this as i run this on my iphone 7plus it take alote of time to load as it load all previous raw data as well.

2      

thanks for recommending such an informative platform i have learned much from this.

2      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.