Swift: How to Expand a Tilde in a Path String

Swift: How to expand a tilde in a path String

Tilde expansion

Swift 1

"~/Desktop".stringByExpandingTildeInPath

Swift 2

NSString(string: "~/Desktop").stringByExpandingTildeInPath

Swift 3

NSString(string: "~/Desktop").expandingTildeInPath

Home Directory

Additionally you can get the home directory like this (returns a String/String?):

NSHomeDirectory()
NSHomeDirectoryForUser("<User>")

In Swift 3 and OS X 10.12 it's also possible to use this (returns a URL/URL?):

FileManager.default().homeDirectoryForCurrentUser
FileManager.default().homeDirectory(forUser: "<User>")

Edit: In Swift 3.1 this got changed to FileManager.default.homeDirectoryForCurrentUser

Tilde-based Paths in Objective-C

Use NSString's stringByExpandingTildeInPath: method.

There are also several other methods that make it easy to work with paths.

Bash like paths to NSURL or Foundation path

In Swift, you can do :

let str = "~/Documents"
// NSString has a function for decoding this, not available to String:
let str2 = (str as NSString).standardizingPath as String

// Swift deprecated the String path manipulation functions,
// but supplies them as part of NSURL:
let url = URL(fileURLWithPath: str)
let url2 = url.standardizedFileURL // Expands the URL

Can I use environment variables or tilde in module.modulemap?

No, the modulemap syntax does not expand tildes or environment variables. It ultimately just expects to stat the path you gave it, and if no file's there, it'll gripe.

  • Here's where the header file lookup is kicked off, during lexing of the module map file.
  • It ultimately passes the path to the SourceManager's FileManager to produce a File object, as here for a header in a framework's Headers/ public header folder.
  • getFile ultimately ends up calling out to getStatValue, which does a cache lookup.
  • The FileSystemStatCache::get eventually grounds out in LLVM's filesystem abstraction, where it calls sys::fs::status, which is documented to act like POSIX stat.
  • POSIX stat works with paths as-is, no tilde or environment variable expansion - the common availability of those is due to the shell helping you out, not something that happens automatically most of the time at the system level.

However, it's standard to use relative paths in module maps. The lexer respects this, and all the module map docs demonstrate this. In the common case where your module map file is colocated with your library and installed alongside it, this should suffice to properly resolve the paths.

Expand tilde to home directory

Normally, the ~ is expanded by the shell before your program sees it.

Adjust how your program acquires its arguments from the command line in a way compatible with the shell expansion mechanism.

One of the possible problems is using exec.Command like this:

cmd := exec.Command("some-binary", someArg) // say 'someArg' is "~/foo"

which will not get expanded. You can, for example use instead:

cmd := exec.Command("sh", "-c", fmt.Sprintf("'some-binary %q'", someArg))

which will get the standard ~ expansion from the shell.

EDIT: fixed the 'sh -c' example.

Can not make path to the file in Swift

When using Swift REPL, the NSBundle.mainBundle()'s path actually points to:

/Applications/Xcode6-Beta6.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/Resources

You may have to use NSFileManager instead:

let manager = NSFileManager.defaultManager()

if manager.fileExistsAtPath("/Users/tsypa/a.txt") {
// file exists, read
} else {
// file doesn't exist
}

Note: You can actually automatically expand tilde in the path to avoid hardcoding the full user's home path:

"~/a.txt".stringByExpandingTildeInPath // prints /Users/<your user>/a.txt

Why is zsh not able to read ~ (tilde) from a path in a script?

Use the following code to get what you want:

export PATH=~/Desktop/Capture/
echo $PATH

# Result: /Users/swift/Desktop/Capture/

Although, when you're using a string, you'll get this:

export PATH="~/Desktop/Capture/"
echo $PATH

# Result: ~/Desktop/Capture/

So to get it right, you'll have to try this approach:

tilde=~
export PATH="${tilde}/Desktop/Capture/"
echo $PATH

# Result: /Users/swift/Desktop/Capture/

P.S. Also, there's one useful command for tilde to be expanded.

Here's an example:

echo tilda=~

# Result: tilda=~

Use magicequalsubst command in zsh:

set -o magicequalsubst
echo tilda=~

# Result: tilda=/Users/swift

How would I resolve a relative path in Objective-C?

Below is my solution which is working well which uses a lower level C function which I was trying to avoid. It is working for my purposes. The full project which uses is available on GitHub with the method I created for Objective-C below.

https://github.com/brennanMKE/Xcode4CodeSnippets/tree/master/SnippetImporter

- (NSString *)resolvePath:(NSString *)path {
NSString *expandedPath = [[path stringByExpandingTildeInPath] stringByStandardizingPath];
const char *cpath = [expandedPath cStringUsingEncoding:NSUTF8StringEncoding];
char *resolved = NULL;
char *returnValue = realpath(cpath, resolved);

if (returnValue == NULL && resolved != NULL) {
printf("Error with path: %s\n", resolved);
// if there is an error then resolved is set with the path which caused the issue
// returning nil will prevent further action on this path
return nil;
}

return [NSString stringWithCString:returnValue encoding:NSUTF8StringEncoding];
}


Related Topics



Leave a reply



Submit