conditionally create/read a .plist file - ios

I want to check for the presence of a .plist file in the Documents directory. If it does not exist, I want to create it and seed it with the 1st entry. If it does exist I want to read it and append an entry to it.
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
// read Faves file...
NSMutableDictionary *tempDict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
appDelegate.dictFaves = tempDict;
} else {
NSLog(#"Creating Favorites file");
BOOL result = [[NSFileManager defaultManager] createFileAtPath: path contents: (NSData *)appDelegate.dictFaves attributes: nil];
}
// .... and Append it to the list of existing favorites
[appDelegate.dictFaves setObject:newFave forKey:key];
createFileAtPath returns FALSE meaning the file was not created.
I question the validity of casting appDelegate.dictFaves to (NSDATA *). If that is unwise, how to I create a file with a Dictionary?

You can write it using:
-[NSDictionary writeToURL:atomically:]
So, you can say:
[appDelegate.dictFaves writeToURL:url atomically:YES];
(assuming appDelegate.dictFaves is a valid NSDictionary)

Related

Not able to create files in caches directory

I'm trying to set up a caches directory for use in my app, but the files are not being created for a reason unknown to me. What am I doing wrong? Here are the methods I'm using:
In class Utilities:
+(NSString *)imageCachePath {
NSString *cacheDirectory = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *pieceImagesDirectory = [cacheDirectory stringByAppendingPathComponent:#"PieceImages"];
NSLog(#"imageCachPath is %#",pieceImagesDirectory);
return pieceImagesDirectory;
}
+ (void)cacheImage:(UIImage *)image usingName:(NSString *)name;
{
NSLog(#"Caching image %#",name);
NSString *pieceImagesDirectory = [self imageCachePath];
BOOL isDir = NO;
NSError *error;
if (! [[NSFileManager defaultManager] fileExistsAtPath:pieceImagesDirectory isDirectory:&isDir] && isDir == NO) {
[[NSFileManager defaultManager]createDirectoryAtPath:pieceImagesDirectory withIntermediateDirectories:YES attributes:nil error:&error];
NSLog(#"Error after creating directory:\n%#",error);
} else {
// file exists - I don't expect to use the else block. This is for figuring out what's going on.
NSLog(#"File %# exists -- is it a directory? %#",pieceImagesDirectory, isDir?#"YES":#"NO");
}
NSString *nameToUseInFilename = [name stringByReplacingOccurrencesOfString:#" " withString:#"_"];
NSString *fullPath = [pieceImagesDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.png",nameToUseInFilename]];
NSData *data = UIImagePNGRepresentation(image);
NSFileManager *fileManager = [NSFileManager defaultManager];
//Save the file, overwrite existing if exists.
NSLog(#"Attempting to create file at path %# with %d bytes of data",fullPath, [data length]);
if ([fileManager createFileAtPath:fullPath contents:data attributes:nil]) {
NSLog(#"Success");
} else {
NSLog(#"Error creating file");
}
}
In the class where the images are created, I call the method thus:
// image is an object of type UIImage
// cachedImageName is a string that resolves to something like User Image/12
[Utilities cacheImage:image usingName:cachedImageName];
Here are sample NSLog output lines in the debugger:
... Caching image User Image/12
... imageCachPath is /var/mobile/Applications/5EBB1152-5CC1-4A30-ABD5-B4C9A60E4CB4/Library/Caches/PieceImages
... File /var/mobile/Applications/5EBB1152-5CC1-4A30-ABD5-B4C9A60E4CB4/Library/Caches/PieceImages exists -- is it a directory? YES
... Attempting to create file at path /var/mobile/Applications/5EBB1152-5CC1-4A30-ABD5-B4C9A60E4CB4/Library/Caches/PieceImages/User_Image/12.png with 12071 bytes of data
... Error creating file
The call to NSFileManager fileExistsAtPath:isDirectory: gives you an indeterminate value for isDir if the directory doesn't exist. You should change your code to:
BOOL isDir = NO;
NSError *error;
if (! [[NSFileManager defaultManager] fileExistsAtPath:pieceImagesDirectory isDirectory:&isDir]) {
[[NSFileManager defaultManager]createDirectoryAtPath:pieceImagesDirectory withIntermediateDirectories:YES attributes:nil error:&error];
NSLog(#"Error after creating directory:\n%#",error);
} else {
// file exists - I don't expect to use the else block. This is for figuring out what's going on.
NSLog(#"File %# exists -- is it a directory? %#",pieceImagesDirectory, isDir?#"YES":#"NO");
}
It also appears that you add a second folder User_Image to the path. You never create this directory.
I would also suggest you change how you write the image data. Instead of using NSFileManager createFileAtPath:contents:attributes:, use NSData writeToFile:options:error:. Then you can get an error object providing more details of any problem.
In the end it may be best to build the full path to the file. Strip off the last path component (the actual filename) and then check for the existence of the remaining path. Then make one call to createDirectoryAtPath... and let it create any all all needed folders.

plist not giving the data the way entered

Before I start, I am creating plist programmatically
I am storing the path names of images in plist (programmatically). While saving I took NSLog and below is what I have
2013-08-04 15:25:24.044 XXX[12595:13d03] inserting data is 5===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-07-42-37.25_tstdddd.png
2013-08-04 15:25:24.057 XXX[12595:13d03] inserting data is 4===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-07-18-20.673_iphone_2.jpg
2013-08-04 15:25:24.086 XXX[12595:13d03] inserting data is 2===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-07-21-03-14-29.292_Spare-Parts-summer-Ad-hyundai.jpg
2013-08-04 15:25:24.087 XXX[12595:13d03] inserting data is 1===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-07-21-03-11-55.395_horizon.jpg
2013-08-04 15:25:24.089 XXX[12595:13d03] inserting data is 6===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-08-03-29.371_2010.jpg
the data is entered with id 5,4,2,1,6.
Now when I read this data, below is what I am getting.
2013-08-04 15:27:28.251 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-07-18-20.673_iphone_2.jpg
2013-08-04 15:27:28.252 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-07-42-37.25_tstdddd.png
2013-08-04 15:27:28.252 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-07-21-03-11-55.395_horizon.jpg
2013-08-04 15:27:28.252 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-08-03-29.371_2010.jpg
2013-08-04 15:27:28.252 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-07-21-03-14-29.292_Spare-Parts-summer-Ad-hyundai.jpg
means while fetching I get data with 4,5,1,6,2
When I double click plist, I have data with id sorted i.e. I have data as 1,2,4,5,6.
Any idea why plist is providing random data?
Below is the screen shot of plist that is getting generated.
Edit 1
For Storing data, below is the code I have.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
news = [NSJSONSerialization JSONObjectWithData:data options:nil error:nil];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Offers.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
if(![[NSFileManager defaultManager] removeItemAtPath:path error:&error])
{
//TODO: Handle/Log error
NSLog(#"files not deleted...");
} else {
NSLog(#"files deleted...");
}
if (![fileManager fileExistsAtPath: path])
{
path = [documentsDirectory stringByAppendingPathComponent: [NSString stringWithFormat: #"Offers.plist"] ];
}
NSMutableDictionary *data002 = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
if ([fileManager fileExistsAtPath: path])
{
data002 = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
}
else
{
// If the file doesn’t exist, create an empty dictionary
data002 = [[NSMutableDictionary alloc] init];
}
int count;
for (count = 0; count < (int)[news count]; count++)
{
[data002 setObject:[[news objectAtIndex:count] objectForKey:#"imagePath"] forKey:[[news objectAtIndex:count] objectForKey:#"id"]];
[data002 writeToFile:path atomically:YES];
}
[data002 release];
}
Answer
What I did is instead of NSMutableDictionary I used NSMutableArray and all worked perfectly.
You plist is a dictionary, it has no order, just keys and values. If you need to store the order then change to use an array (of dictionaries) or use an additional file to store the order.
data002 is a dictionary. That's what makes your plist a dictionary (when you call writeToFile:). If you want to maintain the relative order you need to decide which approach you want to take and then create an NSArray of the items in order and use writeToFile: on the array to save it.
Typically, in order to maintain sequential data you would have to use an array of strings in the plist file rather than directly inserting strings to the root element (which is a dictionary). Alternatively, if you wish to store more complex entities, I would recommend indexing them and referring to the indexes alone in a separate array to maintain order.
Tap on the "+" sign to add an element, then select "Array" from the drop down menu under "type"
If you're doing this in code, simply insert an NSArray as an object in the NSDictionary you're saving as a plist:
[data002 setObject:#[path1,path2,path3] forKey:#"orderedArray"];

How to create NSMutableArray by plist file? [duplicate]

This question already has answers here:
getting data from plist to NSMutableArray and writing the NSMutableArray to same plist iPhone
(3 answers)
Closed 9 years ago.
Previously, I used the following code to create an array for me and it worked.
bundle = [NSBundle mainBundle];
path = [bundle pathForResource:#"MultiSetting" ofType:#"plist"];
settingArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
But after that, I wanted to modify the plist file, therefore, I used the following code to do that and it's NOT working.
NSFileManager *mgr = [NSFileManager defaultManager];
NSArray *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [documentPath objectAtIndex:0];
NSString *dstPath = [documentDirectory stringByAppendingPathComponent:#"MultiSetting.plist"];
bundle = [NSBundle mainBundle];
NSString *srcPath = [bundle pathForResource:#"MultiSetting" ofType:#"plist"];
NSError *error = nil;
[mgr copyItemAtPath:srcPath toPath:dstPath error:(NSError **)error];
settingArray = [[NSMutableArray alloc] initWithContentsOfFile:dstPath];
NSLog(#"%#", settingArray);
Is there any solution to solve this problem? Did I do anything wrong?
First thing you are passing error wrong. You need to change that line to
[mgr copyItemAtPath:srcPath toPath:dstPath error:&error];
Another thing according to your code after the first run, the above line will fail as there will already be a file that particular name. If you want to initialize only once then I guess you could write something like below which makes more sense:
NSError *error = nil;
if (![mgr fileExistsAtPath:dstPath]) {
[mgr copyItemAtPath:srcPath toPath:dstPath error:&error];
if (error) {
NSLog(#"%#",[error localizedDescription]);
}
}
And finally initWithContentsOfFile: might be failing because the plist cannot be parsed into an array. This can be because your plist file's root object is a dictionary (the default when you create the plist using Xcode).
Since you are able to parse the plist file in bundle it might be because, you might have accidentally copied the wrong file (or an empty file or a plist with root as dictionary) the first time and then was not able to copy. So try deleting the file from dstPath and try again.
To check the file do an NSLog of dstPath. For example if you get something like this in the console:
/Users/xxxx/Library/Application Support/iPhone Simulator/5.0/Applications/194351E6-C64E-4CE6-8C82-8F66C8BFFAAF/Documents/YourAppName.app
Copy this till the Documents folder, i.e
/Users/xxxx/Library/Application Support/iPhone Simulator/5.0/Applications/194351E6-C64E-4CE6-8C82-8F66C8BFFAAF/Documents/
Go to:
Finder -> Go -> Go To Folder
and paste the path and click on Go. This should take you to the actual directory and check the contents of the plist . Open it as source code in Xcode to see what is the root object.
Also try deleting this file you find here and running your app agin.
Another way to achieve the above would be to initialize the array from bundle and then write it directly instead of copying the file (but this is not a the direct answer to your problem just a workaround) i.e :
NSString *srcPath = [bundle pathForResource:#"MultiSetting" ofType:#"plist"];
NSError *error = nil;
//initialize from source
NSMutableArray *settingsArray = [[NSMutableArray alloc] initWithContentsOfFile:srcPath];
//write to file
NSError *error = nil;
//check if file exists
if (![mgr fileExistsAtPath:dstPath]) {
[settingArray writeToFile:dstPath atomically: YES];
if (error) {
NSLog(#"%#",[error localizedDescription]);
}
}
Any changes you make to the settingArray array do not automatically get saved to disk. You need to explicitly save it to disk. When you actually want to save the contents of the settingArray variable, you need to call:
[settingArray writeToFile:dstPath atomically: YES];

how to store an image path in a plist?

I know this is probably a silly question but I'm storing most of my game data in a plist - with that I'd like to include references to images used within my game - same hierarchal level as 'supporting files'. I have different types of images stored in 3 separate folders. One folder for example is called imageclue. How could I store the path in my plist, I'm stuck because I can't just store the path in my plist as string - filename.jpg. I've tried getting the path of the file but when I log it out it .
Sorry if I'm not explaining well and thank you in advance for any help :)
EDIT**
I have a plist file added to my program I don't want to programatically add to it as the images are constants - the screenshots below show a tutorial instead of the filename.jpg (because that won't work seen as my images are stored in a file) I wondered what path name do I use as a string.
The image is from a tutorial off of appcoda.com - where it says thumbnails are the image path files. If you look at where the images are stored on the left - they are stored with the program files. My images are in a folder in there so I'm confused as to what to enter in my plist for the image file.
Hope this clears up what I meant, sorry :)
Store three variables in .h file
#interface YourViewController : UIViewController
{
NSString *folder1;
NSString *folder2;
NSString *folder3;
}
in viewdidload:
-(void) viewdidLoad
{
//get the documents directory:
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//getting the folder name:
folder1 = [NSString stringWithFormat:#"%#/imageclue",
documentsDirectory];
folder2 = [NSString stringWithFormat:#"%#/folder2",
documentsDirectory];
folder3 = [NSString stringWithFormat:#"%#/folder3",
documentsDirectory];
}
-(NSArray*) getPlistFromFolder:(NSString*)folder imageName:(NSString*)image
{
NSString *imageTitle = [NSString stringWithFormat:#"%#/image",
folder];
NSArray *data = [[NSArray alloc] initWithContentsOfFile:plistName];
return data;
}
So in the plist file, just store the image name.
Hope this helps...
Do it like this,
NSDictionary *imagePaths = #{#"image 1": [NSHomeDirectory() stringByAppendingPathComponent:#"image 1"]};
[self writeToPlist:imagePaths];
- (void)writeToPlist:imagePaths:(id)plist{
NSError *error;
NSData *data = [NSPropertyListSerialization dataWithPropertyList:plist format:kCFPropertyListXMLFormat_v1_0 options:0 error:&error];
if(error){
NSLog(#"Could not write to file");
return;
}
[data writeToFile:[self plistPath] atomically:YES];
}
Like wise loading is simple as this;
[self loadImagePathForImageNamed:#"image 1"];
- (NSString*)loadImagePathForImageNamed:(NSString*)imageName{
}
- (NSString*)loadImagePathForImageNamed:(NSString*)imageName{
NSData *data = [NSData dataWithContentsOfFile:[self plistPath]];
NSString *error;
NSPropertyListFormat format;
NSDictionary *dictionary = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
if(error){
NSLog(#"Could not open plist %#", error);
return nil;
}
return dictionary[imageName];
}
You may have to handle the error when the file is not there by creating a new one, otherwise this should work.
You are storing path right way, just need to store filename of image with extension in plist when your images are in your Application Bundle, for more reference you can define key name Instead "item1", "item2" in your plist.
Now coming to actual Question, how to access image from plist
Step 1 : Read your recipes.plist from Application Bundle
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"recipes" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:bundlePath];
Step 2 : Now Get Image/Thumbnails name out of it, which you want to load
Step 3 : Define following Function in your Controller, which returns image from name
- (UIImage *)getImageWithName:(NSString *)imageFileName
{
NSString *ext = [imageFileName pathExtension];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:[imageFileName stringByDeletingPathExtension] ofType:ext];
return [UIImage imageWithContentsOfFile:imagePath];
}
HOW TO USE
Suppose you want to load Image with key "Item2" then write following code
NSString *imageFileName = [[dict objectForKey:#"Thumbnail"] valueForKey:#"Item2"];
UIImage *item2Image = [self getImageWithName:imageFileName];
For "Item6"
NSString *imageFileName1 = [[dict objectForKey:#"Thumbnail"] valueForKey:#"Item6"];
UIImage *item6Image = [self getImageWithName:imageFileName1];

What is the correct way to save user data using NSFileManager?

I am having trouble initializing dictionaries I use throughout my program to store user achievements and scores.
I have almost identical code for the two dictionaries and only the gameCenterData dictionary seems to be working properly. I have tried altering the plist file name and contents yet nothing seems to make the playerData dictionary properly load info from the file as it should
In the Root View Controller I have the following code (playerData and gameCenterData are both NSMutableDictionaries and the plist files are in the proper place)
-(NSString *)scoreFilePath
{
NSArray *scorePath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [scorePath objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"PlayerScoreData.plist"];
}
-(NSString *)gameCenterFilePath
{
NSArray *gameCenterPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [gameCenterPath objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"GameCenterData.plist"];
}
then the view did load
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *playerDataPath = [self scoreFilePath];
if (! [[NSFileManager defaultManager] fileExistsAtPath:playerDataPath])
{
playerData = [NSMutableDictionary dictionaryWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"scoreData.plist"]];
[playerData writeToFile:[self scoreFilePath] atomically:YES];
NSLog(#"Player data file does not exist");
}
else
{
playerData = [[NSMutableDictionary alloc] initWithContentsOfFile:[self scoreFilePath]];
NSLog(#"player data file exists");
}
NSLog(#"scoreData is %#",playerData);
NSString *gameCenterPath = [self gameCenterFilePath];
if (! [[NSFileManager defaultManager] fileExistsAtPath:gameCenterPath])
{
gameCenterData = [NSMutableDictionary dictionaryWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"gameCenterData.plist"]];
[gameCenterData writeToFile:[self gameCenterFilePath] atomically:YES];
NSLog(#"game center data file does not exist");
}
else
{
gameCenterData = [[NSMutableDictionary alloc] initWithContentsOfFile:[self gameCenterFilePath]];
NSLog(#"game center data file exists");
}
NSLog(#"gameCenterData is %#",gameCenterData);
the output is as follows
2012-08-05 11:46:49.991 GlobeRoller[6410:1be03] Player data file does not exist
2012-08-05 11:46:49.992 GlobeRoller[6410:1be03] playerData is (null)
2012-08-05 11:46:50.061 GlobeRoller[6410:1be03] game center data file does not exist
2012-08-05 11:46:50.062 GlobeRoller[6410:1be03] gameCenterData is {
"Career Odometer" = 0;
"Career Score" = 0;
"Cities Found" = 0;
"Offline Games Played" = 0;
"Online Games Played" = 0;
"Online Games Won" = 0;
}
I have searched all of the questions and answers to see if I can find out why this isn't working for both methods. Any help you could offer, or resources you could point me to I would greatly appreciate.
Thank you,
CF
The plist file you are trying to load from the bundle is either not there, or has been created improperly. Directly from the documentation of dictionaryWithContentsOfFile:.
Return Value
A new dictionary that contains the dictionary at path, or
nil if there is a file error or if the contents of the file are an
invalid representation of a dictionary.
You should make sure you are using the proper file name, and then open your plist in Xcode to see if it is properly formatted.
iOS is case sensitive. Are you sure that your file in the bundle is lower case, i.e. "#"scoreData.plist", and not upper case like the name your code uses? Also, verify that these two files are in your bundle - check the build phase or select the files (one at a time) and look in the 3rd Xcode pane in the file attribute section (to verify they are included in your target). If all that looks good then when you try to retrieve the files from your bundle:
Also, don't try to find the file at the root level of the bundle - you should be using:
NSString *path = [[NSBundle mainBundle] pathForResource:#"GameCenterData" ofType:#"plist"];
NSLog(#"PATH is %#", path);
...then use path instead of the code you are using now

Resources