skip to Main Content

I am trying to code an app using samples from Apple, but I realised that Scrumdinger app preview doesn’t work in Xcode preview. I started a simulator and there it works. So I understand that this is because of @StateObject. How do you code without preview or how do you make it work?

Here’s the sample code:

@main
struct LandLordApp: App {
    @StateObject var store = EstateStore()
    
    var body: some Scene {
        WindowGroup {
            EstatesList(estates: $store.estates)
        }
    }
}

import Foundation

struct Estate: Identifiable, Codable, Hashable {
    var id = UUID()
    var name: String = ""
    var address: String = ""
    var isRented: Bool = false
}

class EstateStore: ObservableObject {
    @Published var estates: [Estate] = []
}

import SwiftUI

struct EstatesList: View {
    
    @Binding var estates: [Estate]
    
    var body: some View {
        NavigationStack {
            List(estates) {estate in
                NavigationLink {
                    Label(estate.name, systemImage: "building.2")
                } label: {
                    Label(estate.name, systemImage: "building.2")
                }
            }
            .toolbar {
                ToolbarItem {
                    Button {
                        estates.append(Estate(name: "Krautzplaz, 10"))
                    } label: {
                        Label("Add", systemImage: "plus")
                    }
                }
            }
        }
    }
}

struct EstatesList_Previews: PreviewProvider {
    
    static var previews: some View {
        EstatesList(estates: .constant(EstateStore().estates))
    }
}

If you run in a simulator it works. In the Preview it just doesn’t work.

2

Answers


  1. You can replace @StateObject with @ObservedObject, which has similar behavior and is supported in Xcode previews.

    Login or Signup to reply.
  2. Usually we use .environmentObject for the data model store object as follows:

    @main
    struct LandLordApp: App {
    
    // Because this is @StateObject it won't be init during previewing. 
    // @StateObject is init before body is called and App's body isn't called when previewing.
        @StateObject var store = EstateStore() 
        
        var body: some Scene {
            WindowGroup {
                EstatesList()
                     .environmentObject(store) // we could also use EstateStore.shared
            }
        }
    }
    
    struct EstatesList: View {
        
        @EnvironmentObject var store: EstateStore
        
        var body: some View {
            NavigationStack {
                List($store.estates) { $estate in
    
    struct EstatesList_Previews: PreviewProvider {
        
        static var previews: some View {
            EstatesList()
                .environmentObject(EstateStore(previewing: true)) // or EstateStore.preview
        }
    }
    
    class EstateStore: ObservableObject {
        @Published var estates: [Estate] = []
    
        init(previewing: Bool = false) {
            if previewing {
                estates = [Estate( /* sample data */  )]
            }
        }
    
        // or singletons if you prefer not to use @StateObject in the App because say you don't want the scene to be recomputed all the time when not using any estate properties.
        //static var shared = EstateStore()
        //static var preview = EstateStore(previewing: true)
    }
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search