How to Convert NSURL to NSString in Objective-C and Swift

How to Convert NSURL to NSString in Objective-C and Swift

Written by

in

Quick answer: To convert an 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) or URL(string: savedString) (Swift).
  • If you saved path, recreate it with [NSURL fileURLWithPath:savedPath] (Objective-C) or URL(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 URL or NSURL as the property type in your models whenever possible, converting to String only at serialization or UI boundaries.
  • Never pass absoluteString to FileManager methods that take a path parameter; always use path (or pass the URL directly to URL-based FileManager overloads).
  • Standardize on either absoluteString or path for 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 lastPathComponent rather 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.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 3.0; this article is shared under the same license.