Post

Replies

Boosts

Views

Activity

Document-Based App with MVVM
ExampleCode.txt Dear all, I made an app for computing Finite Element Analysis of electric motors. I (think I) managed to follow the MVVM principle by not exposing the model to the views. Now, my goal is to be able to use documents, each representing a different motor. I want to have my files saved on iCloud, and I want to be able to read plain text from it, so some other code (i.e. python) can create new configurations, even though this app is made for building, graphically. Before trying to work with FileDocument, my class ViewModel: ObservableObject had properties with Published, like @Published var staOD = 80.0, and I would have views with TextFields to change these values. Now, I’m trying to blend in FileDocument, and I’m lost. I don’t know how I should work with my data. Under the “Separation of Concerns”, I guessed that: ViewModel: Should handle computations, updates, and application logic. Document: Should focus on data persistence and encapsulate data to be saved/loaded as a document. My ViewModel looks a bit strange to me, and I’m not sure I’m updating it the right way. I have around 100 parameters, I’m afraid I’m updating these parameters too often and unnecessarily every parameter at the same time, even when only one value is changed in the document. What I’m asking: Clarifications on how to work with FileDocument in my case of MVVM (I’m open to change the entire workflow, my main knowledge is on the Model built in Swift, but not SwiftUI) Why isn’t the computed area on the DocumentView at the right value when I open a document? I would like to open documents and have it in the “right” state. In reality, I’m computing an image of the electric motor, and it would be nice to open the document and see the “real” image, and not a dummy image before I can validate the geometry. I have these warnings popping every time I open a document and that scares me, especially because I want ideally to use swift 6 in the coming future, with concurrency the right way. Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates. Thanks a lot for your help, Guillaume I made an example/simplified code that has all the logic. I can't show the entire code in this prompt due to space limitation. Therefore, I put everything (184 lines) in a single Swift file for you to download. You can just create a Multiplatform Document App. Remove all files except the ...App file, in which you can paste the content of the shared swift file. Run on iPhone simulator. Development environment: Xcode 16, macOS 15 Run-time configuration: iOS 18, macOS 15 Open the app, click on "New motor" You will see "Computed area: 3'063..." Click on "Geometry", change "Stator OD" to 60 instead of 80. Click on "Save" button, now Computed area is 863... Click on "Cancel" button, and reopen the same document Problem: area is again 3'063 while when you open Geometry, you see that "Stator OD" is rightfully 60.
1
0
494
Oct ’24
ProgressView updating on multiple steps of computation
Hello, I'm coding an app targeted at iOS but would be nice if it would still work (as is the case now) with iPadOS and macOS. This is a scientific app that performs matrices computations using LAPACK. A typical "step" of computation takes 1 to 5 seconds and I want the user to be able to run multiple computations with different parameters by clicking a single time on a button. For example, with an electric motor, the user clicks the button and it virtually performs the action of turning the motor, time step after time step until it reaches a final value. With each step, the “single step” computation returns a value, and the “multiple step” general computation gathers all these single step values in a list, the aim being to make a graph of these results. So, my issue is making a progress bar so that the user knows where he is with the computation he issued. Using a for loop, I can run the multistep but it won’t refresh the ProgressView until the multistep computation is over. Thus, I decided to try DispatchQueue.global().async{}, in which I update the progress of my computation. It seems to work on the fact of refreshing the ProgressView but I get warnings that tell me I’m doing it the wrong way: [SwiftUI] Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates. I do not know how to publish on ProgressView, also because all the examples I come across on the Internet show how to update ProgressView with a timer, and that is not what I want to do, I want each computation to be achieved, send ProgressView the fact that it can update, and continue with my computation. Also, what I did does not update correctly the final values. As the computation is asynchronous, the multistep function finishes instantaneously, showing a value of 0.0 while when the tasks finishes, the final value should be 187500037500.0. I show you an example code with these values, it’s a dummy and I put a huge for loop to slow down the code and ressemble a computation so you can see the update of the ProgressView. import SwiftUI class Params: ObservableObject {     /// This is a class where I store all my user parameters     @Published var progress = 0.0     @Published var value = 0.0 } struct ContentView: View {     @StateObject var params = Params()     @FocusState private var isFocused: Bool     var body: some View {         NavigationView {             List {                 Section("Electromagnetics") {                     NavigationLink {                         Form {                             ViewMAG_MultiStep(isFocused: _isFocused)                         }                         .toolbar {                             ToolbarItemGroup(placement: .keyboard) {                                 Spacer()                                 Button("Done") {                                     isFocused = false                                 }                             }                         }                     } label: {                         Text("Multi-step")                     }                 }             }             .navigationTitle("FErez")         }         .environmentObject(params)     } } struct ViewMAG_MultiStep: View {     @FocusState var isFocused: Bool     @EnvironmentObject var p: Params     @State private var showResults = false     @State private var induction = 0.0          var body: some View{         List{             Button("Compute") {                 induction = calcMultiStep(p: p)                 showResults.toggle()             }             .sheet(isPresented: $showResults) {                 Text("\(induction)")                                  ProgressView("Progress...", value: p.progress, total: 100)                              }         }         .navigationTitle("Multi-step")     } } func calcSingleStep(p: Params) -> Double {     /// Long computation, can be 1 to 5 seconds.     var induction = p.value     for i in 0...5000000 {         induction += Double(i) * 0.001     }     return induction } func calcMultiStep(p: Params) -> Double{     /// Usually having around 20 steps, can be up to 400.     var induction = 0.0     DispatchQueue.global().async {         for i in 0...5 {             induction += Double(i) * calcSingleStep(p: p)             p.progress += 10.0         }         print("Final value of induction: \(induction)")     }     return induction } struct ContentView_Previews: PreviewProvider {     static var previews: some View {         ContentView()     } }
2
0
1.6k
Aug ’22