In a method I need to read a NSArray or NSDictionary from a plist file.
Here is my problem : How can I create a NSArray OR a NSDictionary from a plist file when I don't know if it's an array or dictionary in it ?
I know I can make :
NSArray *myArray = [NSArray arrayWithContentsOfFile:filePath];
or
NSDictionary *myDico = [NSDictionary dictionaryWithContentsOfFile:filePath];
But I don't know if filePath contains a NSArray or a NSDictionary. I would like something like :
id myObject = [... ...WithContentOfFile:filePath];
Can somebody help me and give me the best way to do this ?
Here's how
NSData *data = [NSData dataWithContentsOfFile:filePath];
id obj = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:NULL error:nil];
if([obj isKindOfClass:[NSDictionary class]]) {
//cast obj to NSDictionary
}
else if([obj isKindOfClass:[NSArray class]]) {
//cast obj to NSArray
}
You can use isKindOfClass method to detect which class is that.
You will have to load .plist file using:
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
plistPath = [rootPath stringByAppendingPathComponent:#"Data.plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
plistPath = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
}
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
id objectFromPlist = [NSPropertyListSerialization
propertyListWithData:plistXML
options:0
format:&format
errorDescription:&errorDesc];
And you can check here:
if([objectFromPlist isKindOfClass:[NSArray class]])
{
NSArray* plistArray = (NSArray*) classFromPlist;
}
Related
I successfully added my dictionary to plist but when i add 2nd time it remove my old dictionary from my plist.
Here is my code :
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"favQuote.plist"];
NSDictionary *plistDict = [[NSDictionary alloc] initWithObjectsAndKeys:self.txtQuote.text,#"quote",self.lblAuthor.text,#"author",nil];
NSError *error = nil;
NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 options:NSPropertyListWriteStreamError error:&error];
if(plistData)
{
[plistData writeToFile:plistPath atomically:YES];
NSLog(#"Data saved sucessfully");
}
else
{
NSLog(#"Data not saved");
}
How do I add my new dictionary without losing old data?
You should store your dictionary of array in your document directory something like,
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"favQuote.plist"];
NSFileManager *manager = [NSFileManager defaultManager];
NSMutableArray *arr = [[NSMutableArray alloc]init];
if ( [manager fileExistsAtPath:plistPath]) {
NSData *data = [NSData dataWithContentsOfFile:plistPath];
arr = (NSMutableArray*)[NSKeyedUnarchiver unarchiveObjectWithData:data];
}
NSDictionary *plistDict = [[NSDictionary alloc] initWithObjectsAndKeys:#"test",#"quote",#"test2",#"author",nil];
[arr addObject:plistDict];
NSData *plistData = [NSKeyedArchiver archivedDataWithRootObject:arr];
if(plistData)
{
[plistData writeToFile:plistPath atomically:YES];
NSLog(#"Data saved sucessfully");
}
else
{
NSLog(#"Data not saved");
}
then you can retrieve and use your values like,
NSMutableArray *arr1 = [[NSMutableArray alloc]init];
if ( [manager fileExistsAtPath:plistPath]) {
NSData *data = [NSData dataWithContentsOfFile:plistPath];
arr1 = (NSMutableArray*)[NSKeyedUnarchiver unarchiveObjectWithData:data];
}
if (arr1.count > 0) {
NSDictionary *firstDic = [arr1 objectAtIndex:0];
}
+ (NSString *)getValueforLocale:(NSString*) i18nkey :(NSString*)locale{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSLog(#"paths are : %#",paths);
NSString *libraryDirectory = [paths objectAtIndex:0];
NSLog(#"libraryDirectory : %#",libraryDirectory);
NSString *filePath = [libraryDirectory stringByAppendingPathComponent:#"I8nDB"];
filePath = [filePath stringByAppendingPathComponent:locale];
NSLog(#"file path is : %#",filePath);
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
if(fileExists)
{
NSDictionary *dict = [[[NSDictionary alloc] initWithContentsOfFile:filePath]autorelease];
NSDictionary *resourceBundle = [[[NSDictionary alloc] init]autorelease];
NSString *keyValue = [[[NSString alloc]init]autorelease];
resourceBundle = [dict valueForKey:#"hash"];
keyValue=[resourceBundle valueForKey:i18nkey];
NSLog(#"value for %# is(container) : %#",i18nkey,keyValue);
if(keyValue != nil || keyValue != NULL)
{
return keyValue;
}
else
{
NSLog(#"key not found in the container file");
NSString *path = [[NSBundle mainBundle] pathForResource:#"Localizable"
ofType:#"strings"
inDirectory:nil
forLocalization:locale];
NSLog(#"path for %# is : %#",locale,path);
fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
if(fileExists)
{
NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
NSLog(#"value for %# is(resources) : %#",i18nkey,[dict objectForKey:i18nkey]);
return [dict objectForKey:i18nkey];
}
else
{
return NULL;
}
}
}
else
{
NSLog(#"%# locale does not exist in container",locale);
NSString *path = [[NSBundle mainBundle] pathForResource:#"Localizable"
ofType:#"strings"
inDirectory:nil
forLocalization:locale];
NSLog(#"path for %# in resources is : %#",locale,path);
fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
if(fileExists)
{
NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
NSLog(#"value for %# is : %#",i18nkey,[dict objectForKey:i18nkey]);
return [dict objectForKey:i18nkey];
}
else
{
return NULL;
}
}
}
If we remove Autorelease from the above code, it is working in iOS7 if not the app is crashing
My Main concern is why it doesn't crash in iOS8&9 and only crashes in iOS7
is there in change related to autorelease over these versions
Why don't you use ARC? Then you won't need autorelease...
See http://rypress.com/tutorials/objective-c/memory-management
Your problem might be related to the settings about ARC.
in Your code you only alloc a dictionary in
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
so you only need to care about it, another object is not owned by You! so You don't need release or autorelease them.
Try flowing code
+ (NSString *)getValueforLocale:(NSString*) i18nkey :(NSString*)locale
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSLog(#"paths are : %#",paths);
NSString *libraryDirectory = [paths objectAtIndex:0];
NSLog(#"libraryDirectory : %#",libraryDirectory);
NSString *filePath = [libraryDirectory stringByAppendingPathComponent:#"I8nDB"];
filePath = [filePath stringByAppendingPathComponent:locale];
NSLog(#"file path is : %#",filePath);
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
if(fileExists)
{
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
//NSDictionary *resourceBundle = [[[NSDictionary alloc] init]autorelease];
//NSString *keyValue = [[[NSString alloc]init]autorelease];
NSDictionary *resourceBundle = [dict valueForKey:#"hash"];
// relese dict here because not use after
[dict release];
NSString *keyValue=[resourceBundle valueForKey:i18nkey];
NSLog(#"value for %# is(container) : %#",i18nkey,keyValue);
if(keyValue != nil || keyValue != NULL)
{
return keyValue;
}
else
{
NSLog(#"key not found in the container file");
NSString *path = [[NSBundle mainBundle] pathForResource:#"Localizable"
ofType:#"strings"
inDirectory:nil
forLocalization:locale];
NSLog(#"path for %# is : %#",locale,path);
fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
if(fileExists)
{
// NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(#"value for %# is(resources) : %#",i18nkey,[dict objectForKey:i18nkey]);
return [dict objectForKey:i18nkey];
}
else
{
return NULL;
}
}
}
else
{
NSLog(#"%# locale does not exist in container",locale);
NSString *path = [[NSBundle mainBundle] pathForResource:#"Localizable"
ofType:#"strings"
inDirectory:nil
forLocalization:locale];
NSLog(#"path for %# in resources is : %#",locale,path);
fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
if(fileExists)
{
// NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(#"value for %# is : %#",i18nkey,[dict objectForKey:i18nkey]);
return [dict objectForKey:i18nkey];
}
else
{
return NULL;
}
}
}
In manual reference counting, retains and releases need to be balanced.
In
NSDictionary *dict = [[[NSDictionary alloc] initWithContentsOfFile:filePath]autorelease];
NSDictionary *resourceBundle = [[[NSDictionary alloc] init]autorelease];
the retains and releases are balanced, because alloc (along with retain, new, copy, mutableCopy) returns a retained instance, and autorelease counts as a release.
However, in
NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
you have an overrelease because you are autorelease something that you have not retained.
iOS version has absolutely nothing to do with it.
I have stored JSON data Into within Plist. Whenever I want to see my stored data I am taking path details and find into my machine then only I can see. I need to read data after storage using objective C.
NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves | NSJSONReadingMutableContainers error:&error];
NSDictionary *response = JSON[#"response"];
NSArray *keys = [response allKeys];
NSMutableArray *objects = [NSMutableArray new];
for (NSString *key in keys) {
NSMutableDictionary *object = response[key];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"subject = %#",object[#"subject"]];
NSArray *objectsWithSameSubject = [objects filteredArrayUsingPredicate:predicate];
NSInteger subjects = [object[#"subject"] integerValue];
if (subjects > 0) {
NSMutableArray *Objects_Subjectcount = [NSMutableArray new];
[object setObject:Objects_Subjectcount forKey:#"Objects_Subjectcount"];
for (NSInteger i = 0; i < subjects; i++) {
[Objects_Subjectcount addObject:object];// object or anything you need
}
}
[objects addObject:object];
}
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = paths.firstObject;
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"File.plist"];
NSError *writeError = nil;
NSDictionary *finalDict = #{#"Objects": objects};
NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:finalDict format:NSPropertyListXMLFormat_v1_0 options:NSPropertyListImmutable error:&writeError];
if(plistData){
[plistData writeToFile:plistPath atomically:YES];
}
else {
NSLog(#"Error in saveData: %#", error);
}
Once you have the right path to the plist file, you can read it by:
NSMutableDictionary *plistDic = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
int value = [[plistDic objectForKey:#”value”] intValue]; //to get a int value for value for example
and to write to plist:
[plistDic setObject:[NSNumber numberWithInt:2] forKey:#”value”];
[plistDic writeToFile: path atomically:YES];
Try this:
NSError *error = nil;
NSData * tempData = [[NSData alloc] initWithContentsOfFile:#"File.plist"];
NSPropertyListFormat plistFormat = nil;
NSDictionary *temp = [NSPropertyListSerialization propertyListWithData:tempData options:NSPropertyListImmutable format:NSPropertyListXMLFormat_v1_0 error:&error];
Note :
when you read data using NSPropertyListSerialization you dont have to pass NSPropertyListFormat . you either pass nil or pass the address for NSPropertyListFormat.
Hope this will help.
I have an issue to generate plist file from NSMutableDictionnary. It seems to doesn't work. i have watched many example on Stackoverflow. I generate Dictionary from a plist but not plist from Dictionary.
Here is my code :
NSArray *keys = [NSArray arrayWithObjects:#"id", #"name", nil];
NSArray *object = [NSArray arrayWithObjects:#"Value id", #"Value name", nil];
NSMutableDictionary *projectData = [NSDictionary dictionaryWithObject:object forKey:keys];
NSFileManager* fileManager = [NSFileManager defaultManager];
for (id key in projectData)
{
NSLog(#"projectData = key : %#, value : %#", key, [projectData objectForKey:key]);
}
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *plistPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"data.plist"];
NSLog(#"%#", plistPath);
//write
[projectData writeToFile:plistPath atomically:YES];
NSMutableDictionary *newDictionnary;
//read
if ((plistPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"data.plist"])) {
if ([fileManager isWritableFileAtPath:plistPath]) {
[projectData writeToFile:plistPath atomically:YES];
newDictionnary = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
//NSLog(#"%#", plistPath);
}
}
else {
NSLog(#"File not found");
}
for (id key in newDictionnary)
{
NSLog(#"New dictionnary = key : %#, value : %#", key, [newDictionnary objectForKey:key]);
}
Thanks for help :)
One note: your paths variable is never used.
You cannot write to your bundle directory directly. You have to write either to the documents folder, the caches folder or the temp folder.
Replace [[NSBundle mainBundle] bundlePath] with paths[0].
Got a little problem. I have a .plist file which contain next data
So i cant understand, how i need read first array, than in array read dictionary. Or maybe need rewrite file and change Root key to type dictionary, and read like this:
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSString *plistPath;
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES) objectAtIndex:0];
plistPath = [rootPath stringByAppendingPathComponent:#"Data.plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
plistPath = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
}
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&errorDesc];
if (!temp) {
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
self.personName = [temp objectForKey:#"Name"];
self.phoneNumbers = [NSMutableArray arrayWithArray:[temp objectForKey:#"Phones"]];
You can do it by the following code:
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
NSArray *dataArray = [NSArray arrayWithContentsOfFile:plistPath];
This array will contain all the dictionaries, you can get them like:
NSDictionary *dict = [dataArray objectAtIndex:0];
SString *plistFile = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
NSArray *array = [NSArray arrayWithContentsOfFile:plistFile];
for(NSDictionary *dictionary in array) {
NSLog(#"%#", dictionary);
}
NSDictionary *item0 = array[0];
NSString *imageName = item[0][#"name"];