Using NSOpenPanel and copying selected file - Objective-C - objective-c

I'm trying to create an app that will copy the selected sound file to the app's directory. To do it, I've written the following code :
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
[openDlg setCanChooseFiles:YES];
[openDlg setAllowsMultipleSelection:NO];
[openDlg setCanChooseDirectories:NO];
[openDlg setAllowedFileTypes:[NSArray arrayWithObjects:#"aif",#"aiff",#"mp3",#"wav",#"m4a",nil]];
if ( [openDlg runModalForDirectory:nil file:nil] == NSOKButton )
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *dataPath = [[NSBundle mainBundle] bundlePath];
NSLog(#"Datapath is %#", dataPath);
NSLog(#"Selected Files : %#",[[openDlg URLs] objectAtIndex:0]);
if ([fileManager fileExistsAtPath:dataPath] == NO)
{
[fileManager copyItemAtPath:[[openDlg URLs] objectAtIndex:0] toPath:[[NSBundle mainBundle] bundlePath] error:&error];
NSLog(#"File copied");
}
}
The problems are that I can select each types of files (not only aif, wav, mp3 etc.) and I never get File copied. Although, the paths are correct. When I delete the if statement, I get an error saying : [NSURL fileSystemRepresentation]: unrecognized selector sent to instance 0x1005a0b90.
What's wrong in this code?

You're passing an NSURL to an API that expects a path in an NSString. You might consider using the URL-based API:
- (BOOL)copyItemAtURL:(NSURL *)srcURL toURL:(NSURL *)dstURL error:(NSError **)error NS_AVAILABLE(10_6, 4_0);
Like this:
[fileManager copyItemAtURL: [[openDlg URLs] objectAtIndex:0] toURL: [NSURL fileURLWithPath: [[NSBundle mainBundle] bundlePath]] error:&error];
Also, I'm guessing that the file has already been copied to the bundle since your description indicates that [fileManager fileExistsAtPath:dataPath] is returning NO (since your NSLog is never executed.) You can either check that manually, or you can ask the NSFileManager to delete any existing file before copying in a newly selected file.

Related

NSFileHandle from NSURL failure

I'm trying to write some data to a file. I am able to obtain an NSURL from a file dialog and the NSURL obtained has the following value
file:///Users/brad/Documents/2016-05-04-06-53-35.csv
My code does the following:
NSURL* myFile = [panel URL]; // is file:///Users/brad/Documents/2016-05-04-06-53-35.csv
NSError *error = nil;
NSFileHandle *myFileHandle = [NSFileHandle fileHandleForWritingToURL:myFile error:&error];
NSLog(#"%#",error);
if (myFileHandle)
{
... // do something
}
else
{
NSLog(#"File operation failed");
}
I get an error which indicates the following
Error Domain=NSCocoaErrorDomain Code=2 "(null)" UserInfo={NSFilePath=/Users/brad/Documents/
Also myFileHandle is nil.
Any clues ?
See the documentation for NSFileHandle fileHandleForWritingToURL:error:. You get back nil if the file doesn't already exist.
Use NSFileManager to first check if the file exists or not. If not, use NSFileManager createFileAtPath:contents:attributes: to create an empty file.
NSURL *myFile = [panel URL];
if (![[NSFileManager defaultManager] fileExistsAtPath:[myFile path]]) {
[[NSFileManager defaultManager] createFileAtPath:[myFile path] contents:[NSData data] attributes:nil];
}
NSError *error = nil;
NSFileHandle *myFileHandle = [NSFileHandle fileHandleForWritingToURL:myFile error:&error];

reading from file: file doesn't exist

I have an App both in C# and Objective-C, that has to read from text file. Since the text file is created after the App, the user has the option to save it anywhere. In C# it was not difficult to find the file, only using file name with out the full path. In Objective-C this seems to be difficult. I can't ask the users to save it at specific Path, it is against Apple's App regulation. Any Idea how to search for the particular file with only filename? During my trial I used NSFileManager to detect the file, and the App can read the file if it is saved in Applications(Mac),But if i save the file in Documents or anywhere else , gives me Error:'file doesn't exist'.
You can save your txt file within your app folder. You can create directory.
Here is some code to create directory in Documents directory.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSFileManager *flmngr = [NSFileManager defaultManager];
BOOL isDir;
if (![flmngr fileExistsAtPath:[NSString stringWithFormat:#"%#/yourDicretoryName", documentsDirectory] isDirectory:&isDir]) {
[flmngr createDirectoryAtPath:[NSString stringWithFormat:#"%#/yourDicretoryName", documentsDirectory]
withIntermediateDirectories:NO
attributes:nil
error:nil];
}
after creating specific directory. You can save your txt file. Assume you have one nsstring
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/txtFile.txt",documentsDirectory];
[yourString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
After saving your txt file you can read like this.
NSString *stringTxt = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
You can get the file list in directory using the code
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSError *error;
NSMutableArray *fileList = [[NSMutableArray alloc]initWithArray:[[NSFileManager defaultManager] contentsOfDirectoryAtPath:[NSString stringWithFormat:#"%#/yourDirectory", documentsDirectory] error:&error]];
-contentsOfDirectoryAtPath
returns NSArray Object. with the file names in directory.
For Cocoa Thouch Mac Application.
You can create directory using code:
NSFileManager *fileManager= [NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:directory isDirectory:&isDir])
if(![fileManager createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:NULL])
NSLog(#"Error: Create folder failed %#", directory);
You can select directory using code:
NSOpenPanel* panel = [NSOpenPanel openPanel];
[panel setCanSelectHiddenExtension:NO];
[panel setCanChooseDirectories:YES];
[panel setCanChooseFiles:NO];
[panel setTitle:#"Kaydetmek için Dosya Yolunu Seçin!"];
[panel beginWithCompletionHandler:^(NSInteger result){
if (result == NSFileHandlingPanelOKButton) {
directoryPath = [[[panel URLs] objectAtIndex:0]path];
directoryPath = [directoryPath stringByReplacingOccurrencesOfString:#"file://localhost" withString:#""];
}
}];
Writing file still same. But you can save your in documents desktop in selected or created directory etc.
Fetching file list is same.

How to replace a file or folder by using NSFileManager?

For using "copyItemAtPath" method of NSFileManager, if exists the same file or folder, occur error.
Is there the replace feature in NSFileManager?
(If exists the same file, delete and copy If not, just copy.)
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self generateTableContents];
}
- (void)generateTableContents {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *appsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath = [appsDirectory objectAtIndex:0];
[fileManager changeCurrentDirectoryPath:documentPath];
[fileManager createDirectoryAtPath:#"user_List1" withIntermediateDirectories:YES attributes:nil error:nil];
NSString *bundlePath = [[NSBundle mainBundle] resourcePath];
NSError *error; // to hold the error details if things go wrong
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:#"IMG_0523" ofType:#"JPG"];
if ([fileManager copyItemAtPath:sourcePath toPath: #"./user_List1/newIMG_0523.JPG" error:&error]) {
NSLog(#"Copy complete!");
} else {
NSLog(#"Copy Failed : %#", error);
}
if ([fileManager copyItemAtPath:#"./user_List1" toPath: #"./user_List2" error:&error]) {
NSLog(#"Copy complete!");
} else {
NSLog(#"Copy Failed : %#", error);
}
}
Excuted result is complete.
/Users/nice7285/Library/Caches/appCode10/DerivedData/tempPrg-dd15264d/Build/Products/Debug-iphonesimulator/tempPrg.app/tempPrg
Simulator session started with process 1184
2012-10-04 06:28:32.653 tempPrg[1184:11303] Copy complete!
2012-10-04 06:28:32.672 tempPrg[1184:11303] Copy complete!
Process finished with exit code 143
But When already exist folder.
Result is fail.
2012-10-04 06:48:31.488 tempPrg[1243:11303] Copy Failed
2012-10-04 06:48:31.497 tempPrg[1243:11303] Copy Failed
You need to do an atomic save, for example:
NSData *myData = ...; //fetched from somewhere
[myData writeToFile:targetPath atomically:YES];
But since you're using copyItemAtPath, I have no idea if atomically works with it so I would just go for the quick dirty hack:
if ([fileManager fileExistsAtPath:txtPath] == YES) {
[fileManager removeItemAtPath:txtPath error:&error]
}
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:#"txtFile" ofType:#"txt"];
[fileManager copyItemAtPath:resourcePath toPath:txtPath error:&error];
Sources: IOS: copy a file in documents folder
Starting with iOS 4 (and macOS 10.7) there is an explicit method for replacing one item with another. It even keeps the properties of the original file (optional)
- (BOOL)replaceItemAtURL:(NSURL *)originalItemURL withItemAtURL:(NSURL *)newItemURL backupItemName:(NSString *)backupItemName options:(NSFileManagerItemReplacementOptions)options resultingItemURL:(NSURL * _Nullable *)resultingURL error:(NSError * _Nullable *)error;
Replaces the contents of the item at the specified URL in a manner
that ensures no data loss occurs.
replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:

copyItemAtPath -path shows null for plist created in background thread

I have a function that checks whether the plist exists or not, if not it will create the plist.
this function is called in background i.e
[self performSelectorInBackground:#selector(CreatePlist:) withObject:xyz];
everything goes just perfect.
but when i try to fetch the data from the plist on main thread i.e
NSString *path = [[NSBundle mainBundle] pathForResource:#"calendar" ofType:#"plist"];
NSLog(#"path is %#", path); // path comes null
fileName = [fileName stringByReplacingOccurrencesOfString:#"/" withString:#""];
NSLog(#"file name data contain %#",fileName);
NSFileManager *fileManager = [NSFileManager defaultManager];
//see if Data.plist exists in the Documents directory
if (![fileManager fileExistsAtPath:[self saveMyPath:fileName] ]) {
[fileManager copyItemAtPath:path toPath:[self saveMyPath:#""] error:nil]; //at this point the app crashes saying path is null
}
when i moved my application from the background to the main thread it works well.
what is the problem here.?
looks like the background thread isn't finished when the main thread gets to it.
you could try using grand central dispatch.. e.g.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self CreatePlist:xyz];
dispatch_async(dispatch_get_main_queue(), ^{
NSString *path = [[NSBundle mainBundle] pathForResource:#"calendar" ofType:#"plist"];
NSLog(#"path is %#", path); // path comes null
fileName = [fileName stringByReplacingOccurrencesOfString:#"/" withString:#""];
NSLog(#"file name data contain %#",fileName);
NSFileManager *fileManager = [NSFileManager defaultManager];
//see if Data.plist exists in the Documents directory
if (![fileManager fileExistsAtPath:[self saveMyPath:fileName] ]) {
[fileManager copyItemAtPath:path toPath:[self saveMyPath:#""] error:nil]; //at this point the app crashes saying path is null
});
});
If the function is called in the background, you should create your own NSAutorealsePool. For example:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// your code
[pool drain];

OSX Application, get file data that user selected using NSOpenPanel

I am new to developing OSX Applications. I normally do iOS Apps, so alot of the concepts carry over. However, I cannot quite seem to figure out why I cannot retrieve the data of a file on my system.
Is there something that needs to be done first in order to read files on the users system?
Here is what I have:
- (IBAction)btnBrowse:(id)sender {
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel beginWithCompletionHandler:^(NSInteger result){
if (result == NSFileHandlingPanelOKButton) {
//Get the file url user selected
NSURL *file = [[panel URLs] objectAtIndex:0];
//Get the file data
NSError *error;
NSString *fileData = [NSString stringWithContentsOfFile:[NSString stringWithFormat:#"%#", file] encoding:NSUTF8StringEncoding error:&error];
//This returns null
NSLog(#"%#", fileData);
//This says that the file does not exist
NSLog(#"Error: %#", error.localizedDescription);
}
}];
}
NSOpenPanel returns NSURL objects, the easiest solution is to use the NSURL related API
NSString *fileData = [NSString stringWithContentsOfURL:file encoding:NSUTF8StringEncoding error:&error];
If you don't know the text encoding you could use this API, if the reading succeeds, encoding contains the used encoding.
NSStringEncoding encoding;
NSString *fileData = [NSString stringWithContentsOfURL:file usedEncoding:&encoding error:&error];
Use the -[NSURL path] method instead of calling 'file' directly, as it's a NSURL, not NSString.