Is there a way to share a Swift state between separate files?

Viewed 308

Is there a way to share a Swift state between separate files? I've tried:

public struct VideoPlayerControlsView : View {
    @State public var playerPaused : Bool = true
    // More Code Here
}

And would like to reference this in another file (where the two are linked, so I can change one and the other would reflect this change), I think that it would be achieved by a Binding:

struct VideoView: View {
    @Binding var videoPos : Double
    
    //more code here
}

But then I'm told: 'init(videoPos)' declared here' alongside that I should change: PlaygroundPage.current.setLiveView(VideoView()) to PlaygroundPage.current.setLiveView(VideoView(videoPos: <#Binding<Double>#>)) but not sure what to set the place holder value to, I also don't think this is the right way to do what I'm trying to achieve?

How can I do this?

NOTE: I'm using Swift Playgrounds on Mac

1 Answers

One thing to keep in mind is that state (and especially @State) should always be private, you generally never have a public state. It is for the internal use of the view only, such as animation or other view-related stuff.

To solve your problem, however, are two sophisticated ways of doing what you are trying to do:

  1. Create a Model

    The model should be the only source of truth. For your video player, you might consider adding a model that represents just that: A video player. Now the example above is not really a model (but the ViewModel in the MVVM paradigm) but it serves as a good example:

    class VideoPlayer: ObservableObject {
        @Published var isCurrentlyPlaying = false
    }
    

    You wire this up to your struct via either a @ObservedObject or a @StateObject (preferred) outlet to your view. You can pass it down the view hierarchy using the EnvironmentObject methodology. Now all of this is pretty complicated stuff if you have never heard of this, so maybe this might help you.

    1. Pass state via binding

    I will start with the example:

    public struct VideoPlayerControlsView : View {
        @Binding public var playerPaused : Bool
        // More Code Here
    }
    
    struct VideoView: View {
        @State private var playerPaused: Bool
        var body: some View {
            // some more code
            VideoPlayerControlsView(playerPaused: $playerPaused)
        }
    }
    

    There are several (more or less) subtle differences:

    Note that the 'state' in the controls view is actually the binding. Also note that you don't initialize the binding. A @Binding represents a way to 'read and write' from the source of truth. And there should (again) only be one source of truth. This source of truth comes from the VideoView.

    Also note that you 'pass' the state somehow to the controls view, not the other way around. But be careful here since you don't actually pass the State but rather a 'way of reading and writing' from and to the state

Hope this clears things up a bit!

Related