What is it?
IGHTMLQuery is a lightweight XML/HTML parser for iOS, built on top of libxml. It is inspired by jQuery and nokogiri. Consider following snippets:
IGXMLDocument* node = [[IGXMLDocument alloc] initWithXMLString:catelogXml error:nil]; NSString* title = [[[node queryWithXPath:@"//cd/title"] firstObject] text]; [[node queryWithXPath:@"//title"] enumerateNodesUsingBlock:^(IGXMLNode *title, NSUInteger idx, BOOL *stop) { NSLog(@"title = %@", title.text); }]; // or use CSS Selector [[node queryWithCSS:@"title"] enumerateNodesUsingBlock:^(IGXMLNode *title, NSUInteger idx, BOOL *stop) { NSLog(@"title = %@", title.text); }]; // quick manipulation [[node queryWithXPath:@"//title"] appendWithXMLString:@"<message>Hi!</message>"];
Features
- Use XPath and CSS Selector for document searching.
- jQuery style chainable syntax.
- XML traversal and manipulation.
Installation
IGHTMLQuery is available through CocoaPods, to install it simply add the following line to your Podfile:
pod "IGHTMLQuery", "~> 0.8.4"
Alternatively:
- Add all the source files in
Classesto your Xcoe project - In "Build Phases" > "Link Binary With Libraries, add
libxml2. - In "Build Setting", find "Header Search Paths" and add "$(SDK_DIR)/usr/include/libxml2"
Usage
Import header
For each files using IGHTMLQuery, import IGHTMLQuery.h:
#import 'IGHTMLQuery.h'
Create a document
Create a XML document:
IGXMLDocument* node = [[IGXMLDocument alloc] initWithXMLString:xml error:nil];
Create a HTML document:
IGHTMLDocument* node = [[IGHTMLDocument alloc] initWithHTMLString:html error:nil];
Traversal
Use parent, nextSibling, previousSibling, children and firstChild to traverse the document.
Query using XPath or CSS Selector
You can query the document or any node with queryWithXPath: or queryWithCSS: methods. They will always return a IGXMLNodeSet object, which is a set like object that you can chain query and operations.
IGXMLNodeSet* contents = [doc queryWithXPath:@"//div[@class='content']"]; [contents enumerateNodesUsingBlock:^(IGXMLNode* content, NSUInteger idx, BOOL *stop){ NSLog(@"%@", content.xml); }]; // use a @try/@catch block for queryWithCSS, as it can throw an exception if // the CSS Selector cannot be converted to XPath. @try { contents = [doc queryWithCSS:@"div.content"]; [contents enumerateNodesUsingBlock:^(IGXMLNode* content, NSUInteger idx, BOOL *stop){ NSLog(@"%@", content.xml); }]; } @catch(NSException * e) { // handle error }
Document Manipulation
You can change the document using methods in IGXMLNodeManipulation protocol.
@protocol IGXMLNodeManipulation <NSObject> -(instancetype) appendWithNode:(IGXMLNode*)child; -(instancetype) prependWithNode:(IGXMLNode*)child; -(instancetype) addChildWithNode:(IGXMLNode*)child; -(instancetype) addNextSiblingWithNode:(IGXMLNode*)child; -(instancetype) addPreviousSiblingWithNode:(IGXMLNode*)child; -(void) empty; -(void) remove; @end
JavaScript/Ruby support
All classes in IGHTMLQuery supports JavaScriptCore exports. Additionally there are Ruby wrappers to used with JavaScriptCoreOpalAdditions, which allow you to manipulate DOM with Ruby in Objective-C like this ...
#import "JSContext+IGHTMLQueryRubyAdditions.h" #import "JSContext+OpalAdditions.h" // load IGHTMLQuery ruby wrapper classes [context configureIGHTMLQuery]; // create a lambda that evalulate script on the fly JSValue* instanceEval = [context evaluateRuby:@"lambda { |doc, script| XMLNode.new(doc).instance_eval(&eval(\"lambda { #{script} }\")) }"]; // a simple script that find the title of first cd have a price less than 9.0 JSValue* node = [instanceEval callWithArguments:@[doc, @"self.xpath('//cd').find {|node| node.xpath('./price').text.to_f < 9.0 }.xpath('./title').text"]]; // convert the result to string NSString* title = [node toString]; XCTAssertEqualObjects((@"Greatest Hits"), title, @"title should be Greatest Hits");
To use IGHTMLQuery with Ruby support, add following line to your Podfile:
pod "IGHTMLQuery/Ruby"
See Test Cases for more detail.
Breaking Changes
0.7.2
In previous version, the method [[IGHTMLDocument alloc] initWithHTMLString:] will create html element without implied HTML tag. (HTML_PARSE_NOIMPLIED option in libxml). Since 0.7.2, HTML_PARSE_NOIMPLIED will no longer be the default.
If you want to maintain the old behavior, check the [[IGHTMLDocument alloc] initWithHTMLFragmentString:] method.
License
MIT License.