After parsing a XML File,i want to create dynamic objects like textbox and buttons based on the contents of xml.
I have parsed my xml and retrieved the data.
I have created a seperate class for XMLParser and made the call in my vieDidload method.
Is it possible to call a method in my mainViewController class from my XMLParser class to create and the dynamic objects.
You can create your own class which will read the parsed value and based on that you go on to create all the GUI Objects.That is not very difficult. Even you can add binding, connections, constraints etc.
I actually did same thing while creating a framework for my project. I cant post the code as its copyright but I can share some of the hits. One basic hint I have mentioned.
well... you can do it with a NSMutableDictionary.
OR you can fill objects that you modeled before from xml
basically just alloc init the existing object and set its properties:
example:
//parse xml
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path]];
parser.delegate = (id<NSXMLParserDelegate>)self;
[parser parse];
e.g. company objects
//callback from parser
- (void)didStartFirmaWithParser:(NSXMLParser*)parser andAttributes:(NSDictionary*)dict {
[[self __didStartEntity:#"RLStoreFirma" withParser:parser] didStartFirmaWithParser:parser andAttributes:dict];
}
//general callback, sets the parsers delegate to THIS new object which then gets filled
- (id)__didStartEntity:(NSString*)name withParser:(AQXMLParser*)parser {
NSEntityDescription *entity = [[store.managedObjectModel entitiesByName] objectForKey:name];
M42StoreEntry *entry = (id)[[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:store.managedObjectContext];
entry.store = self->store;
parser.delegate = entry;
return entry;
}
the object that is to be filled
#import "RLStoreFirma(info).h"
#implementation RLStoreFirma (info)
- (void)didStartFirmaWithParser:(AQXMLParser*)parser andAttributes:(NSDictionary*)dict {
}
- (void)didEndFfkbWithParser:(AQXMLParser*)parser {
self.fkb = currentXMLCharacters;
}
- (void)didEndFfirWithParser:(AQXMLParser*)parser {
self.name = currentXMLCharacters;
}
- (void)didEndFirmaWithParser:(AQXMLParser*)parser {
if(!self.name) {
self.name = self.fkb;
}
[self didEndMainWithParser:(AQXMLParser*)parser];
}
generating new objects at runtime isnt allowed with the iphone sdk / its objc runtime
#pragma mark parser magic
- (SEL) __startSelectorForElement: (NSString *) element
{
NSString * str = nil;
NSMutableString * eSel = [NSMutableString stringWithString: [[element substringWithRange: NSMakeRange(0,1)] uppercaseString]];
if ( [element length] > 1 )
{
[eSel appendString: [element substringFromIndex: 1]];
NSRange range = [eSel rangeOfString: #"-"];
for ( ; range.location != NSNotFound; range = [eSel rangeOfString: #"-"] )
{
NSString * cap = [[eSel substringWithRange: NSMakeRange(range.location+1, 1)] uppercaseString];
range.length += 1;
[eSel replaceCharactersInRange: range withString: cap];
}
}
str = [NSString stringWithFormat: #"didStart%#WithParser:andAttributes:", eSel];
return ( NSSelectorFromString(str) );
}
syntactic sugar to make NSParser nicer.. I cant give you ALL code but I think this might help - i hope
Related
I have written a method that I want to reuse from another method in the same class but I am getting this error message and I don't understand why, how can it be undeclared when it is declared in the same class?
the h file looks like this
#import <Foundation/Foundation.h>
#import "AppDelegate.h"
#import "NWTillHelper.h"
#interface JoshuaWebServices : NSObject
+ (void)downloadDataFromURL:(NSURL *)url withCompletionHandler:(void (^)(NSData *))completionHandler;
- (void)downloadCollections;
#end
And the m file as follows
#import "JoshuaWebServices.h"
#implementation JoshuaWebServices
#synthesize xmlParser;
+ (void)downloadDataFromURL:(NSURL *)url withCompletionHandler:(void (^)(NSData *))completionHandler {
if([NWTillHelper isDebug] == 1) {
NSLog(#"%s entered", __PRETTY_FUNCTION__);
}
// Lots of irrelevant code here
}
- (void)downloadCollections {
// Prepare the URL that we'll get the neighbour countries from.
NSString *URLString = [NSString stringWithFormat:#"https://url.is.not.here"];
NSURL *url = [NSURL URLWithString:URLString];
// Download the data.
[downloadDataFromURL:url withCompletionHandler:^(NSData *data) {
// Make sure that there is data.
if (data != nil) {
self.xmlParser = [[NSXMLParser alloc] initWithData:data];
self.xmlParser.delegate = self;
// Initialize the mutable string that we'll use during parsing.
self.foundValue = [[NSMutableString alloc] init];
// Start parsing.
[self.xmlParser parse];
}
}];
}
Why can I not use the method declared in same class?
Your method needs a receiver. Unlike functions that can just be called on there own. Methods must be called by something, either the class, or an instance of a class. In your case you should use a class because it's a class method.
Change
[downloadDataFromURL:url withCompletionHandler:^(NSData *data) {
// Make sure that there is data.
if (data != nil) {
self.xmlParser = [[NSXMLParser alloc] initWithData:data];
self.xmlParser.delegate = self;
// Initialize the mutable string that we'll use during parsing.
self.foundValue = [[NSMutableString alloc] init];
// Start parsing.
[self.xmlParser parse];
}
}];
to be
[JoshuaWebServices downloadDataFromURL:url withCompletionHandler:^(NSData *data) {
// Make sure that there is data.
if (data != nil) {
self.xmlParser = [[NSXMLParser alloc] initWithData:data];
self.xmlParser.delegate = self;
// Initialize the mutable string that we'll use during parsing.
self.foundValue = [[NSMutableString alloc] init];
// Start parsing.
[self.xmlParser parse];
}
}];
I feel like I have read many (simple) examples that do exactly what I am trying to do. I just cannot seem to get this to work. I need a second eye on my code, and I don't have anyone around, so pardon me if this seems very simple... The code compiles without a problem. Thank you!
#implementation Engine
- (id) initWithInventory: (NSString *) path {
if (self = [super init]) {
NSString *contents = [NSString stringWithContentsOfFile:#"ingredientList.csv" encoding:NSASCIIStringEncoding error:nil];
NSLog(#"%#",contents); // This yields the contents of the file appropriately
NSArray *lines = [contents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSRange ingredientRange = {0,96}; // This is done because I want to omit the last element of the array... the 97th is an empty string caused by the end of file newline character. I know it's bad coding...
NSEnumerator *enumerator = [[lines subarrayWithRange:ingredientRange] objectEnumerator];
NSString *curString;
NSArray *ingredientElements;
NSRange activeEffectRange = {1,4}; // Element 0 will be the key, elements 1-4 are the array to be stored.
while (curString = [enumerator nextObject]) {
ingredientElements = [curString componentsSeparatedByString:#","];
Ingredient *theIngredient = [[Ingredient alloc] initWithName:[ingredientElements objectAtIndex:0] andActiveEffects:[ingredientElements subarrayWithRange:activeEffectRange]];
NSLog(#"%#",[theIngredient ingredientName]);
NSLog(#"%#",[theIngredient activeEffects]); //These both print out correctly.
NSString *theName = [theIngredient ingredientName];
[allIngredients setObject:theIngredient forKey:theName];
NSLog(#"%#",[allIngredients objectForKey:[theIngredient ingredientName]]); // ***This yields (null)***
}
}
return self;
}
EDIT: I should add, that allIngredients is an instance variable of the class being initiated, so it is defined properly as an NSMutableDictionary:
#interface Engine : NSObject {
NSMutableDictionary *allIngredients;
}
- (id) initWithInventory: (NSString *) path;
#end
Where are you creating allIngredients? You've declared it, but you haven't allocated it before you use it.
allIngredients = [[NSMutableDictionary alloc] init]
I'm trying to parse a very simple json object with 1 value. (ocUnit test below)
- (void) testHatCartParseWithValidRefId {
NSString* data = #"{\"refid\":999}";
Cart* obj = [HatCartParseJson parseJsonAndReturnObject:data];
STAssertTrue([obj.refid isEqualToString:#"999"], #"fail");
}
In the implementation everything fails when I add the line to pull it from either key or index. How should I pull this from the json input? Please keep in mind I need this json parse (not string) the actual code I'm working with is a large set of JSON data.
+ (Cart *) parseJsonAndReturnObject:(NSString *)json
{
NSArray* cart = [json JSONValue];
for (NSDictionary* item in cart) {
Cart* obj = [[Cart alloc] init];
//NSString* refid = [item objectAtIndex:0];
//NSString* refid = [item objectForKey:#"refid"];
[obj setRefid:#"999"];
return obj;
}
return nil;
}
Thank you in advance
You are expecting that the return value of JSONValue is an NSArray, which in this case it isn't.
So, you must do a check if the return value is actually an NSArray, and if it is, then iterate through the collection, otherwise check if it's an NSDictionary, and if it is, then return the Cart object with the refid from the NSDictionary. If all of this fails, then just return nil.
As a side point, according to Apple's Object Ownership Policy, you should return autorelease-d objects from methods whose names do not contain the words "alloc", "new" or "copy". This would be one such method where you'd return an autorelease-d object.
+ (Cart *) parseJsonAndReturnObject:(NSString *)json
{
id cart = [json JSONValue];
NSString* refid = nil;
if([cart isKindOfClass:[NSArray class]]) {
refid = [[cart objectAtIndex:0] objectForKey:#"refid"];
} else if([cart isKindOfClass:[NSDictionary class]]) {
refid = [cart objectForKey:#"refid"];
}
if(refid) {
Cart* c = [[Cart alloc] init];
[c setRefid:refid];
return [c autorelease];
}
return nil;
}
I am relatively new to Objective-C and now I have a problem in my iPhone app that I don't fully understand.
I try to use a NSMutableDictionary, this does not seem to work as i expect for some reason. When I run the debugger and do po numberToCallerMap to see the dictionary, I get an exception. I have read the documentation for NSMutableDictionary on how to initialize it, but I can not see what I am doing wrong. Help and advice are appreciated. The variable causing me problem is numberToCallerMap, here is the relevant function:
- (void)setData:(NSString*)value{
[list release];
list = [[NSMutableArray alloc] init];
SBJSON *json = [[[SBJSON alloc] init] autorelease];
NSMutableDictionary* numberToCallerMap;
CallerInfo* caller;
NSDictionary* callerInfo;
#try {
NSArray *array = (NSArray*)[json objectWithString:value];
// reading all the items in the array one by one
numberToCallerMap = [NSMutableDictionary dictionary];
for (id *item in array) {
// if the item is NSDictionary (in this case ... different json file will probably have a different class)
NSDictionary *dict2 = (NSDictionary *) item;
CallInfo *data = [CallInfo alloc];
[data initFromDictionary:dict2];
callerInfo = (NSDictionary*)[dict2 valueForKey:#"caller"] ;
//Here, we want the phonenumber to be part of the CallerInfo object instead.
// It is sent from the server as part of the Call-object
NSString* number = (NSString*)[dict2 valueForKey:#"phoneNumber"];
[callerInfo setValue:number forKey:#"phoneNumber"];
caller = (CallerInfo*)[numberToCallerMap valueForKey:number];
if(caller == nil || [caller isKindOfClass:[NSNull class]]){
caller = [CallerInfo alloc];
[caller initFromDictionary:callerInfo];
[numberToCallerMap setValue:caller forKey:number];
[list insertObject:caller atIndex:0];
}
[caller addRecentCall:data];
}
}
#catch (NSException * e) {
[list release];
list = [[NSMutableArray alloc] init];
}
#finally {
[numberToCallerMap release];
}
}
This is probably not the only problem, but you are not alloc-ing your numberToCallerMap dictionary, you are getting it from a convenience class method -- [NSMutableDictionary dictionary] -- that returns it autoreleased. So you should not call release on it yourself.
Can you show me the syntax or any sample programs to archive an NSArray of custom objects in Objective-C?
Check out NSUserDefaults.
For Archiving your array, you can use the following code:
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:myArray] forKey:#"mySavedArray"];
And then for loading the custom objects in the array you can use this code:
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *savedArray = [currentDefaults objectForKey:#"mySavedArray"];
if (savedArray != nil)
{
NSArray *oldArray = [NSKeyedUnarchiver unarchiveObjectWithData:savedArray];
if (oldArray != nil) {
customObjectArray = [[NSMutableArray alloc] initWithArray:oldSavedArray];
} else {
customObjectArray = [[NSMutableArray alloc] init];
}
}
Make sure you check that the data returned from the user defaults is not nil, because that may crash your app.
The other thing you will need to do is to make your custom object to comply to the NSCoder protocol. You could do this using the -(void)encodeWithCoder:(NSCoder *)coder and -(id)initWithCoder:(NSCoder *)coder methods.
If you want to save to a file (rather than using NSUserDefaults) you can use -initWithContentsOfFile: to load, and -writeToFile:atomically: to save, using NSArrays.
Example:
- (NSArray *)loadMyArray
{
NSArray *arr = [NSArray arrayWithContentsOfFile:
[NSString stringWithFormat:#"%#/myArrayFile", NSHomeDirectory()]];
return arr;
}
// returns success flag
- (BOOL)saveMyArray:(NSArray *)myArray
{
BOOL success = [myArray writeToFile:
[NSString stringWithFormat:#"%#/myArrayFile", NSHomeDirectory()]];
return success;
}
There's a lot of examples on various ways to do this here: http://www.cocoacast.com/?q=node/167