Xcode Getting a Consistent Documents Directory Path - ios

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.

Related

Cannot get mainBundle resources (returns null)

I'm trying to access a resource file I added in the app using relative pathing. I've read that I was supposed to use something like this :
NSBundle *mainBundle = [NSBundle mainBundle];
NSString *filePath = [[mainBundle resourcePath] stringByAppendingPathComponent:#"myFile.txt"];
fh = [NSFileHandle fileHandleForReadingAtPath:resourcePath];
Except this isn't working. With NSLog, I am able to confirm mainBundle isn't null and resourcePath returns something like this:
/Users/tom/Library/Developer/CoreSimulator/Devices/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX/data/Containers/Bundle/Application/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX/MyApp_Demo.app/myFile.txt
I've tried many things, such as adding the name of directory where the resource is located, that would be Ressources/myFile, but nothing is yielding any result.
I'm a total beginner with Objective-C but I have to tinker with legacy code and I have to deal with this, so any help is much appreciated.
Side note:
this is what the project structure looks like from Xcode:
Meanwhile, in Finder, the Ressources directory isn't inside the MyApp directory, rather they're on the same level inside the project directory. I wonder if that could be the problem.
Side note 2:
NSString *filePath2 = [[NSBundle mainBundle] pathForResource:#"myFile" ofType:#"txt"];
returns (null) after logging in the console.
It is best to use a simulator for this debugging process
Verification
You should check if the file or Resources folder is actually being copied to the right location or not. If you have added the Resources folder, than check it with the below code
NSString *resourcesFolderPath = [[NSBundle mainBundle] pathForResource:#"Resources" ofType:nil];
NSString *fullFilePath = [NSString pathWithComponents:#[demoToursPath,"filename.txt"]];
NSFileManager *manager=[NSFileManager defaultManager];
NSLog(#"Filepath: %#", fullFilePath);
NSLog(#"File Exist: %#", [manager fileExistsAtPath:fullFilePath]);
Once you have confirmed this, you can update your code to match the location and path it needs to be in order to access the file.
Additional Debug
You can also just print the Document Directory for the application and open a finder window and navigate there to see if the Resources folder is added at the right place (if it was added)
NSString *documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]
NSLog(#"Document Directory: %#", documentDir);
If you update the question with more info, I would be able to help you better

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.

Where to put txt file when running iOS simulator

I am building a simple iOS app. And I need to read some data from a text file.
But I don't know where to put it.
I have tried to put it under the Debug-iphoneos or Debug-iphonesimulator. But it doesn't work.
Drag it into your project. When asked if it should be part of the app target, make sure it is. The result is that when you build the app, the file will be copied into the app bundle and thus will make its way onto the target device as part of the app, where your code can retrieve it, along these lines:
NSString* f = [[NSBundle mainBundle] pathForResource:#"myfile" ofType:#"txt"];
NSError* err = nil;
NSString* s = [NSString stringWithContentsOfFile:f
encoding:NSUTF8StringEncoding
error:&err];
The main bundle should work like others have stated, if you want to access it for testing via the documents directory for some testing or other purposes. This should give you the directory for that app:
NSLog(#"%#",[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] objectAtIndex:0]);
Resulting URL for if you decide to put it into the documents folder:
[[[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] objectAtIndex:0]URLByAppendingPathComponent:#"YOUR_FILE_NAME_HERE"] URLByAppendingPathExtension:#"txt"];

How to get new .plist data

I'm using this code to read data from a local (in the XCode project) .plist file.
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"GameData.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path])
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"GameData" ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath: path error:&error];
}
gameData = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
And it worked great the start, but now I find when I step through the code in the simulator that my game is using a version of the .plist file that existed right at the start, and the new fields I've set up in my new GameData.plist file is not appearing.
I presume that's because it doesn't get the data again if the file already exists? but then how do I get the new version of the .plist file? I tried removing the if statement, but I get a runtime error saying the file already exists.
It sounds like you need to setup a version system. But, the only reason to copy a file from the bundle to the documents folder is so you can edit it, so you need to think about how to merge the updated file in the bundle and the users additions in the documents folder.
Once you have a plan for merging the files, then you can change your if statement so it doesn't just check if the file already exists (if it does, you need to delete it before you can replace it) but also considers the version. You may want to store the currently copied version number in user defaults and add a version number to the file in the bundle. You may also want to keep the user modified data in a different file and use that as overrides.

fileExistsAtPath: returning NO for files that exist

At a point in my code fileExistsAtPath: is returning NO for files that I have confirmed exist. I've been scratching my head at this and can't figure out why its not working, so changed it to this code as this directory absolutely exists but if it doesn't gets created anyway.
NSError* err = nil;
NSURL *dir = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create: YES
error:&err];
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:[dir absoluteString]];
After running this code the application directory folder exists and err is 0, yet exists is NO.
How can this be?
TIA
You should use [dir path], not [dir absoluteString].
I was bashing my head against the wall for a few hours. Apparently on each and every run in xcode (on simulator) the app directory path was changing. The UUID part of it.
So instead of storing the fullpath I ended up persisting the path postfix
and prefixing that with whatever storage class is implied: temporary, cached
or documents :-[ You realize why you have to sometimes run on device
even if you don't explore the depths of Metal, GLES or multitouch? ;^)
There is a distinction between path and url. A path in unix representation is the location where your particular file or directory exists. Like "/Users/username/Desktop/myfile.txt"
Wheres a url not only contains the location but scheme as well, e.g: http:// or https:// and in our case a file url (file:///Users/username/Desktop/myfile.txt)
Methods like fileExistsAtPath or removeItemAtPath need path in the parameter rather than a url.
While methods like copyItemAtURL:toURL:error:(NSError * _Nullable *)error expects a url.
Use path attribute of NSURL object to retrieve the path. absoluteString gives you the path along with the scheme.

Resources