NSURL to an NSString in Objective-C (or a URL to a String in Swift), use absoluteString when you need the full URL including the file:// or https:// scheme, or use path when you need a local filesystem path for APIs like FileManager.Why this happens
In Apple’s Foundation framework, NSURL (and its Swift value-type counterpart URL) is not a subclass of NSString or String. It is a structured object that parses and stores components such as the scheme (https, file), host, path, query parameters, and fragment.
Because of that separation, you cannot cast an NSURL directly to an NSString or pass a URL where a String is expected. Attempting to store an NSURL in a string property or pass it to string-based APIs causes a compiler type mismatch in Swift and incompatible pointer warnings or runtime crashes in Objective-C.
Choosing the right conversion property matters because a URL can represent either a remote web address or a local file on disk. Using the wrong property—such as passing absoluteString to FileManager—results in broken file lookups because file:// prefixes and percent-encoded characters like %20 are not valid POSIX filesystem paths.
How to fix it
1. Use absoluteString for the complete URL string
If you need the entire URL intact—including its scheme, host, query string, and percent-encoding—read the absoluteString property. This is the right choice for web URLs, logging, or persisting a URL that you plan to reconstruct later with URL(string:).
In Objective-C:
NSURL *imageURL = [NSURL URLWithString:@"https://example.com/images/photo%201.jpg"];
NSString *urlString = imageURL.absoluteString;
In Swift:
import Foundation
let imageURL = URL(string: "https://example.com/images/photo%201.jpg")!
let urlString: String = imageURL.absoluteString
If you convert a local file URL with absoluteString, the resulting string starts with file:///. Keep that in mind before passing it to any API that expects a standard filesystem path.
2. Use path for local files and FileManager
When working with local images, documents, or temporary files selected from the photo library or document picker, FileManager and UIImage(contentsOfFile:) expect a POSIX path without the file:// scheme and without percent-encoding.
In Objective-C:
NSURL *fileURL = [NSURL fileURLWithPath:@"/var/mobile/Documents/My Photo.jpg"];
NSString *filePath = fileURL.path;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
In Swift:
import Foundation
let fileURL = URL(fileURLWithPath: "/var/mobile/Documents/My Photo.jpg")
let filePath: String = fileURL.path
let exists = FileManager.default.fileExists(atPath: filePath)
On iOS 16, macOS 13, and later, Swift’s URL also provides path(percentEncoded:), which makes your intent explicit:
let decodedPath = fileURL.path(percentEncoded: false)
Passing false ensures spaces and special characters are decoded into normal characters for filesystem APIs.
3. Reconstruct the URL properly when reading it back
If you store the converted string in a model property, database, or UserDefaults, make sure you use the matching initializer when turning the string back into an NSURL or URL:
- If you saved
absoluteString, recreate it with[NSURL URLWithString:savedString](Objective-C) orURL(string: savedString)(Swift). - If you saved
path, recreate it with[NSURL fileURLWithPath:savedPath](Objective-C) orURL(fileURLWithPath: savedPath)(Swift).
Mixing these up is a common source of nil URLs or invalid paths. For example, calling URL(string: "/var/mobile/My Photo.jpg") fails if the path contains unencoded spaces, whereas URL(fileURLWithPath:) handles spaces automatically.
4. Store only the filename for app-owned files on iOS
If you copy a user-selected image into your app’s Documents directory and save the full path string, your saved path can break after an app update or restore because iOS changes the sandbox container UUID.
Instead of saving the full path or absoluteString, extract just the filename using lastPathComponent and rebuild the URL dynamically at runtime:
import Foundation
// Save only the filename
let fileName = savedFileURL.lastPathComponent
// Rebuild the URL when loading
if let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let restoredURL = documentsURL.appendingPathComponent(fileName)
print(restoredURL.path)
}
How to prevent it
- Keep
URLorNSURLas the property type in your models whenever possible, converting toStringonly at serialization or UI boundaries. - Never pass
absoluteStringtoFileManagermethods that take a path parameter; always usepath(or pass theURLdirectly toURL-basedFileManageroverloads). - Standardize on either
absoluteStringorpathfor any string-backed persistence so decoding logic never guesses which format was stored. - For files stored inside your app’s sandbox, persist relative paths or
lastPathComponentrather than full container paths.
FAQ
What is the difference between absoluteString and path on NSURL?
absoluteString returns the full URL string including the scheme (https:// or file://) and percent-encoding. path strips the scheme and host and decodes percent-encoded characters, returning a filesystem path suitable for FileManager.
Why does FileManager.fileExists(atPath:) return false for my absoluteString?
fileExists(atPath:) expects a POSIX path such as /var/mobile/.../image.jpg, not a URL string starting with file:///. Switch from url.absoluteString to url.path (or url.path(percentEncoded: false) on newer OS versions).
Should I use relativeString instead of absoluteString?
Only when you specifically created a URL with a relativeTo: base URL and want the relative portion alone. For standalone URLs, relativeString and absoluteString return the same value, so absoluteString is clearer and safer.
Parts of this guide are adapted from content licensed under CC BY-SA 3.0; this article is shared under the same license.
