1

The progressview() does not activate first time through my custom search view but works great on every subsequent search operation.

I have a firebase database backend and am using async/await functionality.

I'm trying to make the progressview appear when the user clicks the "Search" button.

Here is a minimal reproducible code example...

    import SwiftUI

import Firebase
import FirebaseFirestore
import FirebaseAuth
import Combine


 


struct PlayGroupSearchCoursesView: View {
    
    @StateObject var playGroupViewModel  = PlayGroupViewModel()
    @StateObject var courseListViewModel = CourseListViewModel()
    @State var searchText = ""
    @State var isFetching  = false
    @State var activeSearchAttribute: Int = 0
 
    
    var body: some View {
        
        
        NavigationView {
            
            ScrollView {
                
                HStack {
                    HStack {
                        TextField("Search", text: $searchText)
                            .textCase(.lowercase)
                            .autocapitalization(.none)
                            .padding(.leading, 24)
                            .textFieldStyle(RoundedBorderTextFieldStyle())
                        
                    }
                    .background(Color(.systemGray5))
                    .cornerRadius(8)
                    .padding(.horizontal)
                    // overlay (could have used zstack) the icons into the search bar itself
                    
                    
                    Button(action: {
                        
                        // this shows spinner on 2nd and all searches thereafter
                        // it DOES NOT show the spinner in during the first search.
                        isFetching = true
                        print("fetch started")
                        searchText = searchText.lowercased()
                        Task.init {
                            await self.courseListViewModel.reloadSearchCourses(searchText: searchText, nameOrCity: activeSearchAttribute)
                            isFetching = false   // as soon as the search results return, dismiss the spinner.
                            print("fetch complete...")
                        }
                        
                        // this works perfectly every time
//                        isFetching = true
//                        print("stalling for 3 seconds")
//                        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
//                            print("3 seconds are up...")
//                            isFetching = false
//                        }
                        
                    }, label: {
                        Text("Search")
                            .padding(.trailing)
                        
                    })
                }
                
                
                ZStack { //this will always show ProgressView(). won't get blocked.
                    Color.green.edgesIgnoringSafeArea(.all)
                }
                
                
                if (isFetching) {
                    ZStack { //this will always show ProgressView(). won't get blocked.
                        Color.red.edgesIgnoringSafeArea(.all)
                    }
                    ZStack {
                        Color(.systemBackground)
                        // .opacity(0.7)
                            .ignoresSafeArea()
                        ProgressView("Please wait...")
                            .progressViewStyle(CircularProgressViewStyle(tint: .blue))
                            .scaleEffect(1.5)
                            .padding()
                    }
                }
                
                
                //
                // present a list of search results
                //
                ForEach(0...10, id: \.self) { idx in
                    
                    Text("I found this many courses: \(courseListViewModel.searchCourses.count)")
                    
                }
                
                
            }
            .navigationTitle("Golf Course Search")
            
            
        } // nav view
        
        
        
    }
    

}



struct PlayGroupSearchCoursesView_Previews: PreviewProvider {
    static var previews: some View {
        PlayGroupSearchCoursesView()
    }
}



// this foreach above (not shown) simply lists out the array of search results > 

               

Again, this all works perfect the 2nd time the user searches for something. The first search works great and returns the results but no spinner appears for that first search.

Thanks.

Here is a screenshot of the search form and the progressView when it's working.

progressview image

this is the search results once returned from firestore

phil
  • 993
  • 1
  • 10
  • 34
  • 1
    show us a minimum reproducible example code that shows your issue. At the moment, we have two bits of code out of context. – workingdog support Ukraine Aug 02 '22 at 02:02
  • ok, thanks. editing my post to show the full view struct. – phil Aug 02 '22 at 02:05
  • wow, `minimum reproducible example code`. I was thinking `minimum`, not including the kitchen sink. I suggest you do a bit of restructuring of the code, break it into smaller elements. I think your issue arises from the confusing way all this code is setup, eg, which bracket/zstack belongs to what part. – workingdog support Ukraine Aug 02 '22 at 02:38
  • 1
    Re-provide your code with the smallest as possible, but still contains your UI, so we can study this problem better. Now, I am also lost. – Steven-Carrot Aug 02 '22 at 02:56
  • ok, thanks, y'all. I winnowed it down a bunch – phil Aug 02 '22 at 03:10
  • NavigationView{…..}.overlay{ ProgressView() }. Put overlay after the NavigationView not after ZStack{}. – Steven-Carrot Aug 02 '22 at 05:08
  • You guys have been great - thanks for the patience with me. I have spent the time now to create a reproducible and minimal example. See the comments in the code where I show what DOES work and what DOES not work. – phil Aug 02 '22 at 15:05
  • @Phil are you sure this MRE can represent your original problem? I just tested this code, the first search click showed `ProgressView()` or what you called it spinner. – Steven-Carrot Aug 02 '22 at 16:22
  • What!!! wait... i'm curious how you represented my call to " await self.courseListViewModel.reloadSearchCourses()" . When I use the dispatchqueue technique to pause on the main thread for 3 seconds, everything works like a charm. But when I call my 'await func ()' within a Task.init{} contect, the first progressview() is somehow supressed. What sort of await / async function are you calling and how did you structure your call to it? (via tast.init{}. ?) – phil Aug 02 '22 at 17:16

2 Answers2

1

Sometimes, your ProgressView() can be blocked by some random view because you did not notice. Usually, place your ProgressView() as overlay on top of the main stack might solve this accident issue.

struct  CustomView: View {
 var body: some View {
    ZStack { //this will always show ProgressView(). won't get blocked.
        Color.red.edgesIgnoringSafeArea(.all)
    }
    .overlay {
        ProgressView()
    }
    /* //this will not show ProgressView()
    ZStack {
        ProgressView()
        Color.red.edgesIgnoringSafeArea(.all)
    }*/
  }
}
Steven-Carrot
  • 2,368
  • 2
  • 12
  • 37
  • well, shoot, I'd hoped that was it but no difference. I placed the progressview as an overlay on a number of Z and V stacks to no avail. Same issue. Thanks, tail. is there more code I you think I should post? – phil Aug 02 '22 at 01:55
  • Try printing the var `isFetching` after you assign it as true, and see if it really changed. Also, instead of ProgressView(), try displaying some other views at some where else with the condition of `isFetching = true`. If it does not show up, then your data fetching and async are the problem. – Steven-Carrot Aug 02 '22 at 02:04
  • so, isFetching printed 'true'. So I also added another "if (isFetching) {showDashboardView()} and that exhibited the same symptom. i.e. first time I clicked on search button - no dashboardview... All subsequent clicks on the search button rendered the dashboardview. – phil Aug 02 '22 at 02:28
  • try `overlay` on the top-level stack `NavigationView`. I just saw your new added code, – Steven-Carrot Aug 02 '22 at 02:31
  • no change. It did have the effect of showing the progress spinner on a clear background screen. But still no progressview/spinner offered during initial search. – phil Aug 02 '22 at 02:38
  • yes... once results are returned, the user can select one or simply enter a new search string and tap "Search" again. – phil Aug 02 '22 at 02:54
  • @phil I still think you did not follow my instruction clearly. I saw your code wrong twice. You are supposed to put it like this NavigationView{…..}.overlay { ProgressView()} – Steven-Carrot Aug 02 '22 at 05:06
  • Sorry, should have been more clear - i did put it on the end of navigatioinview, it presented the spinner on "2nd and all subsequent" searches in a bit of a different manner (the screen cleared like a modal and the spinner was in the center). It looked nice but it did not appear at all for the initial search. I prefer the way the spinner displays near the bottom of the view - after the search controls so I put it back. – phil Aug 02 '22 at 16:11
  • I have made quite a significant effort to create a reproducible version which is now above. see comments in the part of the code that works one way but not when I make my own await call. – phil Aug 02 '22 at 16:12
1

Most probably it is because your default

@State var isSearching = true  // << here !!

is true, so when you start searching and make it true there is no change and view is not refreshed and you don't see anything at first time. Following toggling makes things work.

So, simple make it false by default

@State var isSearching = false  // << here !!

But as always, assumptions are not reliable - actually minimal reproducible example needed, take it into account for future posts.

Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Thanks, Asperi. The isSearching state var was a red herring - i've removed in the final 'minimal reproducible example'. Thanks very much for sticking with me here. I think I have created a MRE that you can do a quick test with! Updating the code now. – phil Aug 02 '22 at 15:00