SwiftUI: Sharing AppStorage across the whole app

Published by malhal on

Sharing @AppStorage without having to pass it into every feature of the app is simply done in the same way it’s done for anything else you want to share: EnvironmentKey. It lets you share a source of truth into a View hierarchy without needing to pass it into every View. Use it with @AppStorage as follows:

import SwiftUI

extension EnvironmentValues {
    // if you only need read access
    var storageTestMyCustomValue: Bool {
        get { self[StorageTest.MyEnvironmentKey.self] }
        set { self[StorageTest.MyEnvironmentKey.self] = newValue }
    }
    
    // if you need read/write
    var storageTestMyCustomValueBinding: Binding<Bool> {
        get { self[StorageTest.MyEnvironmentKeyBinding.self] }
        set { self[StorageTest.MyEnvironmentKeyBinding.self] = newValue }
    }
}

struct StorageTest {
    
    struct MyEnvironmentKey: EnvironmentKey {
        static let defaultValue = false
    }
    
    struct MyEnvironmentKeyBinding: EnvironmentKey {
        static let defaultValue: Binding<Bool> = Binding<Bool>.constant(false)
    }
    
    struct ContentView: View {
        
        @AppStorage("StorageTest.Toggle") var isOn = false

        var body: some View {
            NavigationStack {
                List {
                    AppStorageNotNeededView()
                }
            }
            .environment(\.storageTestMyCustomValueBinding, $isOn)
        }
    }
    
    struct AppStorageNotNeededView: View {
        var body: some View {
            AppStorageNeededView()
        }
    }
    
    struct AppStorageNeededView: View {
        @Environment(\.storageTestMyCustomValueBinding) var isOn: Binding<Bool>
        
        var body: some View {
            Toggle("Toggle", isOn: isOn.animation())
            if isOn.wrappedValue == true {
                Text("Row")
            }
        }
    }
}
Categories: SwiftUI