ProgressView runs indefinitely because of fetching state

59 Views Asked by At

I'm having a problem with asynchronous data fetch for my ProfileViewModel in order to display all the data in ProfileView. I tried many ways and it ended either in indefinite ProgressView loading or nil error. In the first place I setup sessionStore (authentication state) passing it to ViewModel from my View's .onAppear. Then I would like to fetch firstName and username from Firebase to my ProfileViewModel, which I do in fetchData function which is executed in ProfileView init.

ProfileViewModel:

@MainActor
class ProfileViewModel: ObservableObject {
    @Published var sessionStore: SessionStore?
    private let firestoreManager = FirestoreManager()
    private let firebaseStorageManager = FirebaseStorageManager()
    
    @Published var profile: Profile?
    @Published var profilePicturePhotoURL: URL?
    
    @Published var workouts: [IntervalWorkout]?
    @Published var fetching: Bool = true
    
    init(forPreviews: Bool) {
        [...]
    }
    
    init() {
        [...]
        
        Task {
            try await fetchData()
        }
    }
    
    func setup(sessionStore: SessionStore) {
        self.sessionStore = sessionStore
    }
    
    func fetchData() async throws {
        if sessionStore != nil {
            if sessionStore!.currentUser != nil {
                    print("Fetching Data")
                    
                    let (firstname, username, birthDate, age, country, language, gender, email, profilePictureURL) = try await self.firestoreManager.fetchDataForProfileViewModel(userID: sessionStore!.currentUser!.uid)
                    
                    self.profile = Profile(id: sessionStore!.currentUser!.uid, firstName: firstname, username: username, birthDate: birthDate, age: age, country: country, language: language, gender: gender, email: email, profilePictureURL: profilePictureURL)
                    
                    print(self.profile!.firstName)
                    
                    if profilePictureURL != nil {
                        self.firebaseStorageManager.getDownloadURLForImage(stringURL: profilePictureURL!, userID: sessionStore!.currentUser!.uid) { photoURL in
                             self.profilePicturePhotoURL = photoURL
                            print("Data fetched")
                        }
                    } else {
                        print("Data fetched - no profilePicture")
                    }
                    
                    print("FETCHING: \(self.fetching)")
                    if self.profile != nil {
                        fetching = false
                    }
                
            } else {
                print("Data not fetched - no sessionStore.currentUser")
            }
        } else {
            print("Data not fetched - no sessionStore")
        }
    }
    
    func uploadPhoto(image: UIImage) {
        [...]
    }
    
    func emailAddressChange(oldEmailAddress: String, password: String, newEmailAddress: String, completion: @escaping (() -> ())) {
        [...]
    }
    
    func deleteUserData(completion: @escaping (() -> ())) {
        [...]
    }
}

ProfileView:

struct ProfileView: View {
    @ObservedObject private var profileViewModel = ProfileViewModel()
    @EnvironmentObject private var sessionStore: SessionStore
    @Environment(\.colorScheme) var colorScheme
    
    @State private var image = UIImage()
    
    @State private var shouldPresentAddActionSheet = false
    @State private var shouldPresentImagePicker = false
    @State private var shouldPresentCamera = false
    
    @State private var shouldPresentSettings = false
    
    @State private var tabSelection = 0
    
    init(forPreviews: Bool) {
        if forPreviews {
            self.profileViewModel = ProfileViewModel(forPreviews: true)
        }
    }
    
    var body: some View {
        GeometryReader { geometry in
            let screenWidth = geometry.size.width
            let screenHeight = geometry.size.height
            
            if !self.profileViewModel.fetching {
                ScrollView(.vertical) {
                    HStack {
                        if profileViewModel.profilePicturePhotoURL != nil {
                            AsyncImage(url: profileViewModel.profilePicturePhotoURL!) { image in
                                [...]
                            } placeholder: {
                                [...]
                            }
                        } else {
                            [...]
                        }
                        
                        Spacer(minLength: screenWidth * 0.05)
                        
                        VStack {
                            HStack {
                                Text(profileViewModel.profile != nil ? profileViewModel.profile!.firstName : "Name")
                                    [...]
                                }
                            }
                            .padding(.top, screenHeight * 0.02)
                            
                            HStack {
                                Text(profileViewModel.profile != nil ? profileViewModel.profile!.username : "Username")
                                    .foregroundColor(Color(uiColor: UIColor.lightGray))
                                Spacer()
                            }
                            
                            Spacer()
                        }
                    }
                    .padding()
                    
                    [...]
                }
                [...]
            } else {
                VStack {
                    Spacer()
                    HStack {
                        Spacer()
                        ProgressView("Loading user's data")
                            .progressViewStyle(RingProgressViewStyle())
                        Spacer()
                    }
                    Spacer()
                }
            }
        }
        .onAppear {
            self.profileViewModel.setup(sessionStore: sessionStore)
        }
    }
}

Console output:

Data not fetched - no sessionStore
Data not fetched - no sessionStore
Fetching Data
Data not fetched - no sessionStore
Łukasz
Data fetched - no profilePicture
FETCHING: true
0

There are 0 best solutions below