Two-partition approach also means you don't need to program the flash from the bootloader; you can program from the other app. It is 100% bulletproof and easy to prove as such. The working app never erases itself, so it's always bootable.
My implementation on this is: clear the other partition, write it at any pace you wish, as the last thing write checksum and a "boot counter" which is currently running partition's counter plus 1. After that is done, reset. Then the bootloader logic is simply, "boot the app whose checksum matches the content, and if both are bootable, boot the one with higher counter".
In this approach, bootloader is tiny and trivial, but there are two full set of applications, so available flash effectively halves. This makes sense when the update process is not small or trivial; e.g. it contains full TLS networking stack, which the application uses anyway, so on the app side, the flashing is trivially simple; in the bootloader, it should replicate the whole networking stack. You could do that, and let the application use bootloader's networking stack (you can share the flash, why not), but then you need a way to upgrade that (because it is complex enough it cannot be proven 100% future-proof before the first release), and this just pushes the problem elsewhere: the only 100% reliable way is to duplicate it anyway.
The erase cycle in flashing is relatively long and power consuming. You can't realistically protect it against power loss. And if the whole firmware doesn't fit in RAM at once (as it usually doesn't), you are at the mercy of the upstream system feeding the firmware into your product - that can stall indefinitely or stop working altogether. If you cleared your only working app, any "I have a li-ion battery" doesn't help. So, if you care about product not getting bricked, dual apps or a fully independent bootloader which can do the retry.
Sometimes you will see magical thinking happening: claims of easy wins without duplicating everything, but usually there is a hole somewhere, or some amount of risk. Full duplication of the the whole app is the only obviously 100% bullet-proof when the flashing stack is large. Some people find that adding a physically separate flash chip is somehow easier for them to understand, but I find that weird. Partitioning the internal flash is always the simplest, and external chip makes sense when the otherwise good-fit MCU is already maxed out, and a larger-flash model series is significantly more expensive, complicated or weird than adding the external chip. Rarely so, unless the application is huge (e.g., high resolution graphics for UI).