I'm trying to copy a folder and it's contents to a sub-directory in the documentation directory and it's failing with the error:
"The operation couldn’t be completed. No such file or directory"
First I try to create a folder in the documentation directory like this:
NSString *diagramsDirectory = [docDirectory stringByAppendingPathComponent:#"Diagrams"];
if (![fileManager fileExistsAtPath:docDirectory isDirectory:&isDirectory] || !isDirectory)
{
NSError *error = nil;
NSDictionary *attr = [NSDictionary dictionaryWithObject:NSFileProtectionComplete
forKey:NSFileProtectionKey];
[fileManager createDirectoryAtPath:diagramsDirectory
withIntermediateDirectories:NO
attributes:attr
error:&error];
if (error) {
NSLog(#"error creating dir. path: %#", [error localizedDescription]);
}
}
NSLog(#"diagrams directory = %#", diagramsDirectory);
The console log seems to indicate this works:
diagrams directory = /Users/../iPhone Simulator/../Library/Documentation/Diagrams
However, when I then try to copy a folder called "Diagrams" from a directory on the Mac:
NSString *pathToDirectories = #"/User/Desktop/Project Resource Files/Files/";
NSError *error = nil;
NSArray *folders = [fileManager contentsOfDirectoryAtPath:pathToDirectories error:&error];
for (NSString *folder in folders) {
if ([folder isEqualToString:#"Diagrams"]) {
[self copyFolderAtPath:folder toDestinationFolderAtPath:docDirectory];
}
which calls the "copyFolderAtPath" method:
- (BOOL)copyFolderAtPath:(NSString *)sourceFolder toDestinationFolderAtPath:(NSString *)destinationFolder
{
destinationFolder = [destinationFolder stringByAppendingPathComponent:[sourceFolder lastPathComponent]];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
// check for destination folder
if ([fileManager fileExistsAtPath:destinationFolder])
{
if (![fileManager removeItemAtPath:destinationFolder error:&error])
{
NSLog(#"Could not remove old files. Error: %#", error);
return NO;
}
}
error = nil;
// copy destination
if (!([fileManager copyItemAtPath:sourceFolder toPath:destinationFolder error:&error])) {
NSLog(#"failed copying file at path %# to path %#. Error %#", sourceFolder, destinationFolder, error);
return NO;
}
return YES;
}
it returns "no" and I get the error.
Anyone got an idea what I'm doing wrong?
The device (and therefore the simulator) is isolated from the operating system so you cannot directly do file system copies. Imagine even if it let you do it from the simulator, how would a disconnected device running your app access the OS filesystem?
You will have to look into other options like having an application on the mac that opens sockets or having an http end point on the mac that the device copies from. Other options include syncing documents via iCloud or another cloud service. You can also transfer files via iTunes. I'm sure there's many other options ... Also checkout this
Related
I have a following code, which copies folder from the app bundle to the Documents folder.
- (void)moveTessdataToDocumentsDirectoryIfNecessary
{
// Useful paths
NSFileManager *fileManager = [NSFileManager defaultManager];
// Create specified folder in the Documents directory first
if ([fileManager fileExistsAtPath:self.dataPath] == NO) {
[fileManager createDirectoryAtPath:self.dataPath withIntermediateDirectories:YES attributes:nil error:nil];
}
NSString *tessdataFolderName = #"tessdata";
NSString *tessdataPath = [[NSBundle bundleForClass:self.class].resourcePath stringByAppendingPathComponent:tessdataFolderName];
NSString *destinationPath = [self.dataPath stringByAppendingPathComponent:tessdataFolderName];
if([fileManager fileExistsAtPath:destinationPath] == NO && [fileManager fileExistsAtPath:tessdataPath]) {
NSError *error = nil;
NSLog(#"found %#", tessdataPath);
NSLog(#"coping in %#", destinationPath);
BOOL res = [fileManager copyItemAtPath:tessdataPath toPath:destinationPath error:&error];
if (!res) {
NSLog(#"The result of copyItemAtPath == NO");
}
if(error != nil) {
NSLog(#"ERROR! %#", error.description);
}
}
}
It works fine in a sumilator and in a device.
The issue happens when I start unit tests for this function.
The output while unit testing is:
found /Users/kmakankov/Library/Developer/CoreSimulator/Devices/D28F7234-8C28-4325-BE7D-4DF113DF427E/data/Containers/Bundle/Application/EBD5C654-897B-4066-885B-986553D3459F/TestsProject.app/tessdata
coping in /Users/kmakankov/Library/Developer/CoreSimulator/Devices/D28F7234-8C28-4325-BE7D-4DF113DF427E/data/Containers/Data/Application/50D673A6-3F3E-42A1-98A1-DEAF9A4B2FBE/Documents/tes/tessdata
The result of copyItemAtPath == NO
So as you can see the copyItemAtPath return NO, but there is no error message, so I cannot understand what is the reason of the issue.
Furthemore, If I change copyItemAtPath with moveItemAtPath the function correctly moves the folder from the source to the destination during unit testing, so both source and destination paths are correct.
Does anybody knows what's wrong with copyItemAtPath?
Some extra info. I use iOS SDK 8.1.
I have a newsstand app which has magazines and uses the newsstand framework. I realized there was something wrong when deleting the magazines and/or when downloading them because when I accessed settings/usage my app keeps growing in memory usage when downloading and deleting the same magazine.
Found the issue... when downloading the issue in the delegate method:
-(void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *)destinationURL
I just needed to add something like this at the end:
NSError *error;
[[NSFileManager defaultManager] removeItemAtPath:[destinationURL path] error:&error];
if (error){
NSLog(#"ERROR:%#", error);
}
Even the directory is called "caches" you need to manually delete. Ok problem solved but what about the customers who already download my app and have tons of MBs dead in the cache directory.
I wanted to know how to get this directory and delete everything on it at launch and only once...
I can do it only once using a NSUserdefault but how do I get this directory and delete any zip files in it... an example of this directory and a file within is:
/private/var/mobile/Applications/1291CC20-C55F-48F6-86B6-B0909F887C58/Library/Caches/bgdl-280-6e4e063c922d1f58.zip
but this path varies with the device. I want to do this at launch so I'm sure there are no downloads in progress but any other solutions are welcome, thanks in advance.
Everything that you need is enumerate all files from Caches directory and remove ones that have zip extension:
- (void)removeZipFilesFromCachesDirectory {
static NSString *const kZIPExtension = #"zip";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *cachesDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSError *error = nil;
NSArray *fileNames = [fileManager contentsOfDirectoryAtPath:cachesDirectoryPath error:&error];
if (error == nil) {
for (NSString *fileName in fileNames) {
NSString *filePath = [cachesDirectoryPath stringByAppendingPathComponent:fileName];
if ([filePath.pathExtension.lowercaseString isEqualToString:kZIPExtension]) {
NSError *anError = nil;
[fileManager removeItemAtPath:filePath error:&anError];
if (anError != nil) {
NSLog(#"%#", anError);
}
}
}
} else {
NSLog(#"%#", error);
}
}
I have the same issue as this question where the iCloud ubiquity container is not being cleaned up when I delete the app.
But when I try to delete the ubiquity container I get an error message (The operation couldn’t be completed. (Cocoa error 513.)). How can I delete it?
This is what I'm using:
NSString *path = #"/private/var/mobile/Library/Mobile Documents/XXXXXX";
[[NSFileManager defaultManager] removeItemAtPath:path error:&error];
EDIT: Even if I delete the App on the device and all iCloud data (Settings App->iCloud->Storage & Backup->Manage Storage->App Name) there's still some data left over on the iCloud ubiquity container. This is the data I want to delete the first time the app is launched (in case the user re-installs the app).
You should be able to remove files INSIDE the ubiquity container by going to Settings App->iCloud->Storage & Backup->Manage Storage->App Name and then delete any files. I think you may only see files in the iCloud/Documents directory though so you may need code to clear anything else.
Alternately use a Mac and go to ~/Library/Mobile Documents and remove files there.
To get the iCloud container use this:
NSURL *iCloudURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:ubiquityID];
where ubiquityID is your apps iCloud container ID.
To list all files in the iCloud container use something like this passing in the iCloudURL
/*! Recursively lists all files
#param dir The directory to list
#param padding A string padding to indent the output depending on the level of recursion
*/
- (void)listAllFilesInDirectory:(NSURL*)dir padding:(NSString*)padding {
NSArray *docs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:dir includingPropertiesForKeys:nil options:0 error:nil];
for (NSURL* document in docs) {
FLOG(#" %# %#", padding, [document lastPathComponent]);
BOOL isDir;
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:document.path isDirectory:&isDir];
if (fileExists && isDir) {
[self listAllFilesInDirectory:document padding:[NSString stringWithFormat:#" %#", padding]];
}
}
}
And to delete stuff from the ubiquity container you need to user a fileCoordinator something like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSFileCoordinator* fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
[fileCoordinator coordinateWritingItemAtURL:fileURL options:NSFileCoordinatorWritingForDeleting
error:nil byAccessor:^(NSURL* writingURL) {
NSFileManager* fileManager = [[NSFileManager alloc] init];
NSError *er;
//FLOG(#" deleting %#", writingURL);
bool res = [fileManager removeItemAtURL:writingURL error:&er];
if (res) {
LOG(#" iCloud files removed");
}
else {
LOG(#" document NOT removed");
FLOG(#" error %#, %#", er, er.userInfo);
}
}];
}
I'm trying to sync files created in my app to Dropbox, however it seems the syncing only happens after the app quits, and not in real time when files are created and moved between locations in different folders in the app or created/deleted. Is there a certain call I have to make for instance? Appreciate your help!
Below is the code I am using for syncing:
-(void)createFilePathinFolder:(NSString *)folderName FileName:(NSString *)fileName {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *folder = [self localDocumentsRootPath];
if (![folderName isEqualToString:#"root"]) {
folder = [folder stringByAppendingPathComponent:folderName];
}
NSString *file = [folder stringByAppendingPathComponent:fileName];
if (![fileManager fileExistsAtPath:file]) {
[fileManager createFileAtPath:file contents:[#"0" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
}
//Insert to FileTable
[[DBHelper shared]insertToFileTableWithFolder:folderName FileName:fileName MetaFileName:nil Tag:nil Title:nil];
if ([NetworkHelper shared].canSyncWithCloud) {
NSString *filePathStr = [folderName stringByAppendingPathComponent:fileName];;
if ([folderName isEqualToString:#"root"]) {
filePathStr = fileName;
}
DBPath *filePath = [[DBPath root] childPath:filePathStr];
DBError *error;
DBFile *destFile =[[DBFilesystem sharedFilesystem] createFile:filePath error:&error];
NSData *fileData = [NSData dataWithContentsOfFile:file];
[destFile writeData:fileData error:&error];
//[destFile writeContentsOfFile:file shouldSteal:NO error:&error];
[destFile close];
if (error) {
NSLog(#"Error when creating file %# in Dropbox, error description:%#", fileName, error.description);
}
}
}
Your error checking is all wrong. Your code should be more like this:
DBPath *filePath = [[DBPath root] childPath:filePathStr];
DBError *error = nil;
DBFile *destFile =[[DBFilesystem sharedFilesystem] createFile:filePath error:&error];
if (destFile) {
NSData *fileData = [NSData dataWithContentsOfFile:file];
if (![destFile writeData:fileData error:&error]) {
NSLog(#"Error when writing file %# in Dropbox, error description: %#", fileName, error);
}
[destFile close];
} else {
NSLog(#"Error when creating file %# in Dropbox, error description: %#", fileName, error);
}
The file should sync right away with the code that you have. This assumes you have properly linked your app to an account and all.
What version of the Dropbox Sync API are you using? 1.0.7 has some potential networking issues. I have a beta of 1.0.8 that seems to solve these issues. You may need to wait until 1.0.8 comes out.
You can verify if Dropbox is hung. While running your app in the debugger, wait a minute after the file has been created. If the file doesn't appear, pause your app in the debugger and look at all of the threads. You should see one or more dropbox related threads. If one looks blocked with a reference to dbx_cfhttp_request then you have hit a bug in the Dropbox framework. Putting your device in Airplane mode for 10-15 seconds then turning Airplane mode off again should kick it back into gear.
NSFileManager* fileManager = [NSFileManager defaultManager];
NSURL* url = [[fileManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject];
NSString* directory = [url path];
NSString* filePath = [directory stringByAppendingPathComponent:FILE_NAME];
if ([fileManager fileExistsAtPath:filePath])
{
[fileManager removeItemAtPath:filePath error:nil];
}
Here's my code. When it is executed, the file is deleted, but the space remains occupied. Here's the code for storing something into the file.
NSFileManager* fileManager = [NSFileManager defaultManager];
NSURL* url = [[fileManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject];
NSString* directory = [url path];
NSString* filePath = [directory stringByAppendingPathComponent:FILE_NAME];
NSArray* oldArray = nil;
if ([fileManager fileExistsAtPath:filePath])
{
oldArray = [[NSArray alloc] initWithContentsOfFile:filePath];
[fileManager removeItemAtPath:filePath error:nil];
}
NSMutableArray* mergeArray = [[NSMutableArray alloc] initWithArray:arrayOfPersons];
[mergeArray addObjectsFromArray:oldArray];
if ( [mergeArray writeToFile:filePath atomically:YES]) NSLog(#"Written");
By the way, it cost 1 MB to store an array with only 1 object(an NSDictionary with 2 keys). Is there a cheaper way to store it?
You need to be much more careful with your experiments. The unix file system does lots of stuff with files. In fact, when you "delete" a file, all you do is unlink it from the file system. If that file is open with another file descriptor, anywhere in the OS, it will remain open.
Furthermore, there are lots of optimizations to reuse file nodes. Just because you delete a file, does not mean that space goes back automatically. It could be "reserved" in your app for several reasons, for other files to use. No sense giving it back to the file system until the file system needs it.
settings->general->usage is a very rough measurement of file system utilization. A better measurement would be accessing the attributes of the file and file system directly.
Using your code as a base, consider this:
- (NSString*)workingDirectory {
NSFileManager* fileManager = [NSFileManager defaultManager];
NSURL* url = [[fileManager URLsForDirectory:NSCachesDirectory
inDomains:NSUserDomainMask] lastObject];
return [url path];
}
- (NSString*)filePath {
return [[self workingDirectory] stringByAppendingPathComponent:FILE_NAME];
}
Now, you can see all the attributes of the entire file system with this:
NSDictionary *attributes = [[NSFileManager defaultManager]
attributesOfFileSystemForPath:[self workingDirectory] error:0];
NSLog(#"file system attributes: %#", attributes);
and those for the specific file with this:
NSDictionary *attributes = [[NSFileManager defaultManager]
attributesOfItemAtPath:[self filePath] error:0];
NSLog(#"file attributes: %#", attributes);
Pay attention to NSFileSystemFreeSize and NSFileSize.
Run your app, and dump both of these values. Create your file, and dump them again. Delete the file, and dump them again.
After all that, you may actually see the NSFileSystemFreeSize go UP, even after the delete. Remember, the system itself is creating temporary files, and is probably caching those file system nodes for future use.
You can get more consistent results if you quit all other apps. Then, quit yours (double-click power button, X all running apps). Delete the file before doing this.
Now, start your app, without the file existing.
Dump file system data.
Create the file.
Dump file system data.
Dump file data.
You should see the file taking up about 200-250 bytes, and the file system free size should drop 8192.
Delete the file.
Dump file system data. Is probably at least as big as it was before deleting file.
Quit app (not in XCode -- double-click power, X the app).
Run the app.
Dump file system data. You should see the data back to about what it was when you started earlier.
In conclusion, while it may look like the file system has not released the data, it really has, but maybe the tool you are using to query just does not know the details of the file system.
Note, also, that when an app is running, it will use lots of file system resources for stuff that you are not explicitly doing.
I hope that made sense...
Your code to delete the file looks correct, but you are switching between URLs and Paths when you don't need to. You should also be checking for an error when you try to delete the file so that you can see why it doesn't work. Try this:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *directoryURLs = [fileManager URLsForDirectory:NSCachesDirectory
inDomains:NSUserDomainMask];
NSURL *directoryURL = [directoryURLs objectAtIndex:0];
NSURL *fileURL = [directoryURL URLByAppendingPathComponent:FILE_NAME];
if (!fileURL)
{
NSLog(#"Could not create URL for file.");
return;
}
NSError *err = nil;
if (![fileURL checkResourceIsReachableAndReturnError:&err])
{
NSLog(#"File is not reachable.\n"
"Error: %# %d %#", [err domain], [err code], [[err userInfo] description]);
return;
}
err = nil;
[fileManager removeItemAtURL:fileURL error:&err];
if (err)
{
NSLog(#"Unable to delete existing file.\n"
"Error: %# %d %#", [err domain], [err code], [[err userInfo] description]);
return;
}
May it be, that the length of the ˚FILE_NAME` is greater or equal to 300 chars? This brought me to similar issues with NSFileManager some time ago...