In iOS, I would like to parse an XML stream from the server while downloading. It should not have to wait until the server is done building the XML and the download is complete. The server builds XML in "chunks" and sends it directly to the client. In my app I have a UITableView
which should instantly show the elements of the XML as soon as I received it from the server.
I've tried it with the XMLParser(contentsOf: URL)
constructor, but this first downloads the whole XML and then parses it. There is another constructor XMLParser(stream: InputStream)
, but I don't know how to get an InputStream
from a URLConnection
. The only thing I found was this question which is almost 5 years old, and I couldn't understand it how to do this in Swift 3, if it even works.
The other thing I tried was via Libxml2, but there I have problems with RAM usage or other things (see my old question).
How can I parse an XML stream in chunks without having to wait for a complete download in Swift?
In Android, I would use an XMLPullParser
and have:
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setDoInput(true);
InputStream inputStream = connection.getInputStream();
XmlPullParserFactory xmlFactoryObject = XmlPullParserFactory.newInstance();
XmlPullParser pullParser = xmlFactoryObject.newPullParser();
pullParser.setInput(inputStream, null);
// here comes the actual parsing
There are basically two type of parser: SAX and DOM. As per your requirement, where you are updating UITableView as soon as XML element received, It is perfectly clear that you will have to use SAX parser only. (DOM parser parsed the entire document and builds up an in-memory representation that you can query for different elements).
There are two popular SAX parser for iOS as listed below:
1)NSXMLParser (This has been renamed to XMLParser) - included by default with the iPhone SDK.
2)libxml2 - is an Open Source library that is included by default with the iPhone SDK
Apple has made an excellent code sample called XMLPerformance that allows you to compare the time it takes to parse a ~900KB XML document containing the top 300 iTunes songs with both the NSXML and libxml2 APIs.
Ok, here’s a graph that shows the peak memory usage by parser (this was obtained through running the various methods through the Object Allocations tool)
Data clearly shows that libxml2’s SAX method (Which you already tried) is the best option as far as peak memory usage is concerned.
On the other hand, implementing XMLParser is quite easy. Below is my example snippet which i tried using it.
In your class declaration, implement the XMLParserDelegate protocol
class ViewController: UIViewController,XMLParserDelegate {
override func viewDidLoad() {
super.viewDidLoad()
Use below method to initialize the parser:
func xmlParser()
{
posts = []
parser = XMLParser(contentsOf:(NSURL(string:"http://images.apple.com/main/rss/hotnews/hotnews.rss"))! as URL)!
parser.delegate = self
parser.parse()
}
Once you initialize the delegate, below delegate methods will get called as and when needed:
func parserDidStartDocument(_ parser: XMLParser) {
print("started")
}
func parserDidEndDocument(_ parser: XMLParser) {
print("ended")
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
print("didEndElement")
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
print("foundCharacters")
}
There are other delegate methods as well which you can call according to your requirements.
Maybe the client/server has a problem with some Session-Cookies? Try to delete them after each request:
// Loops through each of the cookies and deletes them.
let cookieStore = HTTPCookieStorage.shared
for cookie in cookieStore.cookies ?? [] {
cookieStore.deleteCookie(cookie)
}
The below solution may help you out. I have explored about your question and succeed some what as a result below. But I can't able to test the below solution lack of API's. Try with your available Endpoints. Hope it will help you.
func xmlStream() {
let task =
URLSession.shared.streamTask(withHostName: "chat.example.com", port: 5555)
task.readData(ofMinLength: 16384, maxLength: 65536, timeout: 30.0) { (data, eof, error) in
let parser = XMLParser(data: data!)
parser.delegate = self
if parser.parse() {
//print result
}
}
task.resume()
}
//XML parser methods
Moreover this may guide you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With