NSURLSessionDownloadTask - downloads but ends with error - objective-c

I am trying to download a pdf file. earlier when i used the completion handler block i was able to see the file in the tmp location. I then wanted to show download progress so i implemented the delegate methods. But i can now see the progress bar working and the file being downloaded. but once the download is complete (bytes written/total bytes = 1) the error delegate is called and there is no file in the tmp location. what am i missing ? below is my code. I have uploaded the project at https://www.dropbox.com/s/vn5zwfwx9izq60a/trydownload.zip
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:[NSURL URLWithString:#"http://aayudham.com/URLLoadingSystem.pdf"]];
[downloadTask resume];
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(#"%#",[error localizedDescription]);
}
-(void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
dispatch_async(dispatch_get_main_queue(), ^{
_progressBar.progress = (double)totalBytesWritten/(double)totalBytesExpectedToWrite;
double value =(double)totalBytesWritten/(double)totalBytesExpectedToWrite;
NSLog(#"%f",value);
});
}
-(void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
}
-(void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSError *error;
//getting docs dir path
NSArray * tempArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [tempArray objectAtIndex:0];
//adding folder path
NSString *appDir = [docsDir stringByAppendingPathComponent:#"/Reader/"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:appDir])
{
[fileManager createDirectoryAtPath:appDir withIntermediateDirectories:NO attributes:nil error:&error];
}
BOOL fileCopied = [fileManager moveItemAtPath:[location path] toPath:[appDir stringByAppendingString:#"/demo.pdf"] error:&error];
NSLog(fileCopied ? #"Yes" : #"No");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end

#Rob thank you for your prompt replies and that helped me a lot. Here is my code that worked. Hope it helps someone. I am able to get the actual file name and move the file to my documents directory using the original name.
-(void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
//getting application's document directory path
NSArray * tempArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [tempArray objectAtIndex:0];
//adding a new folder to the documents directory path
NSString *appDir = [docsDir stringByAppendingPathComponent:#"/Reader/"];
//Checking for directory existence and creating if not already exists
if(![fileManager fileExistsAtPath:appDir])
{
[fileManager createDirectoryAtPath:appDir withIntermediateDirectories:NO attributes:nil error:&error];
}
//retrieving the filename from the response and appending it again to the path
//this path "appDir" will be used as the target path
appDir = [appDir stringByAppendingFormat:#"/%#",[[downloadTask response] suggestedFilename]];
//checking for file existence and deleting if already present.
if([fileManager fileExistsAtPath:appDir])
{
NSLog([fileManager removeItemAtPath:appDir error:&error]?#"deleted":#"not deleted");
}
//moving the file from temp location to app's own directory
BOOL fileCopied = [fileManager moveItemAtPath:[location path] toPath:appDir error:&error];
NSLog(fileCopied ? #"Yes" : #"No");
}

In didFinishDownloadingToURL you should move the file from location to some place more permanent (e.g. your Documents folder). If you're looking for that file in the temporary location later, I'm not surprised it's no longer there.
As the documentation says, the location is defined as such:
A file URL for the temporary file. Because the file is temporary, you must either open the file for reading or move it to a permanent location in your app’s sandbox container directory before returning from this delegate method.
You must move the file to its new location before returning from didFinishDownloadingToURL.

Just in case someone had the same issue that I did I thought I would post my solution here.
My problem was that the predicate methods are firing on a background thread so I was dispatching to my "file io" thread which handles any Writing to files, deleting etc within the app.
The problem with this is that the temporary file gets deleted as soon as the delegate method ends which was occurring at the exact moment I switched threads. So when I tried to access the file in my file io thread it had already been deleted.
My solution was to parse the file into NSData in the delegate method, then use the NSData to write to the filesystem in my file io thread.

Related

Unable to delete the contents of a Directory in my Home Directory using NSDirectoryEnumerator

I have been unable to delete the contents of a directory using NSDirectoryEnumerator.
Here is my method:
- (BOOL) deleteDirectoryContents:(NSString *)directoryPath
{
BOOL success = FALSE;
BOOL isDir;
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:directoryPath isDirectory:&isDir] && isDir)
{
NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:directoryPath];
NSLog(#"dirEnum in deleteDirectoryContents is %#", dirEnum);
NSString *documentsName;
// NSLog(#"[dirEnum nextObject] is: %#", [dirEnum nextObject]);
while (documentsName = [dirEnum nextObject])
{
NSString *filePath = [directoryPath stringByAppendingString:documentsName];
NSLog(#"filePath is: %#", filePath);
BOOL isFileDeleted = [fileManager removeItemAtPath:filePath error:nil];
if(isFileDeleted == NO)
{
NSLog(#"All Contents not removed");
success = FALSE;
break;
}
success = TRUE;
}
if (success) NSLog(#"All Contents Removed");
}
return success;
}
And this is my code in my main program:
NSString *testDir = #"/Users/grinch/MyTestTemp";
//testDir = [NSHomeDirectory() stringByAppendingPathComponent: #"MyTestTemp"];
NSLog(#"testDir is: %#", testDir);
BOOL result = [self deleteDirectoryContents:testDir];
NSLog(#"result is: %d", result);
Here is my console output:
testDir is: /Users/grinch/MyTestTemp
dirEnum in deleteDirectoryContents is <NSAllDescendantPathsEnumerator: 0x60000008c300>
result is: 0
I also checked the value of [dirEnum nextObject] (by uncommenting the NSLog statement in my code). It returns null. And I never see the "filePath is" NSLog statement. So the inner while loop is NEVER executed.
And yes the directory (with files) does exist in my home directory. I created these files. I have permissions. I can easily delete the files in this directory using Finder
What am I missing?
P.S. I did some more testing. It looks like my simple program in Xcode does not have permissions to access files and folders in my home directory. Why? I have no idea.
Here is my additional test code:
NSError *errorMsg;
[[NSFileManager defaultManager] removeItemAtPath:#"/Users/grinch/myTestTemp/Hello.txt" error:&errorMsg];
if (errorMsg) NSLog(#"ERROR - File delete errorMsg is: %#", errorMsg);
success = [[NSFileManager defaultManager] removeItemAtPath:testDir error:&errorMsg];
if (errorMsg) NSLog(#"ERROR - Folder delete errorMsg is: %#", errorMsg);}
And here is my console output:
2022-09-10 09:56:48.958352-0400 NSAlert[97106:7511815] ERROR - File delete errorMsg is: Error Domain=NSCocoaErrorDomain Code=513 "“Hello.txt” couldn’t be removed because you don’t have permission to access it." UserInfo={NSFilePath=/Users/grinch/myTestTemp/Hello.txt, NSUserStringVariant=(
), NSUnderlyingError=0x6040006421f0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
2022-09-10 09:56:48.960560-0400 NSAlert[97106:7511815] ERROR - Folder delete errorMsg is: Error Domain=NSCocoaErrorDomain Code=513 "“MyTestTemp” couldn’t be removed because you don’t have permission to access it." UserInfo={NSFilePath=/Users/grinch/MyTestTemp, NSUserStringVariant=(
), NSUnderlyingError=0x60400045ff80 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
So my questions are:
Why didn't the directory enumerator work?
Why doesn't Xcode have the permissions to delete items in my home folder?
How can I give Xcode the the ability (or permissions) to delete items in my home directory?
I have found a bug in my deleteDirectoryContents: method above. And as a result, have answered most of my questions!
First, the statement NSString *filePath = [directoryPath stringByAppendingString:documentsName]; in the while loop will not work. One must insert a / before documentsname. So the line should be NSString *filePath = [directoryPath stringByAppendingFormat:#"/%#",documentsName]; OR even better use NSString *filePath = [directoryPath stringByAppendingPathComponent:documentsName];.
Now the method deleteDirectoryContents: will work as expected.
Here is the code to the fixed method:
- (BOOL) deleteDirectoryContents:(NSString *)directoryPath
{
BOOL success = FALSE;
BOOL isDir;
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:directoryPath isDirectory:&isDir] && isDir)
{
NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:directoryPath];
NSLog(#"dirEnum in deleteDirectoryContents is %#", dirEnum);
NSString *documentsName;
// NSLog(#"[dirEnum nextObject] is: %#", [dirEnum nextObject]);
while (documentsName = [dirEnum nextObject])
{
//NSString *filePath = [directoryPath stringByAppendingFormat:#"/%#",documentsName];
NSString *filePath = [directoryPath stringByAppendingPathComponent:documentsName];
NSLog(#"filePath is: %#", filePath);
NSError *errorMsg = nil;
BOOL isFileDeleted = [fileManager removeItemAtPath:filePath error:&errorMsg];
if (errorMsg) NSLog(#"ERROR - Failed to delete file or folder at: %#. Error message is: %#", filePath, errorMsg);
if(isFileDeleted == NO)
{
NSLog(#"All Contents not removed");
success = FALSE;
break;
}
success = TRUE;
}
if (success) NSLog(#"All Contents Removed");
}
return success;
}
And I can confirm that if your program creates the directory and the files within it, the program can also delete these files using deleteDirectoryContents: while running in the Xcode sandbox DEPENDING on where the folder was originally created.
If the program creates the folder and files in a temporary directory (e.g. caches directory) NOT the users home folder, it works.
But due to Xcode sandboxing,
deleteDirectoryContents: does not appear to work on folder and files created by the program in the users Home directory when running within Xcode.
P.S. I used the following to obtain a temporary directory name:
- (NSString *) get_temporary_dir
{
NSString *path = nil;
NSString *bundleName = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleIdentifier"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
if ([paths count]) {
path = [[paths objectAtIndex:0] stringByAppendingPathComponent:bundleName];
} else {
path = NSTemporaryDirectory();
path = [path stringByAppendingPathComponent:bundleName];
}
return [[[NSString alloc] initWithUTF8String:[path UTF8String]] autorelease];
}
Then the program created the temporary directory using that name. The program created some files in that temporary directory and then was able to delete them using the deleteDirectoryContents: method above.

iCloud Core Data IOS8 Path is outside of any CloudDocs Container

My iCloud Core Data app was running great on iOS7 and ready to launch. When I test on iOS 8 I get the following error and can't seem to fix it when trying to upload data to iCloud.
I suspect my problem is related to how I am getting the document directory and changes in the doc directory with iOS8 but I just can't figure this out..
014-10-12 15:14:17.862 XXXXXXX [4662:236693] __45-[PFUbiquityFilePresenter processPendingURLs]_block_invoke(439): CoreData: Ubiquity: Librarian returned a serious error for starting downloads Error Domain=BRCloudDocsErrorDomain Code=6 "The operation couldn’t be completed. (BRCloudDocsErrorDomain error 6 - Path is outside of any CloudDocs container, will never sync)" UserInfo=0x7f8b1a525f60 {NSDescription=Path is outside of any CloudDocs container, will never sync, NSFilePath=/Users/garyrea/Library/Developer/CoreSimulator/Devices/9AADFE8E-5ECC-4969-9418-57DA45B747C9/data/Containers/Data/Application/AD2E5E62-7295-4371-A08D-1790E8FCCD96/Documents/CoreDataUbiquitySupport/nobody~simA28745A4-A67F-598C-9260-F9AC36609ECF/iCloud/5B8BFA36-1ACA-4966-B7ED-A7344D36ACF1/container/nobody~simA28745A4-A67F-598C-9260-F9AC36609ECF/iCloud/2trlqdMQVpJ~wlEfiLvjWtQfrUJ8YiNCd84KW_xiw4A=/F0CF5F29-D437-4728-B0A2-C5BB90BBC239.1.cdt} with userInfo {
NSDescription = "Path is outside of any CloudDocs container, will never sync";
NSFilePath = "/Users/garyrea/Library/Developer/CoreSimulator/Devices/9AADFE8E-5ECC-4969-9418-57DA45B747C9/data/Containers/Data/Application/AD2E5E62-7295-4371-A08D-1790E8FCCD96/Documents/CoreDataUbiquitySupport/nobody~simA28745A4-A67F-598C-9260-F9AC36609ECF/iCloud/5B8BFA36-1ACA-4966-B7ED-A7344D36ACF1/container/nobody~simA28745A4-A67F-598C-9260-F9AC36609ECF/iCloud/2trlqdMQVpJ~wlEfiLvjWtQfrUJ8YiNCd84KW_xiw4A=/F0CF5F29-D437-4728-B0A2-C5BB90BBC239.1.cdt";
} for these urls: (
"file:///Users/garyrea/Library/Developer/CoreSimulator/Devices/9AADFE8E-5ECC-4969-9418-57DA45B747C9/data/Containers/Data/Application/AD2E5E62-7295-4371-A08D-1790E8FCCD96/Documents/CoreDataUbiquitySupport/nobody~simA28745A4-A67F-598C-9260-F9AC36609ECF/iCloud/5B8BFA36-1ACA-4966-B7ED-A7344D36ACF1/container/nobody~simA28745A4-A67F-598C-9260-F9AC36609ECF/iCloud/2trlqdMQVpJ~wlEfiLvjWtQfrUJ8YiNCd84KW_xiw4A=/F0CF5F29-D437-4728-B0A2-C5BB90BBC239.1.cdt"
)
my app delegate extension code where I create my persistent store is as follows. I have a seed database for first time installation.
- (NSPersistentStoreCoordinator *)createPersistentStoreCoordinator{
NSPersistentStoreCoordinator *persistentStoreCoordinator = nil;
NSManagedObjectModel *managedObjectModel = [self createManagedObjectModel];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:managedObjectModel];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#
"CoreData.sqlite"];
if (![[NSFileManager defaultManager]fileExistsAtPath:[storeURL path]]){
NSURL *preloadURL=[NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:#"SeedDatabase" ofType:#
"sqlite"]];
NSError *error=nil;
if (![[NSFileManager defaultManager] copyItemAtURL:preloadURL toURL:storeURL error:&error]){
NSLog(#
"File couldnt save");
}
}
NSUbiquitousKeyValueStore *kvStore=[NSUbiquitousKeyValueStore defaultStore];
if (![kvStore boolForKey:#"SEEDED_DATA"]){
NSLog (#
"In the new database");
NSURL *seedStoreURL=[NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:#"SeedDatabase" ofType:#
"sqlite"]];
NSError *seedStoreErrpr;
NSDictionary *seedStoreOptions=#{NSReadOnlyPersistentStoreOption: #YES};
NSPersistentStore *seedStore=[persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:seedStoreURL options:seedStoreOptions error:&seedStoreErrpr];
NSDictionary *iCloudOptions =#{NSPersistentStoreUbiquitousContentNameKey: #"iCloud",
NSMigratePersistentStoresAutomaticallyOption:#YES,
NSInferMappingModelAutomaticallyOption:#YES
};
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSError *error;
[persistentStoreCoordinator migratePersistentStore:seedStore toURL:storeURL options:iCloudOptions withType:NSSQLiteStoreType error:&error];
NSLog(#
"Persistant store migrated");
[kvStore setBool:YES forKey:#
"SEEDED_DATA"];
// [self checkForDuplicates];
}];
}else{
NSError *error;
NSDictionary *storeOptions =#{NSPersistentStoreUbiquitousContentNameKey: #
"iCloud"
};
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:storeOptions
error:&error]) {
NSLog(#
"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
return persistentStoreCoordinator;
}
- (NSURL *)applicationDocumentsDirectory{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
I was able to resolve this error by specifying the iCloud drive directory (Same name as the one on the developer.apple.com interface).
-(NSURL *)cloudDirectory
{
NSFileManager *fileManager=[NSFileManager defaultManager];
NSString *teamID=#"iCloud";
NSString *bundleID=[[NSBundle mainBundle]bundleIdentifier];
NSString *cloudRoot=[NSString stringWithFormat:#"%#.%#",teamID,bundleID];
NSURL *cloudRootURL=[fileManager URLForUbiquityContainerIdentifier:cloudRoot];
NSLog (#"cloudRootURL=%#",cloudRootURL);
return cloudRootURL;
}
and including it in the icloudOptions Dictionary as a NSPersistentStoreUbiquitousContentURLKey
NSDictionary *storeOptions =#{NSPersistentStoreUbiquitousContentNameKey: #"iCloud",
NSPersistentStoreUbiquitousContentURLKey:[self cloudDirectory],
};
I was getting some strange errors so I removed the app from all devices, deleted the iCloud drive file and re ran on an actual device and it worked fine. Not sure if it runs on IOS7 now but since I only specified the NSPersistentStoreUbiquitousContentURLKey I am pretty confident it should be fine.
I had the same issue during loading some test data.
For the load of the data I was deleting all records.
To avoid the exception a simple sleep(1) between cleaning and loading was enough.

How can I keep track of multiple downloads with NSURLSession?

How can I keep track of multiple downloads with NSURLSession?
For example:
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
NSURLSessionDownloadTask *jsonTask = [session downloadTaskWithURL:[NSURL URLWithString:[urlString stringByAppendingString:#"iOSMenu.json"]]];
NSURLSessionDownloadTask *imageTask = [session downloadTaskWithURL:[NSURL URLWithString:[urlString stringByAppendingString:#"menu1#2x.png"]]];
NSURLSessionDownloadTask *titleTask = [session downloadTaskWithURL:[NSURL URLWithString:[urlString stringByAppendingString:#"titleBar#2x.png"]]];
[jsonTask resume];
[imageTask resume];
[titleTask resume];
I downloaded these three files and I can confirm it with NSLog, like this.
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
NSLog(#"%#", location);
}
But I don't know how can I access and differentiate these files. How can I do that?
The didFinishDownloadingToURL method passes you a reference to the NSURLSessionDownloadTask in that delegate method. From that, you can either refer to the task's taskIdentifier or refer to the task's originalRequest.URL property.
You want to make sure you move the file at location to somewhere you'll have access to in the future (as when you return from didFinishDownloadingToURL, it will remove that temporary file if you don't do something with it).
For example, you might save the file to your documents folder. In this example, I'll grab the last path component of the original URL, create a path to a file in your documents folder, and move the file to new location:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *path = [documentsPath stringByAppendingPathComponent:[downloadTask.originalRequest.URL lastPathComponent]];
NSURL *newLocation = [NSURL fileURLWithPath:path];
NSError *error;
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:newLocation error:&error]) {
NSLog(#"failed to move %# to %#: %#", location, newLocation, error);
}
}
Alternatively, if you want to not rely upon the lastPathComponent of the original URL, you can maintain a dictionary mapping the task identifiers (or original URLs) to your new location you want to save the file. You can build that dictionary as you create the tasks, and then the didFinishDownloadingToURL could look up the desired destination using the task's identifier in that dictionary, and use that in the moveItemAtURL method. Clearly, if this is a background session, you'll want to make sure you save this cross reference to persistent storage so you can re-retrieve it when the app is restarted when the downloads finish.

Writing to /tmp folder iPad

I´m writing certain values to a file. See Write Operations below.
This works fine when using iPad 6.1 Simulator.
When trying the same thing on my iPad it fails. I think it´s something with sandboxing. I haven´t found out yet which path is best on iOS Devices to write stuff for internal use.
Any ideas?
#pragma mark Write Operations to Tmp Folder
- (BOOL) psWriteFileWithName: (NSString*) fileName
withString:(NSString*) string {
NSString *fileName = #"artistNumber";
NSError * error = NULL;
NSString *filePath = [NSString stringWithFormat:#"/tmp/%#.txt",fileName];
[string writeToFile:filePath
atomically:YES
encoding: NSUTF8StringEncoding
error:&error];
return YES;
}
You cannot write to /tmp since this is outside of your app sandbox.
However your app also has a temp directory, which can be referenced with the NSTemporaryDirectory() function:
Which works like:
NSString *tempfilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
Here is you method with the correct NSTemporaryDirectory() implementation, also edit some error handling:
#pragma mark Write Operations to Tmp Folder
- (BOOL) psWriteFileWithName: (NSString*) fileName
withString:(NSString*) string {
NSString *fileName = #"artistNumber";
NSError *error = nil;
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
if (![string writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error] ) {
NSLog(#"Error writing file: %#", error);
return NO;
}
return YES;
}

Am I crazy? Help with NSFileManager delegate method shouldProceedAfterError in 10.5+

So I'm kind of a Cocoa n00b, but I'm writing this simple little program and I can't get the NSFileManager delegate method "shouldProceedAfterError..." to fire. Here's the code I'm running in my AppDelegate
-(BOOL)copyFile {
[[NSFileManager defaultManager] setDelegate:self];
NSError *copyError = nil;
NSString *filename = [[NSString alloc] initWithString:[[[self.sourceFile path] componentsSeparatedByString:#"/"] lastObject]];
NSString *destination = [[[[[UserData sharedData] folderLocation] path] stringByAppendingString:#"/"] stringByAppendingString:filename];
[[NSFileManager defaultManager] copyItemAtPath:[self.sourceFile path] toPath:destination error:&copyError];
NSLog(#"error! %#",copyError);
[filename release];
return YES;
}
- (BOOL)fileManager:(NSFileManager *)fileManager shouldProceedAfterError:(NSError *)error copyingItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath {
NSLog(#"more error... %#",error);
return NO;
}
- (BOOL)fileManager:(NSFileManager *)fileManager shouldCopyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath {
NSLog(#"in shouldCopyItemAtPath...");
return YES;
}
The situation I'm trying to deal with is if the file already exists at the destination. I do get an error, but I never get that "more error..." trace to output. I also DO get that trace from shouldCopyItemAtPath: so I'm not exactly sure why the method isn't firing?
Am I going crazy, how did I mess up the delegate implementation here? Thanks for any help!
This is just a hypothesis, but since copyItemAtPath:toPath:error is defined such that "The file specified in srcPath must exist, while dstPath must not exist prior to the operation.", perhaps the scenario where dstPath already exists isn't considered an "error," and so doesn't fire the delegate.
i.e. perhaps "It's not an error if you do something we told you not to do."
You can always do the check and delete it yourself:
NSFileManager* fileManager = [NSFileManager defaultManager];
// Delete the file if it already exists.
if ([fileManager fileExistsAtPath: destination])
if (![fileManager removeItemAtPath: destination error: error])
return NO;
return [fileManager copyItemAtPath: source toPath: destination error: error];
Probably you are providing a wrong path as source?
copyItemAtPath does not call the delegate methods if the the source path is invalid.
You can test that if you use the following method:
-(IBAction)copyFile:(id)sender
{
[[NSFileManager defaultManager] setDelegate:self];
NSError* copyError = nil;
NSString* sourceFilepath = [#"~/Desktop/source.txt" stringByExpandingTildeInPath];
NSString* targetFilepath = [#"~/Desktop/target.txt" stringByExpandingTildeInPath];
[[NSFileManager defaultManager] copyItemAtPath:sourceFilepath toPath:targetFilepath error:&copyError];
NSLog(#"Error:%#", copyError);
}
When calling that method I notice the following behavior:
If ~/Desktop/source.txt is a file and ~/Desktop/target.txt does not exist:
NSFileManager calls the shouldCopyItemAtPath delegate method
If ~/Desktop/source.txt is a file and ~/Desktop/target.txt exists:
NSFileManager first calls the shouldCopyItemAtPath and shouldProceedAfterError
If ~/Desktop/source.txt does not exist
NSFileManager does not call any delegate method and just returns a NSError objet