Here is the NSURL object that I am using for creating and accessing important configuration file on iOS with features:
hidden from user
not user generated file (storing configuration related to user)
not temp or cache (not possible to create later with existing data)
must be backed up by iCloud/iTunes
[NSURL fileURLWithPath:[[NSSearchPathForDirectoriesInDomains(
NSLibraryDirectory,
NSUserDomainMask,
YES) objectAtIndex:0]
stringByAppendingString:#"/important.txt"]];
As suggested in FileSystemOverview (developer.apple.com), I am storing this file under Library.
Maybe better way is storing it under Library/Application Support.
Using NSSearchPathForDirectoriesInDomains, which takes:
enum NSSearchPathDirectory
NSApplicationDirectory
NSDeveloperApplicationDirectory
NSLibraryDirectory
NSDeveloperDirectory
NSApplicationSupportDirectory
...
enum NSSearchPathDomainMask
NSUserDomainMask
NSLocalDomainMask
NSSystemDomainMask
...
BOOL expandTilde
Is this the correct way of storing such a file?
There are couple of alternatives for NSSearchPathDirectory and NSSearchPathDomainMask.
Also what about the expandTilde, on iOS is it necessary?
Is there a better way of doing it, instead of creating path as a NSString using objectAtIndex and appending file name then converting it to NSURL?
Thanks.
Using NSApplicationSupportDirectory would be my first choice for this.
But keep a few things in mind:
Unlike the "Documents" folder, the "Library/Application Support" folder doesn't exist in an iOS app sandbox by default so you must create the folder before trying to use it.
"Hidden from the user" only means that the user won't see it under normal circumstances. But the file is easily accessible by anyone with any technical knowledge.
You do want to pass YES for the expandTilde parameter so your app returns a proper path when you run the app in the simulator. On a real iOS device, it makes little difference.
Do not use stringByAppendingString: to create paths. Use stringByAppendingPathComponent:.
NSString *appSupportPath = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [appSupportPath stringByAppendingPathComponent:#"important.txt"];
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
You can get a direct NSURL using NSFileManager:
NSURL *appSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] firstObject];
NSURL *fileURL = [appSupportURL URLByAppendingPathComponent:#"important.txt"];
Related
I was wondering if property lists, (.plist) would save the contents if i were to exit the app and then re-open it. I would like to save a couple of variables to a plist so I could read from it every time I need some universal information about the user in my app.
Do plists save the variables if I were to close and reopen the app?
Should I be using plists or is there a better way to do this?
Thank you!
Yes, you can use plist to save user infomation. This is a sample way. Make a plist and write to the sandbox.
NSString *urlStr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
urlStr = [urlStr stringByAppendingPathComponent:#"userInfo.dic"];
NSURL *url = [NSURL fileURLWithPath:urlStr];
[userInfoDictionary writeToURL:url error:NULL];
You can also use SQLite database or Core Data if the information is complex.
I save some run-time generated files inside the .app bundle of my iOS app. In the simulator it works fine, in the device it crashes:
Could create output files in the given shader cache path
'/var/mobile/Applications/CB064997-B40E-4FE3-9834-B3217CE33489/SimedTest.app/Ogre3D/assets/RTShaderLib/cache/
Is there a good overview of where I should and shouldn't put files - how to use Documents, Library and tmp, etc?
To clarify, these are files created at startup which pre-calculate some data to save time. IF they are not present they get created so it's fine they are deleted, but not while the app is running.
The bundle is read-only. You don't want to mess around with it for two reasons:
Code Signing: the signature is verified by against the contents of the bundle; if you mess around with the bundle, you break the signature.
App Updates: updates work by replacing the entire app bundle with a newly downloaded one; any changes you make will get lost.
Where you should save stuff:
Documents: if you want it to persist and be backed up
Library/Caches: if you just want to cache downloaded data, like profile pics; will be auto deleted by the system if it is low on room unless you specify with a special do-not-delete flag.
tmp: temporary files, deleted when your app is not running
For a full explanation check out File System Programming Guide and QA1719.
No, every time you change your bundle you invalidate your signature.
If you want to write files you`l need to write in the best folder depending on what you want to do with that file.
Documents folder for long duration files
Cache for small operations
and so on
EDIT
To get the path you`ll need something like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"filename.ext"];
With this path you can write or read like this:
write:
NSString *content = #"One\nTwo\nThree\nFour\nFive";
[content writeToFile:fileName atomically:NO encoding:NSStringEncodingConversionAllowLossy error:nil];
read:
NSString *content = [[NSString alloc] initWithContentsOfFile:fileName usedEncoding:nil error:nil];
I am saving audio data to the Documents directory and trying to read it back. If I play it back immediately it plays successfully, however, if I start a new session and try and play the song locally it will fail even though listing the files in the Documents directory shows that my file is still there. Note that the file is played back from the Documents folder in the same way (same code) if it is played immediately or during a new session.
Here is how I save the audio data to the Documents directory:
+(void)writeDataToAudioFile:(NSData*)data forTrack:(MediaItem*)track
{
// filename looks like "[track_id].mp3"
NSString *fileName = [NSString stringWithFormat:#"%#.%#",track.sc_id,track.original_format];
NSString *pathName = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES) firstObject]
stringByAppendingPathComponent:fileName];
[[NSFileManager defaultManager] createFileAtPath:pathName
contents:data
attributes:nil];
}
Then in my music player I want to load the local URL to this file to initialize the AVPlayer:
NSURL *url;
if(_currentTrack.is_local_item)
{
url = [NSURL fileURLWithPath:_currentTrack.local_file_path];
}
url does not get created properly as AVPlayer does not play. Furthermore, I have tried every different way to load the file as data into an NSData object to check the byte size but trying to access the file as data always returns nil. However, the file exists as if I use NSFileManager I am able to iterate over the items in the Documents directory and print their file names/paths, validating that I the path I have saved in "_currentTrack.local_file_path" does exist. Again, if I play the file immediately after saving the file to disk it will play back.
If there is more info I can provide to make this clearer I will. Thank you very much.
Do not write the full directory path to DB. It will change. You need to only save the file name to DB as reference. Then use as follows:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *fileName = #"SAVED_FILE_NAME.mp3"; // eg: [track_id].mp3
NSString *filePath = [documentsPath stringByAppendingPathComponent:fileName];
This will provide you the actual path of the file.
Keep coding........... :)
I found the solution after putting the problem down for a few days. I break-pointed and print-stated the heck out of the program and I found that the file path I was saving was not the same as the file path of the file.
I think this was a simulator issue, as the issue only occurred between different sessions of the simulator, and worked within the same session, so the device id (which is part of the absolute path) was changing - maybe someone more knowledgeable can weigh in on that.
Pay closer attention to the string values of your variables folks!
I am having problems updating an app to iOS7 SDK. Before I've used iOS 6 SDK and accessed my mp3 file using a NSURL for the folder like this:
NSURL *folderURL = [[NSBundle mainBundle] URLForResource:#"" withExtension:#"" subdirectory:#"AudioGuide"];
Now, using the iOS 7 SDK I always get nil as the value for folderURL and my audio guide doesn't find the mp3's anymore.
I've already looked into the generated .app-Bundle for the simulator (in ~/Library/Application Support/...), and I can see the "AudioGuide" folder in the root. So it's definitly there.
I am not that iOS guru and didn't really follow iOS7 updates. Has there been any changes made on how to access own assets in an app? How do I access my files?
Apple doc for - (NSURL *)URLForResource:(NSString *)name withExtension:(NSString *)extension subdirectory:(NSString *)subpath
Returns the file URL for the resource file identified by the specified
name and extension and residing in a given bundle directory.
what that means is you can get the file URL with specific type in a given subdirectory, so you should use this method call with your file name and extension.
If you are trying to read the list of files under that directory, what you can do is bundle all of your audio files in a single zip or gzipped file and you extract it in your Documents or Application Support directory when it is accessed for the very first time, then you can read it using the code below.
NSArray *directories = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *directory = [directories objectAtIndex:0];
NSString *audioDirectoryPath = [directory stringByAppendingPathComponent:#"AudioGuide"];
NSArray *audioFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:audioDirectoryPath];
Hope this helps!
I save some run-time generated files inside the .app bundle of my iOS app. In the simulator it works fine, in the device it crashes:
Could create output files in the given shader cache path
'/var/mobile/Applications/CB064997-B40E-4FE3-9834-B3217CE33489/SimedTest.app/Ogre3D/assets/RTShaderLib/cache/
Is there a good overview of where I should and shouldn't put files - how to use Documents, Library and tmp, etc?
To clarify, these are files created at startup which pre-calculate some data to save time. IF they are not present they get created so it's fine they are deleted, but not while the app is running.
The bundle is read-only. You don't want to mess around with it for two reasons:
Code Signing: the signature is verified by against the contents of the bundle; if you mess around with the bundle, you break the signature.
App Updates: updates work by replacing the entire app bundle with a newly downloaded one; any changes you make will get lost.
Where you should save stuff:
Documents: if you want it to persist and be backed up
Library/Caches: if you just want to cache downloaded data, like profile pics; will be auto deleted by the system if it is low on room unless you specify with a special do-not-delete flag.
tmp: temporary files, deleted when your app is not running
For a full explanation check out File System Programming Guide and QA1719.
No, every time you change your bundle you invalidate your signature.
If you want to write files you`l need to write in the best folder depending on what you want to do with that file.
Documents folder for long duration files
Cache for small operations
and so on
EDIT
To get the path you`ll need something like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"filename.ext"];
With this path you can write or read like this:
write:
NSString *content = #"One\nTwo\nThree\nFour\nFive";
[content writeToFile:fileName atomically:NO encoding:NSStringEncodingConversionAllowLossy error:nil];
read:
NSString *content = [[NSString alloc] initWithContentsOfFile:fileName usedEncoding:nil error:nil];