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