My latest encounter was a situation where a mobile app needed to send an image and an accompanying textfile to a server, but the textfile couldn't be sent until the image had been received and an ID returned and inserted into the textfile.
It would've been perfectly possible for the backend to already receive both the image and textfile, insert the image, update the textfile, and then insert the textfile.
If you don't want the backend to update the textfile, you've made an arbitrary decision that effectively makes it impossible to post the image and textfile in one request while having the receiver generate the ID.
There's nothing wrong with making that decision, but you can't then generalize your observation about software development in general, which is what your question starts doing.
until you start taking mobile network error handling into account
You are overgeneralizing a very niche problem here.
One major design decision that comes from mobile connectivity, that you don't usually find in other applications, is the ability to work offline when the network is down.
For your average enterprise application, if the network is down everyone stops working on the system. No one can do any work anymore (i.e. updating the state of the application) until the network connection has resumed. But in a mobile context, you specifically want your mobile application to continue working, queue its updates, and send those out when the connection has been restored.
This leads to your mobile app needing the ability to both remember and reference a completely new entity that it just created but has not been saved in the database yet (e.g because the network is down). That reference becomes its ID.
And while it is technically possible to generated a temporary ID and later have the backend replace that ID with one the backend generated for itself, it becomes quite difficult to process the queued updates if those future updates might still be referencing that temporary ID that you just changed into a different permanent one.
It can be done, but it's really not worth the effort.
When you reach that point, then I agree that having the mobile app generate its own identifiers makes a lot of sense, if it's a non-sequential data type like GUID. Most definitely not ints because multiple mobile apps are likely to reuse int values while offline.
I've noticed that usually it requires a lot more complexity to let the receiver generate an ID
And am I right in thinking that the best/least complex approach usually is to let the sender set the ID instead of the receiver?
In a mobile context, it is indeed the easier option. In a general context, it isn't.
It would've helped if you posted an example other than in a mobile context. In my experience, with the exception of needing to work offline during network outages, it is vastly easier to delegate the generation of IDs to the database server.