I'm downloading a XML file from an API with URLSessionDataTask.
The XML looks like this:
<?xml version="1.0" encoding="UTF-8" ?>
<ResultList id="12345678-0" platforms="A;B;C;D;E">
<Book id="1111111111" author="Author A" title="Title A" price="9.95" ... />
<Book id="1111111112" author="Author B" title="Title B" price="2.00" ... />
<Book id="1111111113" author="Author C" title="Title C" price="5.00" ... />
<ResultInfo bookcount="3" />
</ResultList>
Sometimes the XML may have thousands of books.
I'm parsing the XML with the SAX parser from Libxml2. While parsing I create a object Book
and set the values from the XML like so:
private func startElementSAX(_ ctx: UnsafeMutableRawPointer?, name: UnsafePointer<xmlChar>?, prefix: UnsafePointer<xmlChar>?, URI: UnsafePointer<xmlChar>?, nb_namespaces: CInt, namespaces: UnsafeMutablePointer<UnsafePointer<xmlChar>?>?, nb_attributes: CInt, nb_defaulted: CInt, attributes: UnsafeMutablePointer<UnsafePointer<xmlChar>?>?) {
let elementName = String(cString: name!)
switch elementName {
case "Book":
let book = buildBook(nb_attributes: nb_attributes, attributes: attributes)
parser.delegate?.onBook(book: book)
default:
break
}
}
func buildBook(nb_attributes: CInt, attributes: UnsafeMutablePointer<UnsafePointer<xmlChar>?>?) -> Book {
let fields = 5 /* (localname/prefix/URI/value/end) */
let book = Book()
for i in 0..<Int(nb_attributes) {
if let localname = attributes?[i * fields + 0],
//let prefix = attributes?[i * fields + 1],
//let URI = attributes?[i * fields + 2],
let value_start = attributes?[i * fields + 3]//,
/*let value_end = attributes?[i * fields + 4]*/ {
let localnameString = String(cString: localname)
let string_start = String(cString: value_start)
//let string_end = String(cString: value_end)
if let end = string_start.characters.index(of: "\"") {
let value = string_start.substring(to: end)
book.setValue(value, forKey: localnameString)
} else {
book.setValue(string_start, forKey: localnameString)
}
}
}
return book
}
In the UITableViewController the onBook(book: Book)
delegate method appends the book object to an array and updates the UITableView. So far so good.
The problem now is, it takes too much RAM of the device and so my device becomes slow. With ~500 books in the XML it takes >500 MB of RAM. I don't know why. When I lookup the RAM in Instruments, I see all the allocated memory in the category _HeapBufferStorage<_StringBufferIVars, UInt16>
With multiple entries greater than 100 KB
In the Event History is the method buildBook()
listed
When I use the XMLParser from Foundation with the constructor XMLParser(contentsOf: URL)
which first downloads the whole XML and then parses it, I have normal RAM usage. No matter how many books. But I want to show the books ASAP in the UITableView. I just want something like Android's XMLPullParser for iOS.