Вопрос или проблема
Как создать окружение без 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 }
}
}
Объяснение:
-
Структура ThingKey: В методе
defaultValue
происходит вызовfatalError
, если значение не предоставлено. Это поведение, которое соответствует вашим требованиям. -
Контекст использования: Убедитесь, что
ContentView
используется только в том месте, где окружение корректно установлено с помощью.environment(\.thing, thing)
. Если вы попытаетесь доступаться кthing
вContentView
вне этого контекста, это приведет к вызовуfatalError
. -
Отладка: Если у вас продолжает возникать проблема с двойным вызовом инициатора, внимательно проверьте, не вызывается ли
ContentView
в нескольких местах в иерархии представлений. - Вывод ошибки: Теперь, если вы попытаетесь получить
thing
в любом представлении без его предварительного предоставления, вы получите четкую ошибку с сообщением "Thing not provided".
Эти шаги должны помочь вам корректно реализовать окружение в SwiftUI с необходимыми проверками. Если у вас возникнут дополнительные вопросы или сложности, не стесняйтесь спрашивать!