my requirement is parse an xml file and display it in ipad.But i was totally confused how to parse this xml file,i am novice in xml parsing. kindly help me how to create array for it and how to display in ipad.Its a magazine news application ,any source code or sample applicatiom may help me.
There is a built in XML parsing class in iOS called NSXMLParser. As an init parameter it takes an NSData that is usually the data you get back from a web service. Assign yourself as its delegate and call [parser parse] and it will call your delegate when it encounters an element, when it finishes an element, etc. See documentation for the parser and its delegate protocol.
If you have control over the structure of your XML, I've found it easier when it's structured like this:
<Element attr1='value1' attr2='value2' />
instead of:
<Element>
<attr1>value1</attr1>
<attr2>value2</attr2>
</Element>
The reason is that the callback for when it finds an element is:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
where attributes is a dictionary of the values, so in the above example, [attributes objectForKey:#"attr1"] would give you "value1" if it was structured in the first way. If it's the second way, you end up doing lots of state management that's a bit of a pain, especially if you've got elements within elements, etc.
In addition to NSXMLParser thare are open-source parsers that either do everything themselves or wrap the built-in parser. I've written my own wrapper for it before. See here for a post on the subject (thanks to this question for the link).
Related
I'm currently helping a client that needs to change the language in their app due to certain governmental guidelines (medical vs wellness wording). Their app is huge and all the strings are contained in the code i.e. (stringWithFormat/hardcoded), none of it is in an external table. Meaning this would be a huge manual task.
At a undetermined point in the future the client believes they will receive approval to return to their current wording and will want to switch the strings back. Most of the changes will literally be switching a single problematic word to a less problematic word.
I thought that maybe if I could change the strings at run time based on a bool switch that it might eliminate the manual work involved and it would let me switch the language back when needed.
First attempt:
+ (instancetype)stringWithFormat:(NSString *)format, ...
{
va_list args;
va_start(args,format);
//todo check flag if we're changing the language
//todo replace problematic word from 'format'
NSString *result = [NSString stringWithFormat:format,args];
return result;
}
I first quickly coded up a category to override stringWithFormat to replace problematic words. I forgot that I would lose the original implementation of stringWithFormat. This resulted in endless recursion.
Next Attempt (subclass):
I started an attempt to subclass NSString but hit a stackoverflow post saying that if my solution was to subclass a class cluster then I didn't understand my problem because subclassing a class cluster is almost never done.
Final Option (wrapper):
My final attempt would be to write a wrapper class but that kind of defeats the original purpose which was to avoid having to manually seek out each string in the app.
I'm not really sure how to approach this problem anymore. What do I do if I need to add/override functionality to one of the core classes.
There is nothing wrong with the idea of your first attempt, just a little mistake in its implementation. Change:
NSString *result = [NSString stringWithFormat:format,args];
to:
NSString *result = [NSString alloc] initWithFormat:format arguments:args];
which is the expansion of stringWithFormat: and the interception will work.
Thoughts about class clusters are a red herring in this particular situation, the front class for a cluster (NSString) must provide implementations for class methods (+stringWithFormat:), so you can use a simple category to intercept them.
However, having intercepted +stringWithFormat: be careful. A simple test will show you it is used a lot by the frameworks and you do not wish to break them - as my first simple test did by simply changing "d" to "c", which changes "window" to "wincow", which in turn broke the binding setup of Xcode's default app which binds the property "window"...
If you are changing health-related words you might be OK, whole strings would be better.
A better approach might be to simply write a RE to match all the literal strings in the code and replace them by function(string) for some function (not method) you write to do the conversion.
HTH
There is a much simpler solution that seems like a better fit. Use NSLocalizedString, with keys instead of actual strings:
displayString *NSString = NSLocalizedString(#"displayString", nil);
cancelButtonTitle *NSString = NSLocalizedString(#"cancelButtonTitle", nil);
Then create a Localizable.strings file in your app, and define the actual values that should be displayed:
"displayString" = "The string to display in English"
"cancelButtonTitle" = "Cancel"
You put the Localizable.strings file in your app bundle and the app uses it to do string replacements at runtime.
You can also define different versions of Localizable.strings for different languages, so, for example, if the user has set their language to Spanish, the call to NSLocalizedString() gives you the Spanish-language version.
(As you've mentioned, NSString is a class cluster. That means that it is a public interface to a variety of different private subclasses. You can't be sure what private subclass you get when you create an NSString, and that makes it a bad idea to try to subclass it.)
For hardcoded strings you have no other way but to modify those manually by assigning it to a string converter class of some sort. So those for:
yourmom.text = #"Hi Mom";
yourdad.text = [NSString stringWithFormat:#"%# and Dad!",yourmom.text];
You need to change these kind of assignments to something like
yourmom.text = [StringConverter string:#"Hi Mom"];
yourdad.text = [StringConverter string:#"%# and Dad!" placeHolder:yourmom.text];
As for strings in storyboards or xibs, you can change them by iterations loop in viewdidload. Good luck.
I am working on an ePub reader and it is going pretty well so far. But I need some performance running. Currently the used API to convert HTML strings to NSAttributedString objects is Apple's initialization method
- (instancetype)initWithData:(NSData *)data options:(NSDictionary *)options documentAttributes:(NSDictionary **)dict error:(NSError **)error NS_AVAILABLE_IOS(7_0);
The only issue with this method is that, it can't be done in the background. Only on the UI/Main Thread and it takes so much running memory.
Is there some other solution that can tweak my app up a little to enhance the performance and memory utilization?
What you current code does, if by far the most efficient way.
Load html ONCE and parse it to NSAttributedString's once, save them and render inside a custom view.
This is something I have been playing with, and have yet to make my mind up about.
When querying a database, it is extremely common that you will use the data in the response to create custom model objects. Let's use 'Book' as an example.
I have received JSON describing multiple Book objects. I parse the JSON into an NSArray of NSDictionarys. I now have a few options:
Make all properties on Book mutable (I hate this option). You could then have a BookManager class with takes an NSArray of NSDictionarys and maps the data in the dictionary to the correct properties.
Add an initialiser to the Book object which accepts the important details.
Example:
- (instancetype)initWithTitle:(NSString *)title author:(NSString *)author publishDate:(NSDate *)publishDate;
The aforementioned BookManager class could then take the NSDictionarys as before, but create the Book objects with this initialiser. This is nice, because you could then make all of the public facing properties on Book readonly. However, it is very limited, and if (as is often the case) there are a lot of properties on the model, this is not feasible.
Add an initialiser to Book which accepts the NSDictionary representation of itself. I dislike this approach in one way because I feel the responsibility is not the model's to create itself from a dictionary. I prefer the idea of a manager class mapping the dictionary to the model. However, I do like the fact that it means that all properties can be readonly.
There is no doubt in my mind I am missing other options, and if you are aware of them, please point them out. The aim of this question is to finally determine the best way to approach this scenario.
I regularly use an init method with the important arguments, but yes, it becomes very unwieldily when the number of arguments reach double digits and/or several of the arguments can be nil. The longest such method I have seen in the iOS SDK is CLLocation's
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate
altitude:(CLLocationDistance)altitude
horizontalAccuracy:(CLLocationAccuracy)hAccuracy
verticalAccuracy:(CLLocationAccuracy)vAccuracy
course:(CLLocationDirection)course
speed:(CLLocationSpeed)speed
timestamp:(NSDate *)timestamp
Regarding your last option, adding an initWithDictionary: method to Book could be expanded to also include a class-level method for creating instances of Book from an NSDictionary.
+ (instancetype)bookWithDictionary:(NSDictionary *)dictionary
And optionally a convenient way to get a dictionary representation from a Book instance.
- (NSDictionary *)dictionaryRepresentation
If you search the iOS documentation for "withDictionary" and "dictionaryRepresentation" you will see a few places where this is used. In other SDKs, you will sometimes see the method named as someObjectFromDictionary:
I think u can do this using JSONModel
see this
http://code.tutsplus.com/tutorials/getting-started-with-jsonmodel--cms-19840
https://github.com/icanzilb/JSONModel
I'm coding an iOS app and I need to parse some values from an XML file.
I'm parsing the XML using SMXMLDocument and everything works smooth but some tags are returning null value. I noted that those tags are CDATA and after some research I found some methods to workaround this thing. The problem is all those methods refer to NSXMLParser and I can't understand how to do this using SMXMLDocument.
The code in my app is very similar (you could say identical) to the code at this page.
Can you please help me?
Thank you
Apparently SMXMLDocument does not conform to all of the NSXMLParser delegate functions when it parses your document. You are going to have to manually modify SMXMLDocument.m and add the function:
- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock
{
}
Inside that function should look very similar to the foundcharacters function, you just need to take the NSData that you get and convert it into a string. Beware of encoding though, CData data is not always UTF8.
I have researched long and hard about this topic but cannot find a solution. I am very new to iphone app development in xcode so please make the answer comprehensible lol. I followed apple's "Your Second iOS App" tutorial and have the master detail app working perfectly but now I want to save the data the user enters in so that it will appear after resetting the app. The code is the same as what is on apple tutorial here
Thanks in advance!
There are two main decisions you need to make: how you are going to store the data, and when are you going to store/retrieve the data. Let's begin with how to store the data.
How to Store the Data
There are three main approaches you can take: save to a file, save to a SQLite database (either with or without a wrapper such as FMDB), use CoreData. Eventually you will want to master SQLite and CoreData, but for now I recommend taking the first approach.
If you are going to save to/read from a file, you need to decide on the format. There are a number of different options including XML, JSON, your own custom format, etc. In fact, for a "real" project, and depending on the project goals, a format such as JSON is likely to be a good choice. However, I am going to discuss using a serialized archive since a lot of the machinery is already in place.
To store your data in an archive, you will use the NSKeyedArchver to write the data to a file. Later, you will use NSKeyedUnarchiver to retrieve the information from the file. These classes encode/decode your data to/from a byte stream which can then be sent over the wire, written to a file, etc.
Your main data structure is the NSMutableArray named masterBirdSightingList, so the archiver needs to encode the array. NSArray and its mutable counterpart already know how to encode/decode themselves: they just encode/decode each of their elements along with some bookkeeping information. So there is a missing piece to the puzzle. You need to specify how to encode/decode instances of the BirdSighting class.
To specify this, you modify the class so that it implements the NSCoding protocol. In BirdSighting.h, change the #interface declaration to
#interface BirdSighting : NSObject <NSCoding>
Implementing NSCoding is straight-forward. I mentioned above that the various array classes know how to encode/decode themselves, i.e. they already implement the NSCoding protocol. Well to encode/decode a BirdSighting, we just need to encode/decode each of the data members of the class. Looking at the source code, I see they are two NSStrings and an an NSDate. But each of these classes also already implements NSCoding. So in order to encode/decode a BirdSighting instance, we just need to tell each of our instance variables to encode/decode themselves. We do all this work in the methods initWithCoder: and encodeWithCoder: which you add to the BirdSighting class.
Note: one of the details I'm glossing over is the difference between an archiver and a keyed archiver. Usually, you will want to go with a keyed archiver, hence the three macro definitions. And to be honest, I would create NAME_KEY etc., as static NSString constants instead of macros.
#define NAME_KEY #"name"
#define LOCATION_KEY #"location"
#define DATE_KEY #"date"
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
_name = [aDecoder decodeObjectForKey:NAME_KEY];
_location = [aDecoder decodeObjectForKey:LOCATION_KEY];
_date = [aDecoder decodeObjectForKey:DATE_KEY];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.name forKey:NAME_KEY];
[aCoder encodeObject:self.location forKey:LOCATION_KEY];
[aCoder encodeObject:self.date forKey:DATE_KEY];
}
One more note, since BirdSighting inherits directly from NSObject (which does not implement NSCoding), I use self = [super init]. If the parent class did implement NSCoding, you would want to do self = [super initWithCoder:aCoder].
With the above in place, saving to/reading from a file is easy. Somewhere in BirdSightingDataController, probably in initializeDefaultDataList, we insert the following:
masterBirdSightingList = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
where path is an NSString containing the directory path to the archive file and the usual caveats about needing error checking and handling in production code (e.g. what if the file doesn't exist).
Saving the data is easy as well. We can add a method such as the following to the BirdSightingDataController object:
- (BOOL)archiveToPath:(NSString *)path
{
BOOL success = [NSKeyedArchiver archiveRootObject:self.masterBirdSightingList];
return success;
}
When to Store the Data
Now that we have code in place to save and restore the data, we need to decide when we are going to perform these operations. Here I am going to be a little more vague because other issues, such as overall app structure, come into play. However, the app delegate is a likely candidate for managing archiving and unarchiving of the list because of the methods relating to running in the background such as applicationWillResignActive: and applicationWillEnterForeground:.
Deleting the in-memory copy of the sightings list before going into the background is a good thing to do. This suggests putting the code to create the archive in the app delegate. Conversely, you should consider lazy loading the list, i.e. don't retrieve it until you are ready to display it, and this suggests putting the code to retrieve the list in the init method for BirdSightingDataController.
Now your challenge is to do all this without over-coupling and over-complicating your app. But I'll leave that discussion to another Q&A.