Create a plist file in iOS error - ios

I want to write the method which should create for writing a plist file. I got the example code in the Web but can not understand what is wrong with it. First of all, when I try to call this method - I get a message in log:
2013-03-28 15:33:47.953 ECom[6680:c07] Property list invalid for format: 100 (property lists cannot contain NULL)
2013-03-28 15:33:47.954 ECom[6680:c07] An error has occures <ECOMDataController: 0x714e0d0>
Than why does this line return (null)?
data = [NSPropertyListSerialization dataWithPropertyList:plistData format:NSPropertyListXMLFormat_v1_0 options:nil error:&err];
and the last question - how to remove the warning message for the same line?
Incompatible pointer to integer conversion sending 'void *' to parameter of type 'NSPropertyListWriteOptions' (aka 'insigned int')
h-file
#import <Foundation/Foundation.h>
#interface ECOMDataController : NSObject
{
CFStringRef trees[3];
CFArrayRef treeArray;
CFDataRef xmlValues;
BOOL fileStatus;
CFURLRef fileURL;
SInt32 errNbr;
CFPropertyListRef plist;
CFStringRef errStr;
}
#property(nonatomic, retain) NSMutableDictionary * rootElement;
#property(nonatomic, retain) NSMutableDictionary * continentElement;
#property(nonatomic, strong) NSString * name;
#property(nonatomic, strong) NSString * country;
#property(nonatomic, strong) NSArray * elementList;
#property(nonatomic, strong) id plistData;
#property(nonatomic, strong) NSString * plistPath;
#property(nonatomic, strong) NSData * data;
#property(nonatomic, strong) id filePathObj;
-(void)CreateAppPlist;
#end
m-file
#import "ECOMDataController.h"
#implementation ECOMDataController
#synthesize rootElement, continentElement, country, name, elementList, plistData, data, plistPath;
- (void)CreateAppPlist {
// Get path of data.plist file to be created
plistPath = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"plist"];
// Create the data structure
rootElement = [NSMutableDictionary dictionaryWithCapacity:3];
NSError *err;
name = #"North America";
country = #"United States";
continentElement = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:name, country, nil] forKeys:[NSArray arrayWithObjects:#"Name", #"Country", nil]];
[rootElement setObject:continentElement forKey:#""];
//Create plist file and serialize XML
data = [NSPropertyListSerialization dataWithPropertyList:plistData format:NSPropertyListXMLFormat_v1_0 options:nil error:&err];
if(data)
{
[data writeToFile:plistPath atomically:YES];
} else {
NSLog(#"An error has occures %#", err);
}
NSLog(#"%# %# %#", plistPath, rootElement, data);
}
#end

It seems that you are serializing the wrong element, replace plistData by rootElement (and nil by 0) in
data = [NSPropertyListSerialization dataWithPropertyList:plistData format:NSPropertyListXMLFormat_v1_0 options:nil error:&err];

Related

Mantle: Ignore Keys in Nested Dictionaries inside an array

I am using the Mantle framework in iOS for a simple JSON structure that looks like this:
{
"posts":
[
{
"postId": "123",
"title": "Travel plans",
"location": "Europe"
},
{
"postId": "456",
"title": "Vacation Photos",
"location": "Asia"
}
],
"updates": [
{
"friendId": "ABC123"
}
]
}
Essentially I am only interested in the "posts" key and wish to completely ignore the "updates" key. Additionally within the "posts" array I wish to completely ignore the "location" key. Here is how I set up my Mantle Models:
#interface MantlePost: MTLModel <MTLJSONSerializing>
#property (nonatomic, strong) NSString *postId;
#property (nonatomic, strong) NSString *title;
#end
#implementation MantlePost
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return #{
#"postId": #"postId",
#"title": #"title",
};
}
#end
And here is my MantlePosts model:
#interface MantlePosts: MTLModel<MTLJSONSerializing>
#property (nonatomic, strong) NSArray<MantlePost *> *posts;
#end
#implementation MantlePosts
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return #{
#"posts": #"posts"
};
}
+ (NSValueTransformer *)listOfPosts {
return [MTLJSONAdapter arrayTransformerWithModelClass:MantlePost.class];
}
#end
Finally, here is how I load my JSON up to be converted:
- (NSDictionary *)loadJSONFromFile {
NSString *jsonPath = [[NSBundle mainBundle] pathForResource:#"parse-response" ofType:#"json"];
NSError *error = nil;
NSData *jsonData = [[NSString stringWithContentsOfFile:jsonPath usedEncoding:nil error:&error] dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:&error];
return jsonDict;
}
NSError = nil;
NSDictionary *jsonData = [self loadJSONFromFile];
MantlePosts *posts = (MantlePosts *)[MTLJSONAdapter modelOfClass:MantlePosts.class fromJSONDictionary:jsonData error:&error];
The problem is, my descendent array of MantlePosts contains all 3 properties postId, title, and location, when I explicitly mapped only postId and title. The "updates" array is ignored which is what I wanted but I've been stuck being able to ignore certain keys in the descendent arrays. Any help on this would be appreciated.
Here is an example of what I receive when i po the response in the console.
(lldb) po posts
<MantlePosts: 0x6000000153c0> {
posts = (
{
location = Europe;
postId = 123;
title = "Travel plans";
},
{
location = Asia;
postId = 456;
title = "Vacation Photos";
}
);
}
(lldb)

create loop for array to get string from json String?

i need to display a Table View containing information from web service response i do no where iam doing wrong here my sample code
NSData *data = [soapResultsString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *array = [json allValues];
for (int i=0; i<array.count; i++)
{
recordResults =NO;
appDelegate.rateString =[[[json valueForKey:#"plan_history"]valueForKey:#"rate"]objectAtIndex:i];
appDelegate.descriptionString=[[[json valueForKey:#"plan_history"]valueForKey:#"description"]objectAtIndex:i];
appDelegate.validityString=[[[json valueForKey:#"plan_history"]valueForKey:#"validity"]objectAtIndex:i];
appDelegate.plantypeString=[[[json valueForKey:#"plan_history"]valueForKey:#"plantype"]objectAtIndex:i];
}
i need to parse 4 values from plan_history like "rate","description","validity","plan type"
when i run my app i getting only one set of value in Table view . i.e my json string contains more than 20 records containing rate,description,validity and plan type
can u show me how to loop my json value and display all my records in Table View
You should eliminate those calls to allValues and valueForKey, as repeatedly calling those methods is very inefficient ways to tackle JSON parsing.
In one of your comments, you said that your JSON looked like:
{
"plan_history": [
{
"rate": "₹1000",
"description": "FullTalktimeTopupRs.1000FullTalktime",
"validity": "Validity: 0\r",
"plantype": "FullTalkTime"
},
{
"rate": "₹508",
"description": "FullTalktimeTopupRs.558morethanFullTalktime",
"validity": "Validity: 2\r",
"plantype": "FullTalkTime"
}
]
}
(I wonder if there was something before this plan_history entry given your allValues reference, but unless you tell us otherwise, I'll assume this is what the original JSON looked like.)
If so, to parse it you would do:
NSMutableArray *results = [NSMutableArray array];
NSError *error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSArray *planHistory = json[#"plan_history"];
for (NSDictionary *planHistoryEntry in planHistory) {
NSString *rateString = planHistoryEntry[#"rate"];
NSString *description = planHistoryEntry[#"description"];
NSString *validity = planHistoryEntry[#"validity"];
NSString *planType = planHistoryEntry[#"plantype"];
// now do whatever you want with these four values.
// for example, I'd generally create a custom object I defined elsewhere for these four values and add to results, e.g.
[results addObject:[PlanHistoryEntry planHistoryEntryWithRate:rateString
description:description
validity:validity
planType:planType]];
}
// now do something with results, e.g. store it in some property in `appDelegate`, etc.
Where, PlanHistoryEntry might be defined like so:
#interface PlanHistoryEntry : NSObject
#property (nonatomic, copy) NSString *rateString;
#property (nonatomic, copy) NSString *planDescription; // note, do not use `description` for property name
#property (nonatomic, copy) NSString *validity;
#property (nonatomic, copy) NSString *planType;
+ (instancetype) planHistoryEntryWithRate:(NSString *)rateString
planDescription:(NSString *)planDescription
validity:(NSString *)validity
planType:(NSString *)planType;
#end
#implementation PlanHistoryEntry
+ (instancetype) planHistoryEntryWithRate:(NSString *)rateString
planDescription:(NSString *)planDescription
validity:(NSString *)validity
planType:(NSString *)planType
{
PlanHistoryEntry *entry = [[self alloc] init];
entry.rateString = rateString;
entry.planDescription = planDescription;
entry.validity = validity;
entry.planType = planType;
return entry;
}
#end
But I don't want you to get lost in the minutiae of this answer (because given the ambiguity of the question, I may have gotten some details wrong). The key point is that you should not be using allValues or valueForKey. Just navigate the JSON structure more directly as illustrated above.
Try this,
NSData *data = [soapResultsString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *rateArray = [[json objectForKey:#"plan_history"] objectForKey:#"rate"];
NSArray * descriptionArray = [[json objectForKey:#"plan_history"] objectForKey:#"description"];
NSArray * validityArray = [[json objectForKey:#"plan_history"] objectForKey:#"validity"];
NSArray * plantypeArray = [[json objectForKey:#"plan_history"] objectForKey:#"plantype"];
and use rateArray, descriptionArray etc.
You can create a class storing your data as follows:
Something like:
planClass.h
#property(nonatomic, strong) NSString * rateString;
#property(nonatomic, strong) NSString * descriptionString;
#property(nonatomic, strong) NSString * validityString;
#property(nonatomic, strong) NSString * plantypeString;
plan.m
//#synthesize the properties of .h
Now in your .m file where you want to parse the data you can do something like:
NSData *data = [soapResultsString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *array = [json allValues];
for (int i=0; i<array.count; i++)
{
planClass *pc = [[planClass alloc]init];
recordResults =NO;
pc.rateString =[[[json valueForKey:#"plan_history"]valueForKey:#"rate"]objectAtIndex:i];
pc.descriptionString=[[[json valueForKey:#"plan_history"]valueForKey:#"description"]objectAtIndex:i];
pc.validityString=[[[json valueForKey:#"plan_history"]valueForKey:#"validity"]objectAtIndex:i];
pc.plantypeString=[[[json valueForKey:#"plan_history"]valueForKey:#"plantype"]objectAtIndex:i];
[appDelegate.arrayPlan addObject:pc];
}
NSLog(#"appDelegate.arrayPlan >> %#",appDelegate.arrayPlan); // you'll get array of planClass objects
You can now access the arrayPlan declared in appDelegate as follows:
for(id *obj in arrayPlan)
{
planClass *pc = (planClass *)obj;
NSLog("rate: %#",[pc valueForKey:#"rateString"]);
NSLog("descriptionString: %#",[pc valueForKey:#"descriptionString"]);
NSLog("validityString: %#",[pc valueForKey:#"validityString"]);
NSLog("plantypeString: %#",[pc valueForKey:#"plantypeString"]);
}
Hope this helps.
you need to store that value in fatalist control means in NSMutable array like this.
NSData *data = [soapResultsString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSMutableArray *arrayHistory = [[NSMutableArray alloc]init];
NSArray *array = [json allValues];
for (int i=0; i<array.count; i++)
{
recordResults =NO;
appDelegate.rateString =[[[json valueForKey:#"plan_history"]valueForKey:#"rate"]objectAtIndex:i];
appDelegate.descriptionString=[[[json valueForKey:#"plan_history"]valueForKey:#"description"]objectAtIndex:i];
appDelegate.validityString=[[[json valueForKey:#"plan_history"]valueForKey:#"validity"]objectAtIndex:i];
appDelegate.plantypeString=[[[json valueForKey:#"plan_history"]valueForKey:#"plantype"]objectAtIndex:i];
[arrayHistory addObject:appDelegate.rateString];
[arrayHistory addObject:appDelegate.descriptionString];
[arrayHistory addObject:appDelegate.validityString];
[arrayHistory addObject:appDelegate.plantypeString];
}
Now use
arrayHistory
to load data in table view

Generate MD5 hash from Objective-C object [duplicate]

This question already has answers here:
MD5 algorithm in Objective-C
(5 answers)
Closed 9 years ago.
I'd like to generate an MD5 hash for an NSObject:
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * type;
#property (nonatomic, retain) NSString * unit;
#property (nonatomic, retain) NSArray * fields;
What is the best way to do so? I've seen examples for hashing from a dictionary or an array, but not from an entire NSObject.
To generate a MD5 hash for an NSObject or a subclass of NSObject, you need to convert it into something that's easily hashable but still represents the state of the instance. A JSON string is one such option. The code looks like this:
Model.h
#import <Foundation/Foundation.h>
#interface Model : NSObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * type;
#property (nonatomic, retain) NSString * unit;
#property (nonatomic, retain) NSArray * fields;
- (NSString *)md5Hash;
#end
Model.m
#import <CommonCrypto/CommonDigest.h>
#import "Model.h"
#implementation Model
- (NSString *)md5Hash
{
// Serialize this Model instance as a JSON string
NSDictionary *map = #{ #"name": self.name, #"type": self.type,
#"unit": self.unit, #"fields": self.fields };
NSError *error = NULL;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:map
options:NSJSONWritingPrettyPrinted
error:&error];
if (error != nil) {
NSLog(#"Serialization Error: %#", error);
return nil;
}
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// Now create the MD5 hashs
const char *ptr = [jsonString UTF8String];
unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH];
CC_MD5(ptr, strlen(ptr), md5Buffer);
NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
[output appendFormat:#"%02x",md5Buffer[i]];
return output;
}
#end
Then you can easily retrieve the MD5 hash just by calling the md5Hash method
Model *obj = [Model new];
obj.name = #"...";
obj.type = #"...";
obj.unit = #"...";
obj.fields = #[ ... ];
NSString *hashValue = [obj md5Hash];
You can convert the object into a dictionary if you already have code for creating the hash:
NSDictionary *dict = [myObject dictionaryWithValuesForKeys:#[#"name", #"type", #"unit", #"fields"]];
Or you could implement <NSCoding> on your class, archive it and hash the resulting data.

How to localize text based on criterion other than language

I have an application which will be marketed in different European countries. We've gone through the process of localizing the application so that its strings are maintained in the language-specific .lproj files in the Settings.bundle. This all works fine. The problem is that there are some strings which don't key off language, but off the country where the app is run. For example, there are strings which differ between the Austrian version of the app and the German version of the app, even though both these countries speak German. When it's run for the first time, the app asks the user which country it's running in.
Is there a way in which I can maintain these country-specific strings in a resource file, and have the resource file used at run time be decided by a user setting, in this case the country where the app is running, rather than the device language?
Thanks,
Peter Hornby
Define two bundles on a singleton, fallback and preferred...
#import <Foundation/Foundation.h>
#interface Localization : NSObject
#property (nonatomic, retain) NSString* fallbackCountry;
#property (nonatomic, retain) NSString* preferredCountry;
#property (nonatomic, retain) NSDictionary* fallbackCountryBundle;
#property (nonatomic, retain) NSDictionary* preferredCountryBundle;
+(Localization *)sharedInstance;
- (NSString*) countryStringForKey:(NSString*)key;
#end
#import "Localization.h"
#implementation Localization
#synthesize fallbackCountryBundle, preferredCountryBundle;
#synthesize fallbackCountry, preferredCountry;
+(Localization *)sharedInstance
{
static dispatch_once_t pred;
static Localization *shared = nil;
dispatch_once(&pred, ^{
shared = [[Localization alloc] init];
[shared setFallbackCountry:#"country-ES"];
NSLocale *locale = [NSLocale currentLocale];
NSString *countryCode = [locale objectForKey:NSLocaleCountryCode];
[shared setPreferredCountry:[NSString stringWithFormat:#"country-%#",countryCode]];
});
return shared;
}
-(void) setFallbackCountry:(NSString*)country
{
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:country ofType:#"strings"];
self.fallbackCountryBundle = [NSDictionary dictionaryWithContentsOfFile:bundlePath];
trace(#"Fallback: %# %#",[bundlePath lastPathComponent], self.fallbackCountryBundle);
}
-(void) setPreferredCountry:(NSString*)country
{
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:country ofType:#"strings"];
self.preferredCountryBundle = [NSDictionary dictionaryWithContentsOfFile:bundlePath];
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:bundlePath isDirectory:nil];
if (!exists) warn(#"%#.strings %#", country, exists ? #"FOUND" : #"NOT FOUND");
trace(#"Preferred: %# %#",[bundlePath lastPathComponent], self.preferredCountryBundle);
}
- (NSString*) countryStringForKey:(NSString*)key
{
NSString* result = nil;
if (preferredCountryBundle!=nil) result = [preferredCountryBundle objectForKey:key];
if (result == nil) result = [fallbackCountryBundle objectForKey:key];
if (result == nil) result = key;
return result;
}
#end
Then call it from a macro function
#define countryString(key) [[Localization sharedInstance]countryStringForKey:key];
Write a default file for ES, and one file per supported language. eg:
/*
country-ES.strings
*/
"hello" = "hello";
And just get the value for the key:
countryString(#"hello");

TableViewController crashes when calling a retained property?

I have a Table View Controller and during its initialisation I set an NSArray property which is then used in the cellForRowAtIndexPath method to display the data on the table.
But, when I touch a row, once I call this retained NSArray property it says EXC_BAD_ACCESS!
FYI the property is defined as shown below, and uses a custom getter function:
#property (nonatomic,retain) NSArray *dataList;
and in the .m file:
#synthesize dataList;
- (NSArray *)dataList
{
if (!dataList)
{
NSString *p = [kind lowercaseString];
NSString *s = [[NSBundle mainBundle] pathForResource:p ofType:#"txt"];
NSLog(#"%#",s);
NSData *dataRep = [NSData dataWithContentsOfFile:s];
NSPropertyListFormat format;
dataList = [NSPropertyListSerialization propertyListFromData: dataRep
mutabilityOption: NSPropertyListImmutable
format: &format
errorDescription: nil];
if (dataList.count == 0)
NSLog(#"Fetch failed!");
}
return dataList;
}
Any suggestions?
This is the problem:
dataList = [NSPropertyListSerialization propertyListFromData ...
This function does not begin with alloc, copy, or retain, therefore it returns an autoreleased object. However, you need it to be retained so that it stays around.
You have two options:
self.dataList = [NSPropertyListSerialization propertyListFromData ...
or,
dataList = [[NSPropertyListSerialization propertyListFromData ...] retain];

Resources