Activity indicator wheel not consistently appearing every time it should

50 views Asked by At

I have some code that takes a while to write to a database, so I have an activity indicator which only behaves properly the first time it's called. When the user causes this code to run again, the activity indictor only shows briefly once the database work has been finished, instead of during the database work.

The isShowing boolean variable is set to true on the main thread, and the database work is put onto a separate thread.

import SwiftUI

struct LanguagePickerWheel: View {

    @State private var selectedLanguage: String = ""
    @State private var isShowing = false

    var availableLanguages: [String] = []

    @Environment(\.presentationMode) var presentation
    @Environment(\.managedObjectContext) private var viewContext
    
    private func dismiss() {
        self.presentation.wrappedValue.dismiss()
    }
    
    var body: some View {
        GeometryReader { geometry in
            VStack {
                HStack {
                    Button("Cancel") { self.dismiss() }
                        .padding(.top)
                        .padding(.trailing, 125)
                    Button("Select") {
                        DispatchQueue.main.async {
                            isShowing = true
                        }
                        let queue = DispatchQueue(label: "work-queue-1", qos: .userInitiated)
                        queue.async {
                            if selectedLanguage == "" {
                                selectedLanguage = availableLanguages[0]
                            }
                                // submit language to the addAndSaveLanguage method
                            let newLanguage = Language(context: viewContext)
                            newLanguage.name = selectedLanguage
                            newLanguage.setAsRevision = false
                            PersistenceController.shared.saveDB()
                            do {
                                    // This solution assumes you've got the file in your bundle
                                if let path = Bundle.main.path(forResource: "\(selectedLanguage)_English_Over_2500_Words", ofType: "txt") {
                                    let data = try String(contentsOfFile:path, encoding: String.Encoding.utf8)
                                    var arrayOfStrings: [String]
                                    arrayOfStrings = data.components(separatedBy: ";")
                                    
                                    for string in arrayOfStrings {
                                        let newCommonWord = CommonWordThing(context: viewContext)
                                        newCommonWord.native = string.components(separatedBy: "_")[1]
                                        newCommonWord.foreign = string.components(separatedBy: "_")[0]
                                        newCommonWord.ckimage = false
                                        newCommonWord.inUse = false
                                        newCommonWord.typingTestCorrect = 0
                                        newCommonWord.arrangeWordsCorrect = 0
                                        newCommonWord.ckreference = newLanguage.ckrecordname
                                        newCommonWord.attempts = 0
                                        newCommonWord.image = nil
                                        newCommonWord.repetitionInterval = 0
                                        newCommonWord.testsUntilPresented = 0
                                        newCommonWord.setAsRevision = false
                                        newCommonWord.language = newLanguage
                                        
                                        var stringNumber = string.split(separator: "_")[2]
                                        if stringNumber.contains("\r\n") {
                                            stringNumber.removeLast(1)
                                        }
                                        newCommonWord.count = NumberFormatter().number(from: String(stringNumber) as String)?.int64Value ?? 0
                                    }
                                }
                            } catch let err as NSError {
                                    // do something with Error
                                print("Couldn't save new language to database: \(err)")
                            }
                        }
                        PersistenceController.shared.saveDB()
                        isShowing = false
                        self.dismiss()
                    }
                    .padding(.top)
                    .padding(.leading, 125)
                }
                Text("Choose a language:")
                    .font(.title)
                    .padding(.top, 50)
                Picker("Choose a language:", selection: $selectedLanguage, content: {
                    ForEach(Array(availableLanguages), id: \.self) { language in
                        Text(language)
                    }
                })
                .pickerStyle(WheelPickerStyle())
                .padding(.leading)
                .padding(.trailing)
            }
            Text("Please wait, creating common words...")
                .position(x: geometry.frame(in: .local).midX, y: geometry.frame(in: .local).midY)
                .foregroundColor(isShowing ? .gray : .clear)
        }
    }
}
1

There are 1 answers

5
Ptit Xav On

Code in queue is async soma be executed after end of Button action closure :

                Button("Select") {
                    // no need dispatch main as it is already executing in main queue
                    isShowing = true
                    let queue = DispatchQueue(label: "work-queue-1", qos: .userInitiated)
                    print("before async")
                    queue.async {
                        if selectedLanguage == "" {
                            selectedLanguage = availableLanguages[0]
                        }

                        // long time process
                        sleep(6)

                        DispatchQueue.main.async {
                            print("hide message")
                            isShowing = false
                            self.dismiss()
                        }
                        print("end of async")
                    }
                    print("after async")
                    // note : here queue task is running as it is async
                    // so dismiss may be move into queue async
                    // self.dismiss()
                }

EDIT : removed the Task