Reputation: 1
I know that State wrappers are for View and they designed for this goal, but I wanted to try build and test some code if it is possible, my goal is just for learning purpose,
I have 2 big issues with my code!
Xcode is unable to find T.
How can I initialize my state?
import SwiftUI
var state: State<T> where T: StringProtocol = State(get: { state }, set: { newValue in state = newValue })
struct ContentView: View {
var body: some View {
Text(state)
}
}
Update: I could do samething for Binding here, Now I want do it for State as well with up code
import SwiftUI
var state2: String = String() { didSet { print(state2) } }
var binding: Binding = Binding.init(get: { state2 }, set: { newValue in state2 = newValue })
struct ContentView: View {
var body: some View {
TextField("Enter your text", text: binding)
}
}
If I could find the answer of my issue then, i can define my State and Binding both outside of View, 50% of this work done and it need another 50% for State Wrapper.
New Update:
import SwiftUI
var state: State<String> = State.init(initialValue: "Hello") { didSet { print(state.wrappedValue) } }
var binding: Binding = Binding.init(get: { state.wrappedValue }, set: { newValue in state = State(wrappedValue: newValue) })
struct ContentView: View {
var body: some View {
Text(state) // <<: Here is the issue!
TextField("Enter your text", text: binding)
}
}
Upvotes: 4
Views: 1913
Reputation: 54641
Even if you create a State wrapper outside a view, how will the view know when to refresh its body?
Without a way to notify the view, your code will do the same as:
struct ContentView: View {
var body: some View {
Text("Hello")
}
}
What you can do next depends on what you want to achieve.
If all you need is a way to replicate the State behaviour outside the view, I recommend you take a closer look at the Combine framework.
An interesting example is CurrentValueSubject
:
var state = CurrentValueSubject<String, Never>("state1")
It stores the current value and also acts as a Publisher.
What will happen if we use it in a view that doesn't observe anything?
struct ContentView: View {
var body: some View {
Text(state.value)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
state.value = "state2"
}
}
}
}
The answer is: nothing. The view is drawn once and, even if the state
changes, the view won't be re-drawn.
You need a way to notify the view about the changes. In theory you could do something like:
var state = CurrentValueSubject<String, Never>("state1")
struct ContentView: View {
@State var internalState = ""
var body: some View {
Text(internalState)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
state.value = "state2"
}
}
.onReceive(state) {
internalState = $0
}
}
}
But this is neither elegant nor clean. In these cases we should probably use @State
:
struct ContentView: View {
@State var state = "state1"
var body: some View {
Text(state)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
state = "state2"
}
}
}
}
To sum up, if you need a view to be refreshed, just use the native SwiftUI property wrappers (like @State
). And if you need to declare state values outside the view, use ObservableObject
+ @Published
.
Otherwise there is a huge Combine framework which does exactly what you want. I recommend you take a look at these links:
Upvotes: 4