How to stop xcode debugger from mangling filesystem NSURLs on OS X? - xcode

I am trying to write a Mac app that converts files. I am stuck at the beginning because my app cannot open local files, at least while running in the debugger. I use the NSOpenPanel to create a valid file NSURL:
“file:///Volumes/Seagate_1tib/projects/dataskunk/wasteproduct.xml”
But somewhere in xcode, or the debugger or whatever, this gets mangled into
"/Users/charlweed/Library/Developer/Xcode/DerivedData/dataskunk-ghkiumvdkopxarhavynetidlqxio/Build/Products/Debug/file:/Volumes/bigdrive/dataskunk/wasteproduct.xml"
Reading the file then fails with a "No such file or directory error".
How do I prevent this mangling during development?
For example, this gives the error, no matter what file is chosen:
let fileResult = openFileDialog("Choose File", message:"Message")
let xmlInFileURLOpt: NSURL? = NSURL.fileURLWithPath(fileResult)
if let xmlInFileURL = xmlInFileURLOpt
{
var xmlFileError: NSError?
if !xmlInFileURL.checkPromisedItemIsReachableAndReturnError(&xmlFileError){
println("\(xmlFileError)")
return
}
}

The answer is that NSURL.fileURLWithPath() does not take a URL-path as an argument, only a filesystem-path. So "file:///Volumes/disk/file.xml" is wrong, "/Volumes/disk/file.xml" is correct.
The mangling is NSURL prefixing the current directory onto what it thinks is a relative filesystem-path String.

Related

Move Files In Cocoa

I want to move a file from a folder to another folder
I used
[[NSFileManager defaultManager]moveItemAtPath:#"Folder/Filename.fileExtension" toPath:#"FolderToMoveTheFile" error:nil];
Is there something else to type in error: ?
Why it isn't move my file?
You would get the error by doing this:
NSError* error;
if (![[NSFileManager defaultManager]moveItemAtPath:#"Folder/Filename.fileExtension" toPath:#"FolderToMoveTheFile" error:&error])
{
// handle error, typically using the NSError object pointed to by the error variable
// In an app, you might want to pass it to a -presentError:... method of a convenient responder
// This is good enough for debugging:
NSLog(#"failed to move file: %#", error);
}
The second path that goes after toPath: must be the path including the file name at the destination. It is not correct to just specify the path to the directory into which you want to move the file.
Also, you should typically use absolute paths, not relative paths. You can use relative paths but that then depends on the current working directory for the process. That's arbitrary for apps launched from the Finder or Dock. It's really only meaningful for command-line tools launched from a shell (e.g. a Terminal window) where the user might be expected to cd to a directory and then provide relative paths as command-line arguments.

Get path from OS X file reference URL alias (file:///.file/id=...)

In PyQt4 on Apple Mac OS X, we get a file reference URL to a drag-and-dropped file in the following format:
file:///.file/id=123456.78901
This, interestingly, is not a valid file URL, or at least it doesn't transparently resolve in most applications — there is no /.file/id=... file in the filesystem.
Using just shell, how do I dereference this NSURL or file reference or alias (or whatever it's called) into a valid path to file on the filesystem?
In Swift you can use URL.standardized to get the valid path from a reference URL. So in Swift 4 - to get the file name from a drag-and-dropped file you'd do something like...
let fileType = NSPasteboard.PasteboardType (kUTTypeFileURL as String)
extension NSPasteboardItem {
func fileURL ()->URL? {
if let refURL = self.string(forType: fileType) {
return URL (fileURLWithPath: refURL).standardized
}
return nil
}
}
One can use osascript, an AppleScript interpreter available on default OS X installs, run with the following script:
osascript -e 'get posix path of posix file "file:///.file/id=123.456" -- kthxbai'
Prints /Users/josh/Downloads/paste-10837081.py.
That is a NSURL object, to get a path from it:
NSString *filePath = [fileURL path];
This can also be accomplished in Python using the PyObjC bridge.
from Foundation import NSURL # depends on PyObjC
# convert a URL file reference to a POSIX path
NSURL.URLWithString_('file:///.file/id=6571367.8602676692').path()
# convert a path file reference to a POSIX path
NSURL.fileURLWithPath_('/.file/id=6571367.8602676692').path()
Be sure you to select the appropriate NSURL method for your data type or else you'll get the wrong value back.
Resources
NSURL API doc
API discussion of file reference URLs
PyObjC

copy from main bundle to application support swift

I have copied files like this from an application before and have copied the code exactly as it appears in another app but for what ever reason when it try to run this particular code it will only create the new directory.
It will not however save the binary file I have saved in supporting files in the main bundle to the new directory. I have done a fair amount of googling and searching stack overflow and have decided I might have to post something here.
let path = NSBundle.mainBundle().resourcePath?.stringByAppendingPathComponent("youtube-dl")
let destination = "~/Library/Application Support/Youtube DLX/"
let destinationPath = destination.stringByStandardizingPath
NSFileManager.defaultManager().createDirectoryAtPath(destinationPath, withIntermediateDirectories: true, attributes: nil, error: nil)
NSFileManager.defaultManager().copyItemAtPath(path!, toPath: destinationPath + "youtube-dl", error: nil)
note that the file that I am trying to copy has no extension so the full name is just "youtube-dl"
The result of
let destinationPath = destination.stringByStandardizingPath
has no trailing slash, so you are trying to copy the file to
"~/Library/Application Support/Youtube DLXyoutube-dl"
You should use
destinationPath.stringByAppendingPathComponent("youtube-dl")
to generate the target path as you already did for the source path.
Generally (as just mentioned in a comment), you should use the error parameter and
check the return value for success, for example
var error : NSError?
if !NSFileManager.defaultManager().copyItemAtPath(..., error: &error) {
println("copy failed: \(error)")
}

NodeJS fs.watch on directory only fires when changed by editor, but not shell or fs module

When the code below is ran, the watch is only triggered if I edit and save tmp.txt manually, using either my ide, TextEditor.app, or vim.
It doesn't by method of the write stream or manual shell output redirection (typing echo "test" > /path/to/tmp.txt").
Although if I watch the file itself, and not its dirname, then it works.
var fs, Path, file, watchPath, w;
fs = require('fs' );
Path = require('path');
file = __dirname + '/tmp.txt';
watchPath = Path.dirname(file); // changing this to just file makes it trigger
w = fs.watch ( watchPath, function (e,f) {
console.log("will not get here by itself");
w.close();
});
fs.writeFileSync(file,"test","utf-8");
fs.createWriteStream(file, {
flags:'w',
mode: 0777
} )
.end('the_date="'+new Date+'";' ); // another method fails as well
setTimeout (function () {
fs.writeFileSync(file,"test","utf-8");
},500); // as does this one
// child_process exec and spawn fail the same way with or without timeout
So the questions are: why? and how to trigger this event programmatically from a node script?
Thanks!
It doesn't trigger because a change to the contents of a file isn't a change to the directory.
Under the covers, at least as of 0.6, fs.watch on Mac uses kqueue, and it's a pretty thin wrapper around kqueue file system notifications. So, if you really want to understand the details, you have to understand kqueue, and inodes and things like that.
But if you want a short "lie-to-children" explanation: What a user thinks of as a "file" is really two separate things—the actual file, and the directory entry that points to the actual file. This is what allows you to have things like hard links, and files that can still be read and written even after you've deleted them, and so on.
In general, when you write to an existing file, this doesn't make any change to the directory entry, so anyone watching the directory won't see any change. That's why echo >tmp.txt doesn't trigger you.
However, if you, e.g., write a new temporary file and then move it over the old file, that does change the directory entry (making it a pointer to the new file instead of the old one), so you will be notified. That's why TextEditor.app does trigger you.
The thing is, you've asked to watch the directory and not the file.
The directory isn't updated when the file is modified, such as via shell redirection; in this case, the file is opened, modified, and closed. The directory isn't changed -- only the file is.
When you use a text editor to modify a file, the usual set of system calls behind the scenes looks something like this:
fd = open("foo.new")
write(fd, new foo contents)
unlink("foo")
rename("foo.new", "foo")
This way, the foo file is either entirely the old file or entirely the new file, and there's no way for there to be a "partial file" with the new contents. The renaming operations do modify the directory, thus triggering the directory watch.
Although the above answers seems reasonable, they are not fully accurate. It is actually a very useful feature to be able to listen to a directory for file changes, not just "renames". I think this feature works as expected in Windows at least, and in node 0.9.2 is also working for mac since they changed to the FSEvents API that supports the feature:
Version 0.9.2 (Unstable)

Why is an NSString making my Release version crash but inside of Xcode it works?

My program pulls in a C style string from a file, converts it to an NSString and places it in an NSMutableArray. Every time I run the program, either Debug or Release version, in XCode it runs perfectly. However every time I run it outside of XCode it crashes and the report says "-[NSPlaceholderString initWithString:]: nil argument'". This is the line of code where the problem occurs.
input = [[[NSString alloc] initWithString:[NSString stringWithUTF8String:data->acctNames]] mutableCopy];
I have also tried this:
input = [NSString stringWithUTF8String:data->acctNames];
Can anyone explain what is wrong with this?
Sounds like the file you are opening doesn't exist and the string is not being initialized. You should look at the file path and see if it is an absolute path. Maybe you are trying to open the file in a local directory and the file doesn't exist in the run directory after you have built the binary.

Resources