SwiftUI: Pass an ObservableObject's property into another class's initializer - swift

How do I pass a property from an ObservedObject in a View, to another class's initializer in the same View? I get an error with my ObservedObject:
Cannot use instance member 'project' within property initializer; property initializers run before 'self' is available
The reason I want to do this is I have a class which has properties that depend on a value from the ObservedObject.
For example, I have an ObservedObject called project. I want to use the property, project.totalWordsWritten, to change the session class's property, session.totalWordCountWithSession:
struct SessionView: View {
#Binding var isPresented: Bool
#ObservedObject var project: Project
// How to pass in project.totalWordsWritten from ObservedObject project to totalWordCount?
#StateObject var session:Session = Session(startDate: Date(), sessionWordCount: 300, totalWordCount: 4000)
var body: some View {
NavigationView {
VStack(alignment: .leading) {
Form {
Section {
Text("Count")
HStack {
Text("Session word count")
TextField("", value: $session.sessionWordCount, formatter: NumberFormatter())
.textFieldStyle(.roundedBorder)
}
HStack {
// Changing text field here should change the session count above
Text("Total word count")
TextField("", value: $session.totalWordCountWithSession, formatter: NumberFormatter())
.textFieldStyle(.roundedBorder)
}
}
}
}.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
// Save this session into the project
project.addSession(newSession: session)
isPresented = false
}
}
}
}
}
}
struct SessionView_Previews: PreviewProvider {
static var previews: some View {
SessionView(isPresented: .constant(true), project: Project(title: "TestProject", startWordCount: 0))
}
}
Below is the rest of the example:
HomeView.swift
import SwiftUI
struct HomeView: View {
#State private var showingSessionPopover:Bool = false
#StateObject var projectItem:Project = Project(title: "Test Project", startWordCount: 4000)
var body: some View {
NavigationView {
VStack(alignment: .leading) {
Text(projectItem.title).font(Font.custom("OpenSans-Regular", size: 18))
.fontWeight(.bold)
Text("Count today: \(projectItem.wordsWrittenToday)")
Text("Total: \(projectItem.totalWordsWritten)")
}
.toolbar {
ToolbarItem {
Button(action: {
showingSessionPopover = true
}, label: {
Image(systemName: "calendar").imageScale(.large)
}
)
}
}
}.popover(isPresented: $showingSessionPopover) {
SessionView(isPresented: $showingSessionPopover, project: projectItem)
}
}
}
Session.swift:
import Foundation
import SwiftUI
class Session: Identifiable, ObservableObject {
init(startDate:Date, sessionWordCount:Int, totalWordCount: Int) {
self.startDate = startDate
self.endDate = Calendar.current.date(byAdding: .minute, value: 30, to: startDate) ?? Date()
self.sessionWordCount = sessionWordCount
self.totalWordCount = totalWordCount
self.totalWordCountWithSession = self.totalWordCount + sessionWordCount
}
var id: UUID = UUID()
#Published var startDate:Date
#Published var endDate:Date
var totalWordCount: Int
var sessionWordCount:Int
#Published var totalWordCountWithSession:Int {
didSet {
sessionWordCount = totalWordCountWithSession - totalWordCount
}
}
}
Project.swift
import SwiftUI
class Project: Identifiable, ObservableObject {
var id: UUID = UUID()
#Published var title:String
var sessions:[Session] = []
#Published var wordsWrittenToday:Int = 0
#Published var totalWordsWritten:Int = 0
#Published var startWordCount:Int
init(title:String,startWordCount:Int) {
self.title = title
self.startWordCount = startWordCount
self.calculateDailyAndTotalWritten()
}
// Create a new session
func addSession(newSession:Session) {
sessions.append(newSession)
calculateDailyAndTotalWritten()
}
// Re-calculate how many
// today and in total for the project
func calculateDailyAndTotalWritten() {
wordsWrittenToday = 0
totalWordsWritten = startWordCount
for session in sessions {
if (Calendar.current.isDateInToday(session.startDate)) {
wordsWrittenToday += session.sessionWordCount
}
totalWordsWritten += session.sessionWordCount
}
}
}

You can use the StateObject initializer in init:
struct SessionView: View {
#Binding var isPresented: Bool
#ObservedObject var project: Project
#StateObject var session:Session = Session(startDate: Date(), sessionWordCount: 300, totalWordCount: 4000)
init(isPresented: Binding<Bool>, project: Project, session: Session) {
_isPresented = isPresented
_session = StateObject(wrappedValue: Session(startDate: Date(), sessionWordCount: 300, totalWordCount: project.totalWordsWritten))
self.project = project
}
var body: some View {
Text("Hello, world")
}
}
Note that the documentation says:
You don’t call this initializer directly
But, it has been confirmed by SwiftUI engineers in WWDC labs that this is a legitimate technique. What runs in wrappedValue is an autoclosure and only runs on the first init of StateObject, so you don't have to be concerned that every time your View updates that it will run.
In general, though, it's a good idea to try to avoid doing things in the View's init. You could consider instead, for example, using something like task or onAppear to set the value and just put a placeholder value in at first.

Related

Passing a state variable to parent view

I have the following code:
struct BookView: View {
#State var title = ""
#State var author = ""
var body: some View {
TextField("Title", text: $title)
TextField("Author", text: $author)
}
}
struct MainView: View {
#State private var presentNewBook: Bool = false
var body: some View {
NavigationView {
// ... some button that toggles presentNewBook
}.sheet(isPresented: $presentNewBook) {
let view = BookView()
view.toolbar {
ToolbarItem(placement: principal) {
TextField("Title", text: view.$title)
}
}
}
}
}
This compiles but is giving me the following error on runtime:
Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update.
How do I pass a state variable to some other outside view? I can't use ObservableObject on BookView since that would require me to change it from struct to class
In general, your state should always be owned higher up the view hierarchy. Trying to access the child state from a parent is an anti-pattern.
One option is to use #Bindings to pass the values down to child views:
struct BookView: View {
#Binding var title : String
#Binding var author : String
var body: some View {
TextField("Title", text: $title)
TextField("Author", text: $author)
}
}
struct ContentView: View {
#State private var presentNewBook: Bool = false
#State private var title = ""
#State private var author = ""
var body: some View {
NavigationView {
VStack {
Text("Title: \(title)")
Text("Author: \(author)")
Button("Open") {
presentNewBook = true
}
}
}.sheet(isPresented: $presentNewBook) {
BookView(title: $title, author: $author)
}
}
}
Another possibility is using an ObservableObject:
class BookState : ObservableObject {
#Published var title = ""
#Published var author = ""
}
struct BookView: View {
#ObservedObject var bookState : BookState
var body: some View {
TextField("Title", text: $bookState.title)
TextField("Author", text: $bookState.author)
}
}
struct ContentView: View {
#State private var presentNewBook: Bool = false
#StateObject private var bookState = BookState()
var body: some View {
NavigationView {
VStack {
Text("Title: \(bookState.title)")
Text("Author: \(bookState.author)")
Button("Open") {
presentNewBook = true
}
}
}.sheet(isPresented: $presentNewBook) {
BookView(bookState: bookState)
}
}
}
I've altered your example views a bit because to me the structure was unclear, but the concept of owning the state at the parent level is the important element.
You can also pass a state variable among views as such:
let view = BookView(title: "foobar")
view.toolbar {
ToolbarItem(placement: principal) {
TextField("Title", text: view.$title)
}
}
Then, inside of BookView:
#State var title: String
init(title: String) {
_title = State(initialValue: title)
}
Source: How could I initialize the #State variable in the init function in SwiftUI?

#EnvironmentObject property not working properly in swiftUI

Updating cartArray from ViewModel doesn't append to the current elements, but adds object everytime freshly. I need to maintain cartArray as global array so that it can be accessed from any view of the project. I'm adding elements to cartArray from ViewModel. I took a separate class DataStorage which has objects that can be accessible through out the project
Example_AppApp.swift
import SwiftUI
#main
struct Example_AppApp: App {
var body: some Scene {
WindowGroup {
ContentView().environmentObject(DataStorage())
}
}
}
DataStorage.swift
import Foundation
class DataStorage: ObservableObject {
#Published var cartArray = [Book]()
}
ContentView.swift
import SwiftUI
struct ContentView: View {
#State var showSheetView = false
var body: some View {
NavigationView{
ListViewDisplay()
.navigationBarItems(trailing:
Button(action: {
self.showSheetView.toggle()
}) {
Image(systemName: "cart.circle.fill")
.font(Font.system(.title))
}
)
}.sheet(isPresented: $showSheetView) {
View3()
}
}
}
struct ListViewDisplay: View{
var book = [
Book(bookId: 1 ,bookName: "Catch-22"),
Book(bookId: 2 ,bookName: "Just-Shocking" ),
Book(bookId: 3 ,bookName: "Stephen King" ),
Book(bookId: 4,bookName: "A Gentleman in Moscow"),
]
var body: some View {
List(book, id: \.id) { book in
Text(book.bookName)
NavigationLink(destination: View1(book: book)) {
}
}
}
}
View1Modal.swift
import Foundation
struct Book: Codable, Identifiable {
var id:String{bookName}
var bookId : Int
var bookName: String
}
struct BookOption: Codable{
var name: String
var price: Int
}
View1ViewModel.swift
import Foundation
import Combine
class View1ViewModel : ObservableObject{
var dataStorage = DataStorage()
func addBook (bookId:Int ,bookName : String){
dataStorage.cartArray.append(Book(bookId:bookId, bookName: bookName)) // Adding to global array
print(dataStorage.cartArray)
}
}
View1.swift
import SwiftUI
struct View1: View {
#ObservedObject var vwModel = View1ViewModel()
#EnvironmentObject var datastrg: DataStorage
var book:Book
var body: some View {
Text(book.bookName).font(.title)
Spacer()
Button(action: {
vwModel.addBook(bookId: book.bookId, bookName: book.bookName)
}, label: {
Text("Add Book to Cart")
.frame(maxWidth: .infinity, minHeight: 60)
.background(Color.red)
.foregroundColor(Color.white)
.font(.custom("OpenSans-Bold", size: 24))
})
}
}
View3.swift
import SwiftUI
struct View3: View {
#EnvironmentObject var datastorage : DataStorage
var body: some View {
NavigationView {
List(datastorage.cartArray,id:\.id){book in
VStack{
Text(book.bookName)
.font(.custom("OpenSans-Bold", size: 20))
}
}
.navigationBarTitle(Text("Cart"), displayMode: .inline)
}
}
}
When addBook func is called for the first time it prints as
[Example_App.Book(bookId: 1, bookName: "Catch-22")]
When I go back and come back to this View1 and add another book by calling addBook func it adds as new object to cartArray
[Example_App.Book(bookId: 3, bookName: "Stephen King")]
Printing number of elements in cartArray gives as 1 element instead of 2 elements. When I go to View3 and display the Books in list, cartArray shows as empty(0 elements)
I think there is something wrong with var dataStorage = DataStorage() in ViewModel class. Everytime this is being created freshly, so the prevoius values are not stored. But I couldn't understand how to preserve its state
How to display List in View3 ? Any ideas/ suggestions will be helpful
You need to have one instance of DataStorage that gets passed around. Any time you write DataStorage() that creates a new instance.
.environmentObject will let you inject that one instance into the view hierarchy. Then, you can use the #EnvironmentObject property wrapper to access it within a View.
Inside View1, I used onAppear to set the dataStorage property on View1ViewModel -- that means that it has to be an optional on View1ViewModel since it will not be set in init. The reason I'm avoiding setting it in init is because an #EnvironmentObject is not set as of the init of the View -- it gets injected at render time.
#main
struct Example_AppApp: App {
var dataStorage = DataStorage()
var body: some Scene {
WindowGroup {
ContentView().environmentObject(dataStorage)
}
}
}
class DataStorage: ObservableObject {
#Published var cartArray = [Book]()
}
struct ContentView: View {
#State var showSheetView = false
var body: some View {
NavigationView{
ListViewDisplay()
.navigationBarItems(trailing:
Button(action: {
self.showSheetView.toggle()
}) {
Image(systemName: "cart.circle.fill")
.font(Font.system(.title))
}
)
}.sheet(isPresented: $showSheetView) {
View3()
}
}
}
struct ListViewDisplay: View {
var book = [
Book(bookId: 1 ,bookName: "Catch-22"),
Book(bookId: 2 ,bookName: "Just-Shocking" ),
Book(bookId: 3 ,bookName: "Stephen King" ),
Book(bookId: 4,bookName: "A Gentleman in Moscow"),
]
var body: some View {
List(book, id: \.id) { book in
Text(book.bookName)
NavigationLink(destination: View1(book: book)) {
}
}
}
}
struct Book: Codable, Identifiable {
var id:String{bookName}
var bookId : Int
var bookName: String
}
struct BookOption: Codable{
var name: String
var price: Int
}
class View1ViewModel : ObservableObject{
var dataStorage : DataStorage?
func addBook (bookId:Int ,bookName : String) {
guard let dataStorage = dataStorage else {
fatalError("DataStorage not set")
}
dataStorage.cartArray.append(Book(bookId:bookId, bookName: bookName)) // Adding to global array
print(dataStorage.cartArray)
}
}
struct View1: View {
#ObservedObject var vwModel = View1ViewModel()
#EnvironmentObject var datastrg: DataStorage
var book:Book
var body: some View {
Text(book.bookName).font(.title)
Spacer()
Button(action: {
vwModel.addBook(bookId: book.bookId, bookName: book.bookName)
}, label: {
Text("Add Book to Cart")
.frame(maxWidth: .infinity, minHeight: 60)
.background(Color.red)
.foregroundColor(Color.white)
.font(.custom("OpenSans-Bold", size: 24))
})
.onAppear {
vwModel.dataStorage = datastrg
}
}
}
struct View3: View {
#EnvironmentObject var datastorage : DataStorage
var body: some View {
NavigationView {
List(datastorage.cartArray,id:\.id){book in
VStack{
Text(book.bookName)
.font(.custom("OpenSans-Bold", size: 20))
}
}
.navigationBarTitle(Text("Cart"), displayMode: .inline)
}
}
}
You are not calling your function addBook anywhere, add an onappear to your view3 calling the function and your list will populate with data.

How to set up Binding of computed value inside class (SwiftUI)

In my Model I have an array of Items, and a computed property proxy which using set{} and get{} to set and return currently selected item inside array and works as shortcut. Setting item's value manually as model.proxy?.value = 10 works, but can't figure out how to Bind this value to a component using $.
import SwiftUI
struct Item {
var value: Double
}
class Model: ObservableObject {
#Published var items: [Item] = [Item(value: 1), Item(value: 2), Item(value: 3)]
var proxy: Item? {
get {
return items[1]
}
set {
items[1] = newValue!
}
}
}
struct ContentView: View {
#StateObject var model = Model()
var body: some View {
VStack {
Text("Value: \(model.proxy!.value)")
Button(action: {model.proxy?.value = 123}, label: {Text("123")}) // method 1: this works fine
SubView(value: $model.proxy.value) // method 2: binding won't work
}.padding()
}
}
struct SubView <B:BinaryFloatingPoint> : View {
#Binding var value: B
var body: some View {
Button( action: {value = 100}, label: {Text("1")})
}
}
Is there a way to modify proxy so it would be modifiable and bindable so both methods would be available?
Thanks!
Day 2: Binding
Thanks to George, I have managed to set up Binding, but the desired binding with SubView still won't work. Here is the code:
import SwiftUI
struct Item {
var value: Double
}
class Model: ObservableObject {
#Published var items: [Item] = [Item(value: 0), Item(value: 0), Item(value: 0)]
var proxy: Binding <Item?> {
Binding <Item?> (
get: { self.items[1] },
set: { self.items[1] = $0! }
)
}
}
struct ContentView: View {
#StateObject var model = Model()
#State var myval: Double = 10
var body: some View {
VStack {
Text("Value: \(model.proxy.wrappedValue!.value)")
Button(action: {model.proxy.wrappedValue?.value = 555}, label: {Text("555")})
SubView(value: model.proxy.value) // this still wont work
}.padding()
}
}
struct SubView <T:BinaryFloatingPoint> : View {
#Binding var value: T
var body: some View {
Button( action: {value = 100}, label: {Text("B 100")})
}
}
Create a Binding instead.
Example:
var proxy: Binding<Item?> {
Binding<Item?>(
get: { items[1] },
set: { items[1] = $0! }
)
}

How to create bindable custom objects in SWIFT? (conform with ObservableObject)

XCode Version 12.5 (12E262) - Swift 5
To simplify this example, I've created a testObj class and added a few items to an array.
Let's pretend that I want to render buttons on the screen (see preview below), once you click on the button, it should set testObj.isSelected = true which triggers the button to change the background color.
I know it's changing the value to true, however is not triggering the button to change its color.
Here's the example:
//
// TestView.swift
//
import Foundation
import SwiftUI
import Combine
struct TestView: View {
#State var arrayOfTestObj:[testObj] = [
testObj(label: "test1"),
testObj(label: "test2"),
testObj(label: "test3")
]
var body: some View {
VStack {
ForEach(arrayOfTestObj, id: \.id) { o in
HStack {
Text(o.label)
.width(200)
.padding(20)
.background(Color.red.opacity(o.isSelected ? 0.4: 0.1))
.onTapGesture {
o.isSelected.toggle()
}
}
}
}
}
}
class testObj: ObservableObject {
let didChange = PassthroughSubject<testObj, Never>()
var id:String = UUID().uuidString {didSet {didChange.send((self))}}
var label:String = "" {didSet {didChange.send((self))}}
var value:String = "" {didSet {didChange.send((self))}}
var isSelected:Bool = false {didSet {didChange.send((self))}}
init (label:String? = "") {
self.label = label!
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
If I update the ForEach as...
ForEach($arrayOfTestObj, id: \.id) { o in
... then I get this error:
Key path value type '_' cannot be converted to contextual type '_'
How can I change testObj to make it bindable?
Any help is greatly appreciated.
struct TestView: View {
#State var arrayOfTestObj:[TestObj] = [
TestObj(label: "test1"),
TestObj(label: "test2"),
TestObj(label: "test3")
]
var body: some View {
VStack {
ForEach(arrayOfTestObj, id: \.id) { o in
//Use a row view
TestRowView(object: o)
}
}
}
}
//You can observe each object by creating a RowView
struct TestRowView: View {
//And by using this wrapper you observe changes
#ObservedObject var object: TestObj
var body: some View {
HStack {
Text(object.label)
.frame(width:200)
.padding(20)
.background(Color.red.opacity(object.isSelected ? 0.4: 0.1))
.onTapGesture {
object.isSelected.toggle()
}
}
}
}
//Classes and structs should start with a capital letter
class TestObj: ObservableObject {
//You don't have to declare didChange if you need to update manually use the built in objectDidChange
let id:String = UUID().uuidString
//#Published will notify of changes
#Published var label:String = ""
#Published var value:String = ""
#Published var isSelected:Bool = false
init (label:String? = "") {
self.label = label!
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}

Placing SwiftUI Data Sources Somewhere Else

I'm trying to use SwiftUI in a project but beyond the very basic version of using #States and #Bindings that can be found in every tutorial, so I need some help on what I'm doing wrong here.
Environment Setup:
I have following files involved with this problem:
CustomTextField: It's a SwiftUI View that contains an internal TextField along with bunch of other things (According to the design)
CustomTextFieldConfiguration: Contains the things that I need to configure on my custom textfield view
RootView: It's a SwiftUI View that is using CustomTextField as one of it's subviews
RootPresenter: This is where the UI Logic & Presentation Logic goes (Between the view and business logic)
RootPresentationModel: It's the viewModel through which the Presenter can modify view's state
RootBuilder: It contains the builder class that uses the builder pattern to wire components together
The Problem:
The textField value does not update in the textValue property of rootPresentationModel
Here are the implementations (Partially) as I have done and have no idea where I have gone wrong:
CustomTextField:
struct CustomTextField: View {
#Binding var config: CustomTextFieldConfiguration
var body: some View {
ZStack {
VStack {
VStack {
ZStack {
HStack {
TextField($config.placeHolder,
value: $config.textValue,
formatter: NumberFormatter(),
onEditingChanged: {_ in },
onCommit: {})
.frame(height: 52.0)
.padding(EdgeInsets(top: 0, leading: 16 + ($config.detailActionImage != nil ? 44 : 0),
bottom: 0, trailing: 16 + ($config.contentAlignment == .center && $config.detailActionImage != nil ? 44 : 0)))
.background($config.backgroundColor)
.cornerRadius($config.cornerRedius)
.font($config.font)
...
...
...
...
CustomTextFieldConfiguration:
struct CustomTextFieldConfiguration {
#Binding var textValue: String
...
...
...
...
RootView:
struct RootView: View {
#State var configuration: CustomTextFieldConfiguration
var interactor: RootInteractorProtocol!
#Environment(\.colorScheme) private var colorScheme
var body: some View {
HStack {
Spacer(minLength: 40)
VStack(alignment: .trailing) {
CustomTextField(config: $configuration)
Text("\(configuration.textValue)")
}
Spacer(minLength: 40)
}
}
}
RootPresenter:
class RootPresenter: BasePresenter {
#ObservedObject var rootPresentationModel: RootPresentationModel
init(presentationModel: RootPresentationModel) {
rootPresentationModel = presentationModel
}
...
...
...
RootPresentationModel:
class RootPresentationModel: ObservableObject {
var textValue: String = "" {
didSet {
print(textValue)
}
}
}
RootBuilder:
class RootBuilder: BaseBuilder {
class func build() -> (RootView, RootInteractor) {
let interactor = RootInteractor()
let presenter = RootPresenter(presentationModel: RootPresentationModel())
let view: RootView = RootView(configuration: CustomTextFieldConfiguration.Presets.priceInput(textValue: presenter.$rootPresentationModel.textValue, placeholder: "", description: ""), interactor: interactor)
let router = RootRouter()
interactor.presenter = presenter
interactor.router = router
return (view, interactor)
}
}
(That Presets method doesn't do anything important, but just to make sure it will not raise an irrelevant question, here's the implementation):
static func priceInput(textValue: Binding<String>, placeholder: String, description: String) -> CustomTextFieldConfiguration {
return CustomTextFieldConfiguration(textValue: textValue,
placeHolder: placeholder,
description: description,
defaultDescription: description,
textAlignment: .center,
descriptionAlignment: .center,
contentAlignment: .center,
font: CustomFont.headline1))
}
import SwiftUI
struct CustomTextField: View {
#EnvironmentObject var config: CustomTextFieldConfiguration
#Binding var textValue: Double
var body: some View {
ZStack {
VStack {
VStack {
ZStack {
HStack {
//Number formatter forces the need for Double
TextField(config.placeHolder,
value: $textValue,
formatter: NumberFormatter(),
onEditingChanged: {_ in },
onCommit: {})
.frame(height: 52.0)
//.padding(EdgeInsets(top: 0, leading: 16 + (Image(systemName: config.detailActionImageName) != nil ? 44 : 0),bottom: 0, trailing: 16 + (config.contentAlignment == .center && Image(systemName: config.detailActionImageName) != nil ? 44 : 0)))
.background(config.backgroundColor)
.cornerRadius(config.cornerRedius)
.font(config.font)
}
}
}
}
}
}
}
class CustomTextFieldConfiguration: ObservableObject {
#Published var placeHolder: String = "place"
#Published var detailActionImageName: String = "checkmark"
#Published var contentAlignment: UnitPoint = .center
#Published var backgroundColor: Color = Color(UIColor.secondarySystemBackground)
#Published var font: Font = .body
#Published var cornerRedius: CGFloat = CGFloat(5)
#Published var description: String = ""
#Published var defaultDescription: String = ""
#Published var textAlignment: UnitPoint = .center
#Published var descriptionAlignment: UnitPoint = .center
init() {
}
init(placeHolder: String, description: String, defaultDescription: String, textAlignment: UnitPoint,descriptionAlignment: UnitPoint,contentAlignment: UnitPoint, font:Font) {
self.placeHolder = placeHolder
self.description = description
self.defaultDescription = defaultDescription
self.textAlignment = textAlignment
self.descriptionAlignment = descriptionAlignment
self.contentAlignment = contentAlignment
self.font = font
}
struct Presets {
static func priceInput(placeholder: String, description: String) -> CustomTextFieldConfiguration {
return CustomTextFieldConfiguration(placeHolder: placeholder, description: description,defaultDescription: description,textAlignment: .center,descriptionAlignment: .center,contentAlignment: .center, font:Font.headline)
}
}
}
struct RootView: View {
#ObservedObject var configuration: CustomTextFieldConfiguration
//var interactor: RootInteractorProtocol!
#Environment(\.colorScheme) private var colorScheme
#Binding var textValue: Double
var body: some View {
HStack {
Spacer(minLength: 40)
VStack(alignment: .trailing) {
CustomTextField(textValue: $textValue).environmentObject(configuration)
Text("\(textValue)")
}
Spacer(minLength: 40)
}
}
}
//RootPresenter is a class #ObservedObject only works properly in SwiftUI Views/struct
class RootPresenter//: BasePresenter
{
//Won't work can't chain ObservableObjects
// var rootPresentationModel: RootPresentationModel
//
// init(presentationModel: RootPresentationModel) {
// rootPresentationModel = presentationModel
// }
}
class RootPresentationModel: ObservableObject {
#Published var textValue: Double = 12 {
didSet {
print(textValue)
}
}
}
struct NewView: View {
//Must be observed directly
#StateObject var vm: RootPresentationModel = RootPresentationModel()
//This cannot be Observed
let presenter: RootPresenter = RootPresenter()
var body: some View {
RootView(configuration: CustomTextFieldConfiguration.Presets.priceInput(placeholder: "", description: ""), textValue: $vm.textValue//, interactor: interactor
)
}
}
struct NewView_Previews: PreviewProvider {
static var previews: some View {
NewView()
}
}