Environment:
- Xcode 26
- iOS 26
- Also tested on iOS 18 (working correctly)
Description:
I'm experiencing a behavior change with URL(fileURLWithPath:) when the filename starts with a tilde (~) character.
On iOS 18, passing a filename like ~MyFile.txt to URL(fileURLWithPath:) treats the tilde as a literal character. However, on iOS 26, the same code resolves the tilde as the home directory, resulting in unexpected output.
Minimal Example:
let filename = "~MyFile.txt"
let url = URL(fileURLWithPath: filename)
print(url.lastPathComponent)
Expected Result (iOS 18):
~MyFile.txt
Actual Result (iOS 26):
924AF0C4-C3CD-417A-9D5F-733FBB8FCF29
The tilde is being resolved to the app's container directory, and lastPathComponent returns the container UUID instead of the filename.
Questions:
1. Is this an intentional behavior change in iOS 26? 2. Is there documentation about this change? 3. What is the recommended approach for extracting filename components when the filename may contain special characters like ~?
Workaround:
Using NSString.lastPathComponent works correctly on both iOS versions:
let filename = "~MyFile.txt"
let result = (filename as NSString).lastPathComponent
// Returns: "~MyFile.txt" ✅
Is this the recommended approach going forward?
Bug filed FB21757864
Thanks. I’ve added my own comments to your bug.
I want to understand whether this is a bug fix …
In my opinion this is a bug, but the resolution of your bug report will act as an official answer to that question (-:
We need to extract the filename without extension for display purposes.
Ah, I’ve found myself in that very conundrum. It’s annoying that we never got a Swift equivalent of Objective-C’s -stringByDeletingPathExtension method. I think it’d be reasonable for you to file an enhancement request for that. And if you do, please post that bug number as well.
The original code used:
Are you sure that’s the right code? Because it doesn’t use deletingPathExtension, which means it returns the name with the extension.
Regardless, given your requirements I agree that prepending a slash is a reasonable option. For example:
func fileNameWithoutExtension(_ fileNameWithExtension: String) -> String {
URL(fileURLWithPath: "/" + fileNameWithExtension).deletingPathExtension().lastPathComponent
}
IMPORTANT This only makes sense when the file doesn’t exist on disk. If the file does exist on disk, it’s better to use the localizedNameKey resource value. That takes into account whether the user wants the extension hidden or not.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"