I don't know that it's clearly documented when the Task is queued onto the main actor relative to the code around it, but it's hard to imagine any implementation that would not print "3" before "5". Still, I don't think this a great way to write this code, and I would suggest that using MainActor.run is a "code smell" here.
Since runTest is isolated to the main actor, there's really no point in using run that I can see from this example. The prints are necessarily going to run sequentially, other than perhaps "3", so you may as well write:
Now it becomes a question of why you are interested in an ordering between "3" and "5". If you want all of the task "3" to finish before "5", then you could just as well rewrite this as:
Note also that there's no real need for runTest to be async in either of these rewritten versions, and if you drop the keyword there's no await in viewDidLoad either:
i posed a similar question recently, but with slightly different specifics. my takeaway from the resulting commentary was that treating Task { @MainActor in ... } as analogous to enqueuing work on a FIFO serial queue is subtly inaccurate. i'm not sure there's any guarantee in the concurrency model that the print("3") and print("5") statements will be ordered in a particular way.
The safest assumption about any "is there a guarantee around the order of execution between tasks" question is "no". The purpose of a task is to allow concurrency; within the task, you get sequential execution, but the task naturally wants to run independently of other tasks, and you should make minimal assumptions about how it executes with respect to other tasks, and use explicit communication mechanisms between tasks, such as task groups, async let, or the channels and queues from swift-async-algorithms, where you need communication. If you need a sequence of things to execute in a specific order, the best way to go about that is to have those things happen in that specific order on a single task.
Note though that from my experience a task iterating on an async sequence does not guarantee fully sequential behavior if the body of the loop has async functions. It appears to run parts of the body for one element interlieved with later elements. Placing an NSLock forced sequential behavior, but in swift 6 that is not going to work.