Как создать окружение без defaultValue, чтобы оно выдавало ошибку при использовании без предоставления?

Вопрос или проблема

Как создать окружение без defaultValue, чтобы оно выдавало ошибку при использовании без предоставления?

Я пытаюсь сделать так, чтобы thing был обязательным параметром вместо того, чтобы предоставлять его по умолчанию с помощью defaultValue = Thing(). Если я сделаю окружение необязательным с помощью Thing?, то мне придется разбирать его в каждом представлении. Без такого подхода, мой инициализатор всегда, похоже, вызывается дважды, когда мне нужно передать конкретный экземпляр.

Я пробую с помощью кода ниже, но он выдает фатальную ошибку “Thing not provided” как только запускается. Я предоставляю окружение в thingApp, поэтому мне непонятно, почему это происходит. Это происходит даже когда @Environment(\.thing) var thing не используется.

import SwiftUI

@main
struct thingApp: App {
    var thing = Thing()

    var body: some Scene {
        WindowGroup {
            ContentView().environment(\.thing, thing)
        }
    }
}

struct ContentView: View {
    @Environment(\.thing) var thing

    var body: some View {
        Text("count: \(self.thing.count)")
    }
}

@MainActor
@Observable class Thing {
    var count: Int = 3

    func inc() {
        self.count = self.count + 1
    }
}

private struct ThingKey: EnvironmentKey {
    static var defaultValue: Thing {
        fatalError("Thing not provided")
    }
}

extension EnvironmentValues {
    var thing: Thing {
        get { self[ThingKey.self] }
        set { self[ThingKey.self] = newValue }
    }
}

Ответ или решение

Чтобы создать окружение (Environment) в SwiftUI, которое выдает ошибку при использовании без предоставленного значения, вам необходимо правильно реализовать ключ окружения и его значения. Ваш подход в целом верный, но стоит обратить внимание на некоторые нюансы.

Ваша реализация с методом fatalError в defaultValue ключа окружения (ThingKey) является правильной, однако проблема может возникнуть из-за порядка инициализации. Убедитесь, что ваш экземпляр Thing существует до того, как вы получите доступ к нему в представлении. Также, если вы получаете данное значение в инициализаторе ContentView, это может вызвать лишний вызов инициализации.

Вот обновленный код, который позволяет вам достичь желаемого поведения:

import SwiftUI

@main
struct ThingApp: App {
    var thing = Thing() // Создаем экземпляр Thing

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.thing, thing) // Передаем thing в окружение
        }
    }
}

struct ContentView: View {
    @Environment(\.thing) var thing // Используем окружение

    var body: some View {
        Text("count: \(self.thing.count)")
    }
}

@MainActor
@Observable class Thing {
    var count: Int = 3

    func inc() {
        self.count += 1
    }
}

private struct ThingKey: EnvironmentKey {
    static var defaultValue: Thing {
        fatalError("Thing not provided") // Генерируем ошибку, если объект не предоставлен
    }
}

extension EnvironmentValues {
    var thing: Thing {
        get { self[ThingKey.self] }
        set { self[ThingKey.self] = newValue }
    }
}

Объяснение:

  1. Структура ThingKey: В методе defaultValue происходит вызов fatalError, если значение не предоставлено. Это поведение, которое соответствует вашим требованиям.

  2. Контекст использования: Убедитесь, что ContentView используется только в том месте, где окружение корректно установлено с помощью .environment(\.thing, thing). Если вы попытаетесь доступаться к thing в ContentView вне этого контекста, это приведет к вызову fatalError.

  3. Отладка: Если у вас продолжает возникать проблема с двойным вызовом инициатора, внимательно проверьте, не вызывается ли ContentView в нескольких местах в иерархии представлений.

  4. Вывод ошибки: Теперь, если вы попытаетесь получить thing в любом представлении без его предварительного предоставления, вы получите четкую ошибку с сообщением "Thing not provided".

Эти шаги должны помочь вам корректно реализовать окружение в SwiftUI с необходимыми проверками. Если у вас возникнут дополнительные вопросы или сложности, не стесняйтесь спрашивать!

Оцените материал
Добавить комментарий

Капча загружается...