Junior — Senior
Optimization of the user list loading screen
livecode
Task condition
The module is implemented as a screen with a slider to select the number of requested users, a button to initiate the download, and several views to display the result. The full list is displayed entirely, without pagination.
It is required to study the current implementation and fix all identified issues.
struct ContentView: View {
@ObservedObject private var vm = ViewModel()
@State private var userCount = 3.0
var body: some View {
ZStack(alignment: .top) {
Background(count: $userCount)
.frame(width: UIScreen.main.bounds.width, height: 115)
VStack {
Slider(
value: $userCount,
in: 3...100,
step: 1
) { _ in }
.tint(.purple)
Button("Request \(Int(userCount)) users") {
Task {
await vm.downloadNames(count: Int(userCount))
}
}
.padding()
.foregroundStyle(.white)
.background(.purple)
.clipShape(.buttonBorder)
ProxyView(viewModel: vm)
}
}
}
}
struct Background: View {
@Binding var count: Double
var backgroundColor: Color {
switch count {
case ..<10: return .green
case 10..<50: return .yellow
default: return .red
}
}
var body: some View {
Rectangle()
.fill(backgroundColor)
}
}
struct ProxyView: View {
@ObservedObject var viewModel: ViewModel
var body: some View {
SuccessView(viewModel: viewModel)
}
}
struct SuccessView: View {
@ObservedObject var viewModel: ViewModel
@State private var loading: Bool = false
var body: some View {
VStack {
Text("Total users fetched: \(viewModel.names.count)")
if loading { ProgressView() }
List(viewModel.names, id: \ .self) { name in
Text(name)
}
}
.onReceive(viewModel.publisher) { value in
loading = value
}
}
}
class ViewModel: ObservableObject {
let networkService = NetworkService()
let value = CurrentValueSubject<Bool, Never>(false)
@Published var names: [String] = []
var publisher: AnyPublisher<Bool, Never> {
value.eraseToAnyPublisher()
}
func downloadNames(count: Int) async {
value.send(true)
async let ids = networkService.fetchUserIds(count: count)
for id in await ids {
networkService.fetchName(id: id) { name in
self.names.append(name)
}
}
value.send(false)
}
}
class NetworkService {
func fetchName(id: Int, completion: @escaping (String) -> Void) {
let names = ["Alexander", "Maxim", "Ivan", "Artyom", "Dmitry", "Nikito", "Mikhail", "Daniil", "Egor"]
let surnames = ["Smirnov", "Ivanov", "Kuznetsov", "Sokolov", "Popov", "Lebedev", "Kozlov", "Novikov", "Morozov"]
DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + 1) {
completion(names.randomElement()! + " " + surnames.randomElement()!)
}
}
func fetchUserIds(count: Int) async -> [Int] {
try? await Task.sleep(for: .seconds(1))
return Array(1...count)
}
}
# Preview {
ContentView()
}