Behavior of Bookmark URLs and Files App Recently Deleted – Clarification and Potential Bug

I am developing an iOS/iPadOS application and have encountered some behavior regarding Files App and security-scoped bookmarks that I would like to clarify. Additionally, I would like to report some behavior which might include a potential issue.

Question1: Accessing deleted files via bookmark (Specification clarification)

Our app saves file URLs as bookmarks, which file that user has selected on Files App or app-created so to open a file which user has modified previously in the next launch.

When a user deletes a file in Files App (moves a file to Recently Deleted), the app can still resolve the bookmark and access the file for read/write operations. Is this behavior intended? In other words, is it correct that a bookmark can access a file that has been deleted in Files App but not permanently removed?

Question2: Overwriting a file in Recently Deleted (Potential bug)

We noticed that overwriting a file in Recently Deleted behaves differently depending on the method used.

Current implementation

1.Create a temporary file in the same directory

2.Write content to the temporary file

3.Delete the original file ([NSFileManager removeItemAtURL:error:])

4.Move the temporary file to the original file path ([NSFileManager moveItemAtURL:toURL:error:])

Result: The file disappears from Files App Recently Deleted. In contrast, using [NSFileManager replaceItemAtURL:withItemAtURL:] keeps the file visible in Recently Deleted. Is this difference designed behavior? If not, this may be a bug.

Question3: Detecting files in Recently Deleted

We want to detect whether a file resides in Recently Deleted, but we cannot find a reliable and officially supported method.

Recently Deleted files appear under .Trash, but using the path alone is not a reliable method. We have tried the following APIs without success:

  • [NSURL getResourceValue:forKey:NSURLIsHiddenKey error:]
  • [NSURL checkResourceIsReachableAndReturnError:]
  • [NSFileManager fileExistsAtPath:]
  • [NSFileManager isReadableFileAtPath:]
  • [NSFileManager getRelationship:ofDirectory:NSTrashDirectory inDomain:NSUserDomainMask toItemAtURL:error:]

We could not obtain the Recently Deleted folder URL using standard APIs.

  • [NSFileManager URLsForDirectory:NSTrashDirectory inDomains:NSUserDomainMask]
  • [NSFileManager URLForDirectory:NSTrashDirectory inDomain:NSUserDomainMask appropriateForURL:url create:error:]

Could you advise a safe and supported way to detect Recently Deleted files properly by the app?

Question1: Accessing deleted files via bookmark (Specification clarification) Our app saves file URLs as bookmarks, which file that user has selected on Files App or app-created, so to open a file which user has modified previously in the next launch.

As a side note, you may want to take a look at this post which describes a longstanding issue which makes bookmark resolution much less reliable than it would be on macOS.

When a user deletes a file in Files App (moves a file to Recently Deleted), the app can still resolve the bookmark and access the file for read/write operations. Is this behavior intended? In other words, is it correct that a bookmark can access a file that has been deleted in Files App but not permanently removed?

Yes. Bookmarks are a relatively low-level file system construct compared to the "Recently Deleted" concept Files.app presents. As far as the bookmark system is concerned, it's just another file on disk.

Question2: Overwriting a file in Recently Deleted (Potential bug) We noticed that overwriting a file in Recently Deleted behaves differently depending on the method used. Current implementation

  1. Create a temporary file in the same directory
  2. Write content to the temporary file
  3. Delete the original file ([NSFileManager removeItemAtURL:error:])
  4. Move the temporary file to the original file path ([NSFileManager moveItemAtURL:toURL:error:])

Result: The file disappears from Files App Recently Deleted. In contrast, using [NSFileManager replaceItemAtURL:withItemAtURL:] keeps the file visible in Recently Deleted. Is this difference designed behavior?

Yes and no. At the file system level, those are actually totally different operations, at least on APFS/HFS+. The first case is actually a series of otherwise unrelated operations, where you deleted a file and then moved an item in that happened to have the same name as the previous file.

replaceItemAtURL (on file systems which support it, like APFS and HFS+) is a TOTALLY different, much lower level operation. It's underlying implementation uses renamex_np(RENAME_SWAP) (see "man renamex_np" for more info) to atomically exchange the two objects, with the expectation being that the "new" object is a logical replacement for the first object.

Putting the difference in more concrete terms, if you create a file named "/dirA/foo", bookmark that file, then perform these two operations:

Operation 1:

  1. Move "/dirA/foo" to "/dirB/"
  2. Create a new file named "foo" in "/dirA/"

Operation 2:

  1. Create a new file at "/dirB/foo"
  2. use renamex_np(RENAME_SWAP) to "swap" the two files.

Now, if you resolve the bookmark, what file do you get?

Operation 1 -> "/dirB/foo". That's the file you bookmarked, and all you did was move that file. That might seem odd, but it makes a lot more sense if you imagine waiting 10 min. between "1" and "2". Operations to unrelated files shouldn't change what file a bookmark resolves to, so you get "/dirB/foo".

Operation 2 -> "/dirA/foo". The point of renamex_np() is to support save semantics, so it preserves the necessary data so that "/dirA/foo" remains the bookmark target.

Having said that...

If not, this may be a bug.

...yes, please file a bug on this and post the bug number back here. The explanation above is why you're seeing different behavior; however, that doesn't mean that Files.app can't/shouldn't make its own decision about how it should handle this and (potentially) treat those two cases as "the same".

Question3: Detecting files in Recently Deleted

We want to detect whether a file resides in Recently Deleted, but we cannot find a reliable and officially supported method. Recently Deleted files appear under .Trash, but using the path alone is not a reliable method. We have tried the following APIs without success:

[NSFileManager getRelationship:ofDirectory:NSTrashDirectory inDomain:NSUserDomainMask toItemAtURL:error:]

...

[NSFileManager URLForDirectory:NSTrashDirectory inDomain:NSUserDomainMask appropriateForURL:url create:error:]

Both of these APIs should, generally speaking, work, with the limitation that not all volumes provide a "trash" location (for example, some cloud storage vendors, SMB volumes, read-only mounts, etc.).

What's the scenario it's failing at?

We could not obtain the Recently Deleted folder URL using standard APIs. [NSFileManager URLsForDirectory:NSTrashDirectory inDomains:NSUserDomainMask]

This method isn't really reliable, as there isn't actually any single "trash" location.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Hello,

Thanks for your detailed explanation.

...yes, please file a bug on this and post the bug number back here.

Sharing your bug number regarding this topic which I previously filed. FB21561068 https://feedbackassistant.apple.com/feedback/21561068

What's the scenario it's failing at?

When determining whether the URL is in “trash” location by using API with the following steps, we are seeing a failure.

Precondition: Download the attached sample project (TrashedFileURL.zip) in the feedback which I shared with you in above and build the TrashedFileURL.app from the project inside the zip folder.

1.Create a file in NSDocumentDirectory by TrashedFileURL.app. a.file:///private/var/mobile/Containers/Data/Application/{Application UUID}/Documents/{File UUID}.txt

2.Save the Bookmark of the file created in 1. in TrashedFileURL.app.

3.Delete the file by Files.app.

4.Get URL of the file from the Bookmark which is saved on TrashedFileURL.app. a.URL is referring a filepath which is inside of .Trash directory. file:///private/var/mobile/Containers/Data/Application/{Application UUID}/Documents/.Trash/{File UUID}.txt

5.When running [NSFileManager URLForDirectory:NSTrashDirectory inDomain:NSUserDomainMask appropriateForURL:url create:error:] to the URL which got in 4., it will return the following Error.

Error Domain=NSCocoaErrorDomain Code=3328 "The requested operation couldn’t be completed because the feature is not supported."

6.When running [NSFileManager getRelationship:ofDirectory:NSTrashDirectory inDomain:NSUserDomainMask toItemAtURL:error:] to the URL which got in 4., it will return the following Error.

Error Domain=NSCocoaErrorDomain Code=256 "The file couldn’t be opened."

If you need any additional information, please let me know.

Sharing your bug number regarding this topic which I previously filed. FB21561068

Perfect, thank you.

Precondition: Download the attached sample project (TrashedFileURL.zip) in the feedback which I shared with you above and build the TrashedFileURL.app from the project inside the zip folder.

I took a look at your sample and the answer is basically, yes, I think there's a problem on our end. More specifically, looking at the three APIs:

  1. URLsForDirectory-> This should probably just return "NULL", given that there isn't a universally accessible trash target on iOS.

  2. URLForDirectory:appropriateForURL:-> I think there's an argument this should have returned "<app container>/Documents/.Trash/", since we knew the target file. However, it might end up returning NULL as well, as I'm not sure moving the object to that location is necessarily equivalent to trashItemAtURL in the same way it would be on macOS.

  3. getRelationship(NSTrashDirectory)-> This is the critical failure, since we specifically returned the wrong answer to the exact question you asked the system.

That leads back to your original question here:

Could you advise a safe and supported way to detect Recently Deleted files properly by the app?

All I can really suggest here is that you augment your getRelationship check by looking for ".Trash". Strictly speaking, that isn't really "safe“; however, it's hard to imagine why a user would create and store files they intended to keep in a directory called ".Trash".

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Behavior of Bookmark URLs and Files App Recently Deleted – Clarification and Potential Bug
 
 
Q