EnvironmentKey.defaultValueを変更する
表題の通りなのですが「あ、それいけるんだ」と発見があったので書きます。
Environment
SwiftUIを使っている方には馴染み深いEnvironmentという仕組みがあります。これは親のViewが子Viewに値を共有するための仕組みです。
Environment: https://developer.apple.com/documentation/swiftui/environment
通常、defaultValueを設定して、変更が必要であれば environment(::)を使用します。以下のコードは Hello
から こんにちは
に変更しているコードです。ComponentA,ComponentBで @Environment(\.hello) var hello
を使う時値は こんにちは
になります
struct HelloEnvironmentKey: EnvironmentKey {
static var defaultValue: String = "Hello"
}
extension EnvironmentValues {
var hello: String {
get { self[HelloEnvironmentKey.self] }
set { self[HelloEnvironmentKey.self] = newValue }
}
}
VStack {
ComponentA()
ComponentB()
}
.environment(\.hello, "こんにちは")
そんな生活を送っていたのですが、defaultValueを変更もできるのでは。と思い試してみました。
試してみた
下のコードで試しました
import SwiftUI
struct ContentView: View {
@State var isPresented = false
var body: some View {
let _ = Self._printChanges()
VStack {
Button {
HelloEnvironmentKey.defaultValue = "こんにちは"
} label: {
Text("Change")
}
Button {
isPresented = true
} label: {
Text("Present sheet")
}
.sheet(isPresented: $isPresented, content: {
ModalView()
})
}
.padding()
}
}
#Preview {
ContentView()
}
struct ModalView: View {
@Environment(\.hello) var hello
var body: some View {
Text(hello)
}
}
struct HelloEnvironmentKey: EnvironmentKey {
static var defaultValue: String = "Hello"
}
extension EnvironmentValues {
var hello: String {
get { self[HelloEnvironmentKey.self] }
set { self[HelloEnvironmentKey.self] = newValue }
}
}
このコードで Change
を押さずにモーダルを表示すると Hello
と表示され、Change
を押した後にモーダルを出すと こんにちは
と表示されました。念のため Self._printChanges
を挟みましたが Change
を押した時にViewの再評価は実行されないです。
使い所
無いです(`・ω・´)キリ。ただ、iOS14時代のSwiftUIではモーダル表示でEnvironmentが引き継がれない問題に遭遇した & そのことについて言及している記事も見かけました。その不具合と戦う環境の人は defaultValueを設定するのも一つの手段になるかもしれないです。
かくいう私もたまにEnvironment引き継がれないよなー。と思い昔は過ごしていました。最近その問題に遭遇してないのでもしかしたら新しいOSでは発生しないかも知れないですね。公式の発表では無いのですが見つけた記事を貼るのでご参考までに
参考:
- https://www.kaioelfke.de/blog/swiftui-environment-updates-propagation-2023/
- https://oleb.net/2020/sheet-environment/
とはいえ、アプリ起動後で使用したいEnvironmentのdefaultValueを設定したいシチュはあると思います。その場合は専用のViewを用意するなりして親の処理が解決したら子供のViewを評価する方法をとると良さそうです。下のような書き方ですね。もし、defaultValueを設定し直すときはとても慎重に判断しましょう。SSoTが崩れる可能性があります。
https://x.com/bannzai/status/1801413794763313387
まとめ
ちょっとした小ネタでした。この記事でわかることは EnvironmentValuesのgetの処理のフォールバックの先は常に EnvironmentKey.defaultValue を見ていることがわかった点だと思います。これで少しSwiftUIの内側に詳しくなれたと思えばこの記事は超優良記事ですね。実務で全く役に立たないと思ってたけどこんなに良い記事になるとは書いてて思いませんでした。いやー、よかった。良い子のみんなは真似しないでね
おしまい \(^o^)/
Discussion