Mystery Inverted Bool

Can someone help me figure out why the "stillLoading" boolean seems to be the opposite of what it should be? The first print outputs false, and the second outputs true, and this is opposite of what it should be. I also tried using the boolean the other way around as "doneLoading," but when I flipped the values, the outputs were still opposite of what they should have been. Here is a copy-pastable

. I'm pretty new to SwiftUI, so I may be misunderstanding something fundamental. Anyway, any help is greatly appreciated!
import Foundation
import UIKit

class AddParticipantModel: ObservableObject {

    @Published var firstName: String = ""
    @Published var lastName: String = ""
    @Published var dateOfBirth: Date = Date()
    @Published var address1: String = ""
    @Published var address2: String = ""
    @Published var city: String = ""
    @Published var state: String = "NV"
    @Published var zip: String = ""
    
    @Published var showErrorAlert: Bool = false
    @Published var stillLoading: Bool = false

    func addParticipant(authString: String) async {
        
        let participant: Participant = Participant(id: "", approved: ApprovalStatus.Pending, active: false, firstName: firstName, lastName: lastName, dob: dateOfBirth, programStart: Date(), address1: address1, address2: address2, city: city, state: state, zip: zip)
        
        DispatchQueue.main.async {
            self.stillLoading = true
        }
/***********************Printing false*******************************/
        print(stillLoading)
        
        await AddParticipantAction(
            parameters: AddParticipantRequest(
                participant: participant,
                deviceId: UIDevice.current.identifierForVendor!.uuidString,
                authString: authString
            )
        ).call { response in
            //Check for an error response from the API
            if response.errorNumber == 0{
                DispatchQueue.main.async{
                    self.showErrorAlert = false;
                    self.stillLoading = false;
                }
            }
            else if response.errorNumber == 5 || response.errorNumber == 10{
                DispatchQueue.main.async{
                    self.showErrorAlert = false;
                    self.stillLoading = false;
                }
                Auth.shared.logout()
            }
            else{
                DispatchQueue.main.async{
                    self.showErrorAlert = true;
                    self.stillLoading = false;
                }
            }
/***********************Printing true*******************************/
            print(self.stillLoading)
        }
    }
}

Accepted Reply

What @mungbeans said, which is the explanation, plus.

A good way to solve it is to use async await pattern everywhere.

But you can have a fix with asyncAfter (but risky as no guarantee on timing if server is slow for instance). So it is just to see… not for final code.

        DispatchQueue.main.async {
            self.stillLoading = true
        }
/***********************Printing false*******************************  Should now print true ******/
     DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {    // Wait a small time for the first dispatch to be executed
        print(stillLoading)
    }
        
        await AddParticipantAction(
            parameters: AddParticipantRequest(
                participant: participant,
                deviceId: UIDevice.current.identifierForVendor!.uuidString,
                authString: authString
            )
        ).call { response in
            //Check for an error response from the API
            if response.errorNumber == 0{
                DispatchQueue.main.async{
                    self.showErrorAlert = false;
                    self.stillLoading = false;
                }
            }
            else if response.errorNumber == 5 || response.errorNumber == 10{
                DispatchQueue.main.async{
                    self.showErrorAlert = false;
                    self.stillLoading = false;
                }
                Auth.shared.logout()
            }
            else{
                DispatchQueue.main.async{
                    self.showErrorAlert = true;
                    self.stillLoading = false;
                }
            }
/***********************Printing true******************************* Should now print false ******/
     DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {    // Wait longer for other dispatch to be executed
            print(self.stillLoading)
    }
  • Awesome! Thanks so much for the help!

  • Actually, I just changed the DispatchQueue.main.async to DispatchQueue.main.sync. Shouldn't be anything wrong with that, right? Anyway, thanks for your help!

  • Question in fact: why do you even need Dispatch ? If I understand your code, you are alre in the main queue. So try remove a y Dispatch call.

Add a Comment

Replies

DispatchQueue.main.async is, as it says in its name, asynchronous. Therefore your code is not executing in a linear line-by-line manner.

You think the code is being executed in this order don't you:

  1)   DispatchQueue.main.async {
  2)        self.stillLoading = true
        }
  3)   print(stillLoading)

It is NOT the case the line 2 is guaranteed to execute before line 3.

Because line 1 is asynchronous, after 1 has executed, then control will jump to line 3 and meanwhile line 2 is dispatched to execute in parallel.

What @mungbeans said, which is the explanation, plus.

A good way to solve it is to use async await pattern everywhere.

But you can have a fix with asyncAfter (but risky as no guarantee on timing if server is slow for instance). So it is just to see… not for final code.

        DispatchQueue.main.async {
            self.stillLoading = true
        }
/***********************Printing false*******************************  Should now print true ******/
     DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {    // Wait a small time for the first dispatch to be executed
        print(stillLoading)
    }
        
        await AddParticipantAction(
            parameters: AddParticipantRequest(
                participant: participant,
                deviceId: UIDevice.current.identifierForVendor!.uuidString,
                authString: authString
            )
        ).call { response in
            //Check for an error response from the API
            if response.errorNumber == 0{
                DispatchQueue.main.async{
                    self.showErrorAlert = false;
                    self.stillLoading = false;
                }
            }
            else if response.errorNumber == 5 || response.errorNumber == 10{
                DispatchQueue.main.async{
                    self.showErrorAlert = false;
                    self.stillLoading = false;
                }
                Auth.shared.logout()
            }
            else{
                DispatchQueue.main.async{
                    self.showErrorAlert = true;
                    self.stillLoading = false;
                }
            }
/***********************Printing true******************************* Should now print false ******/
     DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {    // Wait longer for other dispatch to be executed
            print(self.stillLoading)
    }
  • Awesome! Thanks so much for the help!

  • Actually, I just changed the DispatchQueue.main.async to DispatchQueue.main.sync. Shouldn't be anything wrong with that, right? Anyway, thanks for your help!

  • Question in fact: why do you even need Dispatch ? If I understand your code, you are alre in the main queue. So try remove a y Dispatch call.

Add a Comment