You don't show how the view uses the result. That should help.
I would do it with recursively calling iteration steps.
In the View, as an action for a button, call
simulation()
This function is defined as
func simulation() {
let numberOfStep = 100000 // any very large number
runSimulation(in: stopped.atStep..<numberOfStep)
}
The trick is that in runSimulation(), you call calls it recursively
func runSimulation<T: Sequence>(in sequence: T) where T.Element == Int {
// execute one step of the loop
runSimulation(in: sequence.dropFirst()) // call recursively
}
stopped is a state var
@State var stopped : (on: Bool, atStep: Int) = (on: false, atStep: 0)
atStep is the value (loop index) at which you may have stopped with another button action in the view, which let's you resume where you stopped if you want with a resume button.
Note that in your case, you could do it by chunks of 100 steps instead of individual steps, so the number of steps would be 1000 instead of 100000 for instance.
Much more detailed explanation here:
https://stackoverflow.com/questions/64202210/dispatchqueue-main-asyncafter-not-delaying
Hope that helps.