Way to update user while code is executing? - swift

I'm creating a SwiftUI multiplatform app in XCode and I have a section of my code that hangs. I want to update the user so they know what's happening and are willing to wait. I originally planned to have an alert with text that changed and then planned to have a Text element that updated. In both cases this isn't shown until after the code executes and as such only shows the final message (normally the success done message unless an error made it end sooner).
Is there anyway I can have a visible message to the user through an alert or SwiftUI element that is updated right away and thus will be helpful?

The fact that the alert isn't even shown until after the code executes is bad and incorrect. This suggests that you are doing something lengthy on the main thread, and that is an absolute no-no. You are freezing the interface and you risk the WatchDog process crashing your app before the user's very eyes. If something takes time, do it in the background.

Related

Why would fireEvent work but userEvent not work?

In short, I have a table row with an onclick event. I am getting this via
const row = screen.getByRole('row', { name: /My row/i })
await userEvent.click(row)
This does not trigger the event handler. However, if I do:
const row = screen.getByRole('row', { name: /My row/i })
fireEvent.click(row)
This works fine.
I know userEvent uses more events to simulate a row click, but the event handler works fine in the live application too. Are there any reasons why userEvent might not work?
Like most very strange things, the problem lied elsewhere. But for documentation purposes, this was due to the app rerendering while doing my assertions. What would happen was this:
App renders, making a bunch of API calls
My API call for my test finishes, say, get user
findByText('My User') passes and gets me my DOM element
Another API call finishes, re-rendering the component to show this data
The result of findByText is no longer the current active DOM element
click fires
As its no longer in the document, there's nothing to click/fire an event
I changed my previous lines to check for ALL data loads before grabbing my row and it seems to consistently be working. This means I have to assert things unrelated to my tests, but that may be due to my app having poor UX with things popping in as they load?
Either way, I'm not 100% confident this is the reason, but if
userEvent.click is not firing events, or
toBeInTheDocument is failing, even if findBy worked
It may be due to your app rerendering after you've asserted everything has loaded. Hope I can save someone else 3 days of suffering like I had to to find that simple fact...

Navigate To at start up simulation

When I run the model, I want my model to go to the viewarea of the simulation parameters in the agent of this user interface. So at the simulation and java actions I added this by the next code (also a screenshot is added at the bottom):
root.uI_startUP.SimulationParameters.navigateTo();
The strange thing is when I run the model for the first time, he goes for like 1 second to this view area, but then automatically returns to the main agent. When I stop the model and restart it again (and keep the run window open), this problem is not happening and it is staying in the good user-interface agent view area.
What could be the reason behind this? and how can this be solved.
Added later:
At the moment I fixed it by creating an event which is triggered by the variable start==true, and after that navigates to the interface and sets this value to false. see figure below
This works, and seems to be a solution.
But I'm still curious why the first method is not working..
Seems to be the code in "Initial experiment setup" that messes here.
Remove both code snippets and only call uI_startUP.SimulationParameters.navigateTo() on the "On startup" of Main.
This is how you should do it anyway :)

How do I implement "Cancel" button inside the method building data source in ModelController.m for Page View Controller

I have a Page View Controller that's implemented in 3 files. One of them, the ModelController.m implements "generateData" method, which basically just builds and formats all the pages for this Page View Controller. Inside this method, the first thing I do is I create a Progress Bar popup with "Cancel" button. Then, inside the while() loop, I keep building the pages and at the same time, every 10th page, I update the progress bar for the user to see that the application is still generating the output.
Once the "generateData" method completes, i.e. builds all the pages (and there may be over 1,000 or even 10,000 pages depending on user input), it returns "self" to a method inside RootViewController.m, which in turn passes that generated data in "dataObject" to "viewWillAppear" in DataViewController.m, which finally displays pages in that object to the user.
It all works great, except the "Cancel" button. Because "generateData" method runs on the main thread, it blocks and the "Cancel" button becomes totally unresponsive. By "blocks" I of course mean it takes all CPU cycles, not allowing anyting else to execute, and it may take over a minute to generate 10,000 pages so I really want to allow the user to cancel the operation.
I tried to create a separate thread for the Progress Bar popup which contains Cancel button, but that won't work because all UI operations need to be performed on the application's main thread.
What almost seems to work, is when I put the "generateData" method on a separate thread and keep the Progress Bar on the main thread. This would work just fine, except that now "generateData" is put on another thread to execute in the background and returns immediately, hence returning empty "self" or rather empty "dataObject", causing a crash because there is nothing in that object yet to display.
So how can I check if the "generateData" thread is done, and "return self" ONLY when it's done? I can't really have some BOOL true/false variable and do it in a loop, because that loop on the main thread would be again blocking the "Cancel" button.
Or, alternatively, what is the correct way to implement the "Cancel" button on a lengthy method in iOS? Maybe I'm doing it all wrong, but for the life of me I can't find the right recipe for this.
Thank you in advance for the help, the code is rather extensive and that's why I didn't include any, but I hope my description gives you a good idea of what's going on. I can provide code if that would help.
The correct way to do this is to load the data asynchronously (on a background thread) and update the main (UI) thread to update the progress bar, etc... But you probably don't need a progress bar since you can probably load those pages faster than a user can flip them, so load 100 and let the user play while you continue to load the next 99,900... Except those may not be the exact numbers you should use....
If you continue to use the progress view and cancel button, then you would cancel the background thread when the user presses "Cancel", which would respond since your data generation is on a background thread...
Look into either Grand Central Dispatch or NSInvocationOperations
Here's a good tutorial by Ray Wenderlich to get you started.

Xcode/iPhone -- break when the next event enters my code?

I am working on a large (>30k lines) event-driven app. I have a sequence of inputs that produces a bug. What I want to do is to break as soon as the final input enters my code.
Is there a general way to do that?
I understand that for any specific sequence of inputs, I can find out where that last input is going to enter my code, then set a breakpoint there. What I would like to do is take out the step of "find out where that last input enters my code." In other words, I am running the app in the simulator, and I want to set a flag somewhere that says "break the next time you are going to enter non-system Objective C code." Then I send the event that causes the problem.
I understand what you are asking, but have you tried using an Exception Breakpoint? This will basically act like an auto-inserted breakpoint on the piece of code that throws the exception. If that doesn't work for you, try a symbolic breakpoint
If you want to intercept UI events, you can try subclassing UIWindow and overriding its sendEvent: method, then setting this class as the class of the UIWindow object in your main XIB file. sendEvent: will be called each time the user generates a touch event. Unfortunately, at this point you cannot yet know which UI object will finally consume the event (read: which event handler code will be ultimately called) since that depends on the actual state of the responder chain. But anyway, you can use this method to inject events into the system.

dismissModalView crash iphone app, but not with debugger on

I have an iphone app with a modal view. The app crashes when the modal view get dismissed.
As soon as the modal disappear at the bottom of the screen, and consequently the original view is shown behind, then the app crashes with no entry in console view.
I have tried to switch debugger on and I discovered that the app just run fine, with no crashes at all.
-First, I would like to know why such behaviour, shouldn't the debugger sit just on top without "disturbing" the app ?
-Second, without debugger, can you point out what should I look at to solve my problem ? Or if you encounter something similiar ? Please be as much specific you can, because I am not an expert in objective-c programming.
I don't know what details to give you, the app is a normal one with standard iphone component, but for start I can say the modal view (which is built with IB) is called inside a NavigationBar system.
thanks
When these types of things happen, it is almost always because of memory allocation issues. The first step I would take is to do a "Clean All" and a "Build and Analyze", and look at all the analysis warnings. Analyze is very good at finding basic "use before allocate", "use after deallocate", or "wrong number of retains/releases" types of problems.
The next step is to turn on "Zombie" detection mode, which looks for accesses on memory that has already been deleted (in other words, killed objects coming back from the dead.) To do this, get info on your executable, go to the Arguments tab, and add a "variable to be set in the environment" of "NSZombieEnabled" to value "YES". Then look in the console when you run to see if something looks off.
Finally, you might try creating a new configuration where you use all the release mode settings for optimization, but add debug symbols. Maybe that will shake up the allocation disbursement in memory enough to trigger the bug in debug mode.