import SwiftUI
struct UserDataLoaderView: View {
@State private var userName = ""
@State private var userEmail = ""
@State private var posts: [String] = []
@State private var isLoadingProfile = false
@State private var isLoadingPosts = false
@State private var showError = false
@State private var errorMessage = ""
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text("User Data Loader")
.font(.title)
.padding(.bottom)
if isLoadingProfile {
Text("Loading user profile...")
} else {
Text("Name: \(userName)")
Text("Email: \(userEmail)")
}
if isLoadingPosts {
Text("Loading user posts...")
} else {
Text("Posts:")
ForEach(posts, id: \.self) { post in
Text("- \(post)")
}
}
Button("Reload Data") {
loadData()
}
.padding(.top)
}
.padding()
.alert(isPresented: $showError) {
Alert(title: Text("Error"), message: Text(errorMessage), dismissButton: .default(Text("Retry")) {
loadData()
})
}
.onAppear {
loadData()
}
}
func loadData() {
isLoadingProfile = true
isLoadingPosts = true
showError = false
Task {
do {
async let profile = fetchUserProfile()
async let userPosts = fetchUserPosts()
let (profileResult, postsResult) = try await (profile, userPosts)
userName = profileResult.name
userEmail = profileResult.email
posts = postsResult
isLoadingProfile = false
isLoadingPosts = false
} catch {
isLoadingProfile = false
isLoadingPosts = false
errorMessage = error.localizedDescription
showError = true
}
}
}
func fetchUserProfile() async throws -> (name: String, email: String) {
try await Task.sleep(nanoseconds: 1_000_000_000) // 1 second delay
if Bool.random() { // Simulate random error
throw NSError(domain: "ProfileError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to load profile"])
}
return ("Jane Doe", "jane.doe@example.com")
}
func fetchUserPosts() async throws -> [String] {
try await Task.sleep(nanoseconds: 1_500_000_000) // 1.5 seconds delay
if Bool.random() { // Simulate random error
throw NSError(domain: "PostsError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to load posts"])
}
return ["Post 1", "Post 2", "Post 3"]
}
}This solution uses Swift's structured concurrency with async let to start loading the user profile and posts at the same time. Both tasks run concurrently, making the app faster and more efficient.
Loading states isLoadingProfile and isLoadingPosts control the UI to show loading messages separately for profile and posts.
If either task throws an error, the catch block sets an error message and shows an alert with a retry button.
The loadData() function is called when the view appears and when the user taps the Reload Data button, allowing easy retry and refresh.
Simulated delays and random errors mimic real network calls for learning purposes.