Как обновить SwiftUI-представление, если свойство является ссылкой на Router, а не @Published?

«Как обновить SwiftUI-представление, если свойство является ссылкой на Router, а не @Published?» — вопрос из категории SwiftUI, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

SwiftUI не будет автоматически обновлять представление, если свойство — это обычная ссылка на объект Router, а не свойство, обернутое в @Published. Для принудительного обновления необходимо вручную уведомлять систему об изменениях.

Основные подходы:

  1. Сделать Router наблюдаемым объектом (ObservableObject) и использовать @ObservedObject в представлении. Изменения состояния нужно отмечать через objectWillChange.send().

    class Router: ObservableObject {
        var currentRoute: Route = .home {
            didSet { objectWillChange.send() }
        }
    }
    
    struct ContentView: View {
        @ObservedObject var router: Router
        var body: some View {
            Text(router.currentRoute.title)
        }
    }
  2. Явный вызов обновления. Если объект уже является ObservableObject, можно вручную отправить уведомление при необходимости.

    router.objectWillChange.send()
  3. Использовать идентификатор для принудительного обновления (id). Это заставляет SwiftUI пересоздать представление.

    struct ContentView: View {
        let router: Router
        @State private var refreshID = UUID()
    
        var body: some View {
            VStack {
                // Контент, зависящий от router
            }
            .id(refreshID) // Представление пересоздается при изменении refreshID
        }
    
        func updateView() {
            refreshID = UUID() // Принудительное обновление
        }
    }

Ключевой момент: Без механизма публикации (@Published, ObservableObject или ручного вызова objectWillChange) SwiftUI не отслеживает изменения в обычных ссылочных типах.