Код-ревью Middle SwiftUI обновлено

Код-ревью: экран профиля на SwiftUI

Вопрос

Найдите проблемы в экране профиля.

struct ProfileScreen: View {
    @ObservedObject var vm = ProfileViewModel()
    @State var formatter = DateFormatter()

    var body: some View {
        let posts = vm.posts.sorted { $0.date > $1.date }
        List {
            ForEach(posts.indices, id: \.self) { i in
                PostRow(post: posts[i])
            }
        }
        .onAppear {
            Task { await vm.load() }
        }
    }
}

Коротко

Исправленный вариант: @State private var vm = ProfileViewModel() с @Observable @MainActor final class, модель отдаёт уже отсортированные posts, ForEach(vm.posts) { PostRow(post: $0) } с Identifiable, загрузка в .task, форматтер статический.

Что должен найти ревьюер

  1. Ошибкастрока 2
    @ObservedObject с инициализацией на месте: при пересоздании структуры родителем view model создаётся заново и данные теряются. Нужен @StateObject (или @State с @Observable).
  2. Стильстрока 3
    @State без private и для объекта, который не является состоянием экрана: форматтер лучше статическим или в модели.
  3. Производительностьстрока 6
    Сортировка в body выполняется при каждом пересчёте. Сортированный список должна готовить модель.
  4. Ошибкастрока 8
    Индексы как id: при удалении или вставке строки анимации и состояние PostRow уедут на чужие посты.
  5. Ошибкастрока 12
    onAppear с Task не отменяет загрузку при уходе и запускает её при каждом появлении. Нужен .task { await vm.load() }.

Разбор

Исправленный вариант: @State private var vm = ProfileViewModel() с @Observable @MainActor final class, модель отдаёт уже отсортированные posts, ForEach(vm.posts) { PostRow(post: $0) } с Identifiable, загрузка в .task, форматтер статический.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.