NSFileManager -createFileAtPath fails with NSInvalidArgumentException - ios

Maybe I've just missed something in the documentation, but I can't find anything that says this behavior should have changed in iOS 8.
My app sets the current working directory to the Documents directory, then tries to create a file there using NSFileManager -createFileAtPath. Prior to iOS 8 this works fine. On devices running iOS 8, I get the following on the call to -createFileAtPath:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '*** -[NSFileManager fileSystemRepresentationWithPath:]: nil or empty path argument'
Here is a minimal code snippet that reproduces the problem:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [paths objectAtIndex:0];
[[NSFileManager defaultManager] changeCurrentDirectoryPath:docsDir];
[[NSFileManager defaultManager] createFileAtPath:#"temp.dat" contents:nil attributes:nil];
Note the path argument on createFileAtPath is "temp.dat" - if I change it to "./temp.dat", the call succeeds.
Am I doing something stupid and this was just "accidentally" working in prior iOS releases? Or did they intentionally change the behavior here? Or is this a bug in iOS 8? Other NSFileManager methods that take a path argument seem to be okay with just a filename (e.g. -removeItemAtPath:#"temp.dat" error:&err succeeds).
Edited to add:
This only happens on a physical device. In the simulator, the above call to createFileAtPath succeeds.

I opened a bug with Apple and they closed it as a duplicate. While that doesn't necessarily confirm that they consider it a bug, it at least confirms that I'm not the first to encounter this, and that the behavior did indeed change with iOS 8.
For now, the solution is to prepend ./ to the filename, or supply an absolute path.

Related

Issue with encoding objects on iOS Device

I'm making an app that uses encoding to store objects on the document directory,
On iOS Simulator, the objects are getting saved perfectly, and if i closed the app and got back to it all the data are restored with no issue.
But today i tried my app on the iPhone 5s, the objects are not getting saved when i close the app and go back to it again all the data are getting removed only on the real device, what is the problem ?
I'm using this method to get the directory path:
- (NSString *)pathForTask
{
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [documentDirectories firstObject];
return [documentDirectory stringByAppendingString:#"Tasks"];
}
Archive:
NSString *path = [self pathForTask];
[NSKeyedArchiver archiveRootObject:self.privateTasks toFile:path];
Unarchive:
NSString *path = [self pathForTask];
_privateTasks = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
The following line of code is causing your problem:
return [documentDirectory stringByAppendingString:#"Tasks"];
The results in the returned path being something like:
.../DocumentsTasks
Note the lack of a slash.
Change the line to:
return [documentDirectory stringByAppendingPathComponent:#"Tasks"];
This will return a proper path and your code will work on a device.
It works in the simulator because you end up creating a file named DocumentTasks in some folder on your computer. But on a real device, the folder that this file is trying to be written to is read-only due to sandboxing.
Try getting the documents directory using the following code instead:
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
After getting the documents directory, you can get the path to a specific file by doing:
NSURL *fileURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"[FILE NAME]"];
I'm not sure why your code doesn't work, but this is the code I use on my app and it works in the simulator and the physical device.
Frequently code that works on the sim but not on the device is caused by filename upper/lower case mismatching.
The file system in Mac OS is almost always case-insenstive. (You can use disk volumes who's file system are case sensitive in Mac OS, but they are not by default.)
The simulator runs on Mac OS. It's file system is not case sensitive. So if you save a file as "Tasks" and then load it as "tasks" it works on the sim, but not on an actual iOS device. Make sure you check really carefully for mismatched case in your filenames.

Xcode Getting a Consistent Documents Directory Path

I've looked through the Apple documentation on this point and other questions here, but cannot find a means of getting a consistent path to the documents directory.
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *urls = [fm URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *directory = [urls lastObject];
This produces a different path each time due to one component.
Example:
file:///var/mobile/Containers/Data/Application/CA708CF5-0E1B-414D-A795-31A8BB884BA5/Documents
Next run:
file:///var/mobile/Containers/Data/Application/2C96E341-85EF-485D-AC19-F8844B0880C3/Documents
I realize I need some kind of relative path here but I cannot figure out how to get it. How can I get to the Documents directory consistently to both write and read a file my app will produce?
The path is determined on installation. Each time you run your app in the simulator, it will be removed and reinstalled. Hence the differernt path. So you don't need to worry about this.

fileExistsAtPath: Does Not Return Correct Value in iOS 8 SDK

My app was working perfectly in iOS 7, and now I'm facing some new bugs after switching to iOS 8 SDK.
For Ex. [NSFileManager defaultManager] file existence checking method ( fileExistsAtPath: ) is no longer working. Here's the details about current situation in my code:
This is the block of code whose condition never turns True :
File *tempFile = currentMessage.contains;
NSString *address = tempFile.thumbAddress;
if ([[NSFileManager defaultManager] fileExistsAtPath:currentMessage.contains.thumbAddress])
{
image = [UIImage imageWithContentsOfFile:currentMessage.contains.thumbAddress];
}
I have put a breakpoint before the if and traced it to see what is contains :
Printing description of address:
/Users/Unkn0wn/Library/Developer/CoreSimulator/Devices/C07E1D76-372E-4C9A-8749-50D369294FBA/data/Containers/Data/Application/1DC7DB3A-B0A2-43AE-9EDA-3E121786D1AA/Documents/FileAt141406917489510096thumbnail.jpg
I have checked the Finder to see if the file actually exists ( the address is copied from console so there's no chance of mistyping and typos in checking the path and file existence ) :
But that if block is not executed because fileExistsAtPath: returns NO.
Am I missing anything or it is just a bug of new SDK ? ( Because it was working perfectly with SDK 7.1.1 )
Any suggestions is highly appreciated.
Thanks
If thumbAddress is a full path (not a relative one), it can cause bugs when the app is updated because the app folder changes, for example, your first version of the app could be at that path:
/Users/Unkn0wn/Library/Developer/.../Application/ABCD/
When you update the app, the directory will change, for example:
/Users/Unkn0wn/Library/Developer/.../Application/EFGH/
So if you save a full path for your image in the first version of the app like :
ABCD/yourImage.jpg
And try to retrieve this image with the new version of the app, this image will not be found anymore as your image will now be at the path:
EFGH/yourImage.jpg
Hopes it helps!
EDIT
Maybe your absolute path is incorrect. Try calling it with a relative path:
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
path = [path stringByAppendingString:#"FileAt141406917489510096thumbnail.jpg"];
NSData *imageData = [NSData dataWithContentsOfFile:path]; // Should be != nil

Has the Core Data database persistent storage location changed

I was getting the persistent storage error that has been reported by many others. However in my case it was not due to changing the database model. I chased that rabbit for a couple of hours. Finally one post lead me to try and delete the database itself. I went to the directory my URL pointed to
url=/Users/ccox/Library/Application Support/iPhone Simulator/7.1/Applications/8A231E05-B3A2-4E83-8C81-6B3989C262A5/Library/Documentation/DBEViewDocumentand
it didn't exist.
url=/Users/ccox/Library/Application Support/iPhone Simulator/7.1/Applications/8A231E05-B3A2-4E83-8C81-6B3989C262A5/Library/
was there but the Documentation directory was not there.
Here is the code I am using to get the URL
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsDirectory =[[fileManager URLsForDirectory:NSDocumentationDirectory inDomains:NSUserDomainMask] firstObject];
NSString *documentName = #"DBEViewDocument";
self.url = [documentsDirectory URLByAppendingPathComponent:documentName];
self.document = [[UIManagedDocument alloc] initWithFileURL:self.url];
NSLog(#"url=%#",[self.url path]);
The output from the NSLog is the output URL above.
Is there a setting somewhere that sets the destination location for that documents directory? Apparently the Documentation directory is a not a standard directory since it's not created as part of the application install.
I went ahead and created the Documentation directory and now everything is working fine (at least that far), but I can't believe this is by design and that everyone is having to do this.
Any thoughts?
Thanks
Chip
You're using the wrong directory. NSDocumentationDirectory is not a standard location for documents, and is not created by default. You want NSDocumentDirectory, which is created by default.

Download localizable 'strings' files from server

I would like to be able to add/edit languages in my iOS applications. Therefore, I followed the approach in this question, could not get it to work however.
This is what I did:
I created a folder myFolder and dragged the localization folders (en.lproj, ...) from my XCode project into that folder. Then I renamed it to myFolder.bundle.
Then I tried to load that whole bundle from a URL like so:
NSURL *url = [NSURL URLWithString:#"http://192.168.0.12/language_plists/Localizable.bundle"];
NSBundle *bundle = [[NSBundle alloc] initWithURL:url];
which throws the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSBundle initWithURL:]: non-file URL argument'
Explicitly it will not work using a non-file URL, because NSBundle only deals with File URLs. At best if you were to download, you would have to download into either Cache or Document locations, and you would have to do so explicitly.
Further, You will need to re-submit the App in order to get many of the benefits of localization, because your App Store entry will only contain languages that can be tested before download.
You have option with this github
Download your json file in your document directory and access its path.
i.e.
//Download the app from server and save it to Document directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths lastObject] stringByAppendingPathComponent:#"strings.json"];

Resources