NSData's writeToFile method failing with server address - cocoa

I am trying to write an NSData object to a directory like so;
[myData writeToFile:[NSString stringWithFormat:#"%#/%#.txt", path, filename] atomically:YES];
I receive no errors or warnings but I am assuming the write fails because the path variable has the format of afp://10.0.0.20/username/Desktop. I am connected to the networked share.
Do I need to modify the string or take a different approach here?
EDIT: Tried the following approach after recommendation but it failed
NSMutableURLRequest *post = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"afp://10.0.0.20/username/Desktop/filename.txt"]];
[post setHTTPMethod: #"POST"];
[post setHTTPBody:horreumImageDataNewThread];
NSURLResponse *response;
NSError *error;
[NSURLConnection sendSynchronousRequest:post returningResponse:&response error:&error];
TIA,
Ricky.

Where are you getting the afp path string from? You would normally write to a networked volume by using a path like:
/Volumes/NameOfMountedVolume/path/to/file
Also, you should use the -stringByAppendingPathComponent: method of NSString to concatenate paths:
NSString* fullPath = [path stringByAppendingPathComponent:filename];

Related

Failed attempt using Related Items to create backup file in sandboxed app

The App Sandbox design guide says:
The related items feature of App Sandbox lets your app access files
that have the same name as a user-chosen file, but a different
extension. This feature consists of two parts: a list of related
extensions in the application’s Info.plist file and code to tell the
sandbox what you’re doing.
My Info.plist defines a document type for .pnd files (the user-chosen file), as well as a document type for .bak files. The entry for the .bak files has, among other properties, the property NSIsRelatedItemType = YES.
I am trying to use Related Items to move an existing file to a backup file (change .pnd suffix to .bak suffix) when the user writes a new version of the .pnd file. The application is sandboxed. I am not proficient with sandboxing.
I am using PasteurOrgManager as the NSFilePresenter class for both the original and backup files:
#interface PasteurOrgData : NSObject <NSFilePresenter>
. . . .
#property (readonly, copy) NSURL *primaryPresentedItemURL;
#property (readonly, copy) NSURL *presentedItemURL;
#property (readwrite) NSOperationQueue *presentedItemOperationQueue;
#property (readwrite) NSFileCoordinator *fileCoordinator;
. . . .
- (void) doBackupOf: (NSString*) path;
. . . .
#end
The doBackupOf: method is as follows. Notice that it also sets the NSFilePresenter properties:
- (void) doBackupOf: (NSString*) path
{
NSError *error = nil;
NSString *appSuffix = #".pnd";
NSURL *const pathAsURL = [NSURL URLWithString: [NSString stringWithFormat: #"file://%#", path]];
NSString *const baseName = [pathAsURL lastPathComponent];
NSString *const prefixToBasename = [path substringToIndex: [path length] - [baseName length] - 1];
NSString *const baseNameWithoutExtension = [baseName substringToIndex: [baseName length] - [appSuffix length]];
NSString *backupPath = [NSString stringWithFormat: #"%#/%#.bak", prefixToBasename, baseNameWithoutExtension];
NSURL *const backupURL = [NSURL URLWithString: [NSString stringWithFormat: #"file://%#", backupPath]];
// Move backup to trash — I am sure this will be my next challenge
// (it's a no-op now because there is no pre-existing .bak file)
[[NSFileManager defaultManager] trashItemAtURL: backupURL
resultingItemURL: nil
error: &error];
// Move file to backup
primaryPresentedItemURL = pathAsURL;
presentedItemURL = backupURL;
presentedItemOperationQueue = [NSOperationQueue mainQueue];
[NSFileCoordinator addFilePresenter: self];
fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter: self]; // error here
[self backupItemWithCoordinationFrom: pathAsURL
to: backupURL];
[NSFileCoordinator removeFilePresenter: self];
fileCoordinator = nil;
}
The backupItemWithCoordinationFrom: method does the heavy lifting, basically:
[fileCoordinator coordinateWritingItemAtURL: from
options: NSFileCoordinatorWritingForMoving
error: &error
byAccessor: ^(NSURL *oldURL) {
[self.fileCoordinator itemAtURL: oldURL willMoveToURL: to];
[[NSFileManager defaultManager] moveItemAtURL: oldURL
toURL: to
error: &error];
[self.fileCoordinator itemAtURL: oldURL didMoveToURL: to];
}
but the code doesn't make it that far. I have traced the code and the URL variables are as I expect, and are reasonable. At the point of "error here" in the above code, where I allocate the File Presenter, I get:
NSFileSandboxingRequestRelatedItemExtension: an error was received from pboxd instead of a token. Domain: NSPOSIXErrorDomain, code: 1
[presenter] +[NSFileCoordinator addFilePresenter:] could not get a sandbox extension. primaryPresentedItemURL: file:///Users/cope/Me.pnd, presentedItemURL: file:///Users/cope/Me.bak
Any help is appreciated.
(I have read related posts Where can a sandboxed Mac app save files? and Why do NSFilePresenter protocol methods never get called?. I have taken note of several other sandboxing-related posts that don't seem relevant to this issue.)
MacBook Pro, MacOS 10.13.5, XCode Version 9.3 (9E145)
do not read too much about avoiding sandboxing. Most explenations go too far out of the most obvious problem. Instead of explaining the pitfalls that rightfully triggers sandboxing they explain mostly how to avoid the Sandbox at all. Which is not a solution - it is a thread!
So the most obvious problem is exposing a URL to pasteboard that still needs properly escaped characters in the string before you transform to NSURL.
So your NSString beginning with "file://" should use something like..
NSString *encodeStringForURL = [yourstring stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
before you transform to NSURL with
NSURL *fileurl = [NSURL URLWithString:encodeStringForURL];
NString *output = fileurl.absoluteString;

get file attributes with nsfilesustem in cocoa no such file exists

I've searching for about 3 hours of how to get the creation date of a file, I get the URL with an enumerator, after that I pass it to path correcting the percents, and finally I try to get the file attributes...so, I don no alter the path in anyway but I always get the same error "The operation couldn’t be completed. No such file or directory", and the path "file:///Users/raul/Desktop/DSC_0386.JPG".
The code sample:
NSError* error = nil;
//NSDictionary* fileAttribs = [[NSFileManager defaultManager] attributesOfItemAtPath:[[url absoluteString] stringByRemovingPercentEncoding] error:&error];
NSDictionary* fileAttribs = [[NSFileManager defaultManager] attributesOfItemAtPath:#"file://Users/raul/Desktop/DSC_0386.JPG" error:&error];
NSLog(#"%#",error);
NSDate *fecha = [fileAttribs objectForKey:NSFileCreationDate];
I've commented the first NSDictionary to try out the second statement with the nsstring directly.
I've checked that my file already exists.
Please, any help?? I'm missing anything?
Several issues:
1) In most cases, you shouldn't have to convert an NSURL to a path string in order to operate on a file. In particular, you can use the "resource value" API of NSURL to get the creation time directly:
NSDate* creationDate;
NSError* error;
if ([url getResourceValue:&creationDate forKey:NSURLCreationDateKey error:&error])
/* use creationDate */;
else
/* handle error */;
2) If you do need to get a path string from NSURL, don't use -absoluteString. That will still be a URL string, with things like "file://", etc. A URL string is not a valid path string. The error message you quoted in your question was already telling you this. It showed you a file "path" of "file:///Users/raul/Desktop/DSC_0386.JPG", but that's not a path at all.
You should just use the -path method. You do not need to do anything with percent encoding when you get the -path.
3) You should ignore any error output parameter until you have checked whether the method you called succeeded or failed, usually by examining its return value. That is, the code you posted should be reorganized like this:
NSError* error = nil;
NSDictionary* fileAttribs = [[NSFileManager defaultManager] attributesOfItemAtPath:#"file://Users/raul/Desktop/DSC_0386.JPG" error:&error];
if (fileAttribs)
{
NSDate *fecha = [fileAttribs objectForKey:NSFileCreationDate];
// ... use fecha ...
}
else
NSLog(#"%#",error);

What’s the correct way to create an NSURL from an NSString?

I’ve got an NSString that stores the path to a saved file:
NSString *filePath = [NSString stringWithFormat:
#"%#/someFolder/%#",
NSHomeDirectory(),
[NSString stringWithFormat:#"%#",[self.fileName stringByAppendingPathExtension:#"txt"]]];
And it’s OK — when I log it, I get:
/Users/username/someFolder/fileName.txt
So my next step is to make an NSURL object from this NSString. I did this:
NSURL *pathURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#", filePath]];
NSLog(#"URL = %#", pathURL);
but the response is:
URL = (null)
What’s wrong here? How can I do this correctly?
A path is not a valid URL by itself. You have to use this:
NSURL *pathURL = [NSURL fileURLWithPath:filePath];
And read the documentation. (And don’t overuse / abuse format strings.)

How to get the path/directory component from a NSURL?

I retrieved a NSURL from a NSSavePanel. I now have this NSURL which gives me the following:
file://localhost/Users/brett/Documents/asdf%20asdf.json
Now, it is easy for me to retrieve just the filename using something like the following:
[[[NSFileManager defaultManager] displayNameAtPath:pathAndFilename] stringByDeletingPathExtension]
This gives me just the localized filename, as expected: asdf%20asdf
So, how do I get the path, like so: file://localhost/Users/brett/Documents/
-[NSURL URLByDeletingLastPathComponent] is the simplest way to achieve this.
You could use NSString methods to work with file paths. For example,
NSString *directory = [[URL absoluteString] stringByDeletingLastPathComponent];
NSString *filename = [[URL absoluteString] lastPathComponent];
You could find other useful methods in Apple Docs: NSString Class Reference -> Working with Paths section
Directly from your NSSavePanel:
NSSavePanel *savePanel;
...
NSString *path = savePanel.directoryURL.path;

Reading a plist

I'm trying to read ~/Library/Preferences/com.apple.mail.plist (on Snow Leopard) to get the email address and other information to enter into the about dialog. I'm using the following code, which is obviously wrong:
NSBundle* bundle;
bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"~/Library/Preferences/com.apple.mail.plist" ofType:#"plist"];
NSDictionary *plistData = [NSDictionary dictionaryWithContentsOfFile:plistPath];
NSString *item = [plistData valueForKeyPath:#"MailAccounts.Item 2.AccountName"];
NSLog(#"Result = %#", item);
Moreover, the value I need to read is MailAcounts -> Item 2 -> AccountName and I am not sure I am doing this correctly (due to the space in the Item 2 key).
I tried reading Apple's developer guide to plist files but no help there.
How can I read a plist and extract the values as an NSString?
Thanks.
The first level is an array, so you need to use "MailAccounts.AccountName" and treat it as NSArray*:
NSString *plistPath = [#"~/Library/Preferences/com.apple.mail.plist" stringByExpandingTildeInPath];
NSDictionary *plistData = [NSDictionary dictionaryWithContentsOfFile:plistPath];
NSArray *item = [plistData valueForKeyPath:#"MailAccounts.AccountName"];
NSLog(#"Account: %#", [item objectAtIndex:2]);
Alternatively you can go by keys and pull the array from "MailAccounts" first using valueForKey: (which will yield NSArray*) and then objectAtIndex: to get the dictionary of that particular account (useful if you need more than the name).
Two things:
You don't want or need to use NSBundle to get the path to the file. The file lies outside of the app bundle. So you should just have
NSString *plistPath = #"~/Library/Preferences/com.apple.mail.plist";
You have to expand the tilde in the path to the user directory. NSString has a method for this. Use something like
NSString *plistPath = [#"~/Library/Preferences/com.apple.mail.plist" stringByExpandingTildeInPath];

Resources