Accessing StateObject's object without being installed on a View. This will create a new instance each time - SwiftUI
You cannot access any value before they get initialized, use onAppear()
:
import SwiftUI
@main
struct YourApp: App {
@StateObject private var amplifyConfig: AmplifyConfig = AmplifyConfig()
var body: some Scene {
WindowGroup {
ContentView()
.onAppear() {
if (!amplifyConfig.isAmplifyConfigured) {
amplifyConfig.dataStoreHubEventSubscriber()
amplifyConfig.configureAmplify()
}
}
}
}
}
Update: An actual use case
import SwiftUI
@main
struct YourApp: App {
@StateObject private var amplifyConfig: AmplifyConfig = AmplifyConfig()
@State private var isLoaded: Bool = Bool()
var body: some Scene {
WindowGroup {
VStack {
if (isLoaded) { ContentView() }
else { Text("Loading . . .") }
}
.onAppear() {
if (!amplifyConfig.isAmplifyConfigured) {
amplifyConfig.dataStoreHubEventSubscriber()
amplifyConfig.configureAmplify()
completionHandler { value in isLoaded = value }
}
else {
isLoaded = true
}
}
}
}
}
func completionHandler(value: @escaping (Bool) -> Void) {
// Some heavy work here, I am using DispatchQueue.main.asyncAfter for replicating that heavy work is happening! But you use your own code here.
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds(3000)) { value(true) }
}
Use of @StateObject in Document-based SwiftUI application: getting instances instead of references
@StateObject
is just for Views, try ReferenceFileDocument but before you resort to using classes I highly recommend figuring out if you can stick to structs because Swift and SwiftUI work best with value types. See https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes
Form reverts and reloads data on unwind
As I´ve allready mentioned in the comments you are using multiple sources of truth. This is in general a bad idea as you would need to syncronize this changes all over your app.
You mentioned:
I have trimmed down my actual code to replicate the my actual environment, but may have made this example complicated but in the actual app is needed.
So please take this as a more general example:
If you want to reuse your ContentView
pass the data into it. Capsulate this data in the Viewmodel
and pass that into the Environment
.
The View shouldn´t care if the data given is new or should be edited. It has a certain job: presenting what it has been given.
struct ContentView: View {
@EnvironmentObject private var vm: ViewModel
var body: some View {
Form {
TextField("Name", text: $vm.model.name)
Section {
NavigationLink(vm.model.numbers.isEmpty ? "Choose items" : vm.getNames()) {
ListItemsView()
.environmentObject(vm)
}
} footer: { Text("\(vm.model.numbers.description)") }
}
.navigationTitle("Add new")
}
}
struct ListItemsView: View {
@EnvironmentObject private var vm: ViewModel
var body: some View {
Form {
List(0..<10) { index in
Text("\(index)")
.frame(maxWidth: .infinity)
.contentShape(Rectangle())
.onTapGesture {
vm.model.numbers.append(index)
}
}
}
}
}
class ViewModel: ObservableObject {
@Published var model: Model = Model(name: "", numbers: [])
func getNames() -> String{
getNames(from: model.numbers)
}
func getNames(from items: [Int]) -> String {
var output: [String] = []
for item in items {
output.append("\(item)")
}
return output.joined(separator: ", ")
}
}
struct Model {
let id = UUID().uuidString
//use vars here
var name: String
var numbers: [Int]
}
Edit:
This is still possible. With the now changed design I would recommend the following solution:
NavigationView {
Section {
VStack {
List($viewmodel.models) { $model in
NavigationLink("Go to form - \(model.name.isEmpty ? "new user" : model.name)") {
ContentView(model: $model, title: "edit")
}
}
Button("Add new"){
// use this button to add a new model an trigger navigation
viewmodel.models.append(Model(name: "", numbers: []))
showNew = true
}
// create this invisible navigation link to shwo the newly added model
NavigationLink("", isActive: $showNew){
ContentView(model: $viewmodel.models.last!, title: "add new")
}
}
}
.navigationTitle("Main screen")
}
your Model
would need to be Identifiable
and changes those let
´s to var
´s:
struct Model: Identifiable {
let id = UUID().uuidString
var name: String
var numbers: [Int]
}
class ViewModel: ObservableObject {
@Published var models: [Model]
init(){
models = [
Model(name: "Michael", numbers: [1, 5, 7]),
Model(name: "Jan", numbers: [1]),
Model(name: "Liam", numbers: [3, 6]),
Model(name: "Rav", numbers: [7, 8]),
Model(name: "Paul", numbers: [2, 4])
]
}
}
struct ContentView: View {
@Binding var model: Model
let title: String
var body: some View {
Form {
TextField("Name", text: $model.name)
Section {
NavigationLink(model.numbers.isEmpty ? "Choose items" : getNames()) {
ListItemsView(model: $model)
}
} footer: { Text("\(model.numbers.description)") }
}
.navigationTitle(title)
}
func getNames() -> String{
var output: [String] = []
for item in model.numbers {
output.append("\(item)")
}
return output.joined(separator: ", ")
}
}
struct ListItemsView: View {
@Binding var model: Model
var body: some View {
Form {
List(0..<10) { index in
Text("\(index)")
.frame(maxWidth: .infinity)
.contentShape(Rectangle())
.onTapGesture {
model.numbers.append(index)
}
}
}
}
}
Related Topics
How to Add a Show More/Show Less Uibutton to Control Uitextview
Saving Coredata to a Web Server with Swift 3.0
Swift 2 for Loop Low and High Date Between
How to Move Application's Window Between Virtual Desktops in Os X
Solving System of Equations in Swift
How to Sum the Numbers(Int16) of Stored Core Data - Swift 3
Swift-Setting a Physics Body Velocity by Angle
How to Make a Segue to Second Item of Tab Bar
Firebase iOS Receive Data from Push Notification
How to Add 3D Shapes in Swift Ui
Custom Annotation Showing Same Image for All Different Types of Poi'S
Swift Struct with Lazy, Private Property Conforming to Protocol
Command Failed Due to Signal: Segmentation Fault: 11 While Emitting Ir Sil Function
How to Sort Dates in a Dictionary
How to Apply a Grace Time Using Rx
Ios15 Uttype Deprecations for Url-Extension
Type Check Operator (Is) for Check VS Homogenous Protocol: Why Can This Be Done for Optionals