UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Reset AppStorage values for previews

Forums > SwiftUI

If I use an @AppStorage variable in my view and change its value, the value remains changed when I stop and then start the preview again. Is there a way, similar the preview Core Data stack, to have the @AppStorage/UserDefault values reset?

4      

I had the same issue and I don't know if this is a "limitation" or actually SwiftUI "telling" us to use view composition.

Personally, to test the design and layout of views that use @AppStorage variables, I just extract child views that take the original variable(s) marked with @AppStorage as plain, stateless variable(s) and generate as many previews as needed for those children. That's usually enough for me. However, if you want to easily preview the "assembled" layout, you could, for example, extract a new parent view that is identical to the one you already had with @AppStorage but using @Binding instead. To add persistence, just wrap this new parent in a "container" view that adds persistence with @AppStorage:

enum AnimalSize: Int {
    case small, large

    func toggle() -> AnimalSize {
        self == .small ? .large : .small
    }
}

// A child that can be easily previewed
struct CatView: View {
    var size: AnimalSize

    var body: some View {
        Text("🐈")
            .font(.system(size: size == .small ? 25 : 75))
    }
}

// Another child
struct DogView: View {
    var size: AnimalSize

    var body: some View {
        Text("🐕")
            .font(.system(size: size == .small ? 25 : 75))
    }
}

// The new parent view we can preview for all state combinations
struct AnimalView: View {
    @Binding var catSize: AnimalSize
    @Binding var dogSize: AnimalSize

    var body: some View {
        VStack(spacing: 25) {
            CatView(size: catSize)
                .onTapGesture { catSize = catSize.toggle() }
            DogView(size: dogSize)
                .onTapGesture { dogSize = dogSize.toggle() }
        }
    }
}

// The wrapper that adds persistence and you don't really need to preview
struct AnimalViewPersistent: View {
    @AppStorage("cat.size") private var catSize: AnimalSize = .small
    @AppStorage("dog.size") private var dogSize: AnimalSize = .small

    var body: some View {
        AnimalView(catSize: $catSize, dogSize: $dogSize)
    }
}

// All parameter combinations we might want to preview
struct AnimalView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            AnimalView(catSize: .constant(.small), dogSize: .constant(.small))
            AnimalView(catSize: .constant(.large), dogSize: .constant(.small))
            AnimalView(catSize: .constant(.small), dogSize: .constant(.large))
            AnimalView(catSize: .constant(.large), dogSize: .constant(.large))
        }
        .previewLayout(.sizeThatFits)
    }
}

As a nice bonus your views now become smaller, more testable and reusable.

P.S. N1: Alternatively, I guess you could pass the Binding down all the way to the children and place the logic that changes state from the parent right into the applicable child. As a matter of personal preference, I like to have the children as dumb as possible and keep the state changes identical to what I had in the original parent view using @AppStorage.

P.S. N2: You might still want to live preview the container view that adds persistence to check that the state changing logic is wroking as expected (e.g. animations). However, you already get all possible state combinations in the static previews that use constant bindings without having to manually change state in a live preview because @AppStorage just persisted the state from the last live preview. This way, if you change one child, you can immediately see those changes reflected on the final layout of the parent that uses @Binding.

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.