Kevin, thanks for your response. That clears things up quite a bit. I'll try to summarize what I've learned here, as well as some lingering questions - feel free to correct.
Learned
My question in the original post was caused by the File system changes introduced in iOS 17 - iOS 17 will put the app bundle and its data container on different volumes.
When url(for:in:appropriateFor:create:) creates an itemReplacementDirectory, it uses the volume from the appropriateFor url: parameter.
Since the app bundle is on a different volume now, the method throws a file permission error.
There are 2 kinds of temporary files - a) those will eventually end up in another destination, and b) those don't need to be kept around and will be purged with the temporary directory.
in case a), the temporary file is intended to replace another file, say we want to modify the temporary copy and overwrite the original file, itemReplacementDirectory with url(for:in:appropriateFor:create:) is the most suitable API.
in case b), the temporary file may be just an transitory output that we may read from, then throw away. NSTemporaryDirectory() and its equivalents are the most suitable.
The itemReplacementDirectory has no guarantee to be under the tmp directory, and we should not rely on any folder structure of its parent directories, to recursively remove items, or whatever.
Questions
There are a couple of doc pages talking about temporary directories on Apple Developer Documentation.
https://developer.apple.com/documentation/foundation/1409211-nstemporarydirectory
https://developer.apple.com/documentation/foundation/filemanager/1642996-temporarydirectory
https://developer.apple.com/documentation/foundation/filemanager/1407693-url
https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/itemreplacementdirectory
On the 1st page, it says "see url(for:in:appropriateFor:create:) for the preferred means of finding the correct temporary directory".
On the 3rd page discussion section, it says "You can use this method to create a new temporary directory."
On the 4th page, it says "Pass this constant … to create a temporary directory."
These docs were where the confusion really came from. It pushed us to use url(for:in:appropriateFor:create:) API in the first place, only later did we find out that API best suits a edit-and-move temp file (case a above), not for a use-and-throw temp file.
Hope some distinction between these 2 usecases can be added to the doc's discussion.
Two luxuries come with using url(for:in:appropriateFor:create:) with itemReplacementDirectory API are…
a) it helps create the directory instead of us calling createDirectory manually, and
b) it creates a subdirectory that is app-specific. This doesn't mean much on iOS, but as we are doing cross-platform apps/frameworks, this make macOS development easier. Besides, the item replacement directory always has the bundle name in part of its path components, which makes debugging easier. i.e.,
# NSTemporaryDirectory()
file:///var/folders/09/qv7t964n5nv1zh6bqvzthlvc0000gp/T/
# itemReplacementDirectory
file:///var/folders/09/qv7t964n5nv1zh6bqvzthlvc0000gp/T/TemporaryItems/NSIRD_MyProject_dNMkNh/
Indeed, we can easily create our own method to provide these capabilities, but it's always hard to say no to a native one-liner API that behaves almost exactly as we wanted. 😉