Разбирайте XML файл в кусках, не дожидаясь полной загрузки в Swift
В iOS я хотел бы проанализировать поток XML с сервера во время загрузки. Не нужно ждать, пока сервер не закончит создание XML, и загрузка будет завершена. Сервер создает XML в "кусках" и отправляет его непосредственно клиенту. В моем приложении у меня есть UITableView
, который должен немедленно отображать элементы XML, как только я получил его с сервера.
Я пробовал его с конструктором XMLParser(contentsOf: URL)
, но сначала загружает весь XML, а затем анализирует его. Существует еще один конструктор XMLParser(stream: InputStream)
, но я не знаю, как получить InputStream
от URLConnection
. Единственное, что я нашел, это этот вопрос, которому почти 5 лет, и я не мог понять, как это сделать в Swift 3, если он даже работает.
Другая вещь, которую я пробовал, была через Libxml2, но там у меня проблемы с использованием ОЗУ или другими вещами (см. мой старый вопрос).
Как я могу разобрать XML-поток в кусках, не дожидаясь полной загрузки в Swift?
В Android я использовал бы XMLPullParser
и имел бы:
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
Ответы
Ответ 1
Возможно, у клиента/сервера возникла проблема с некоторыми cookie-сессиями? Попробуйте удалить их после каждого запроса:
// Loops through each of the cookies and deletes them.
let cookieStore = HTTPCookieStorage.shared
for cookie in cookieStore.cookies ?? [] {
cookieStore.deleteCookie(cookie)
}
Ответ 2
В основном есть два типа синтаксического анализатора: SAX и DOM. В соответствии с вашим требованием, когда вы обновляете UITableView сразу после получения XML-элемента, совершенно ясно, что вам придется использовать только синтаксический анализатор SAX. (Парсер DOM проанализировал весь документ и создал представление в памяти, которое вы можете запросить для разных элементов).
В iOS есть два популярных анализатора SAX:
1) NSXMLParser (он был переименован в XMLParser) - включен по умолчанию с iPhone SDK.
2) libxml2 - это библиотека с открытым исходным кодом, которая по умолчанию включена в iPhone SDK
Apple сделала отличный code образец под названием XMLPerformance, который позволяет сравнить время, затрачиваемое на анализ XML-документа размером 900 КБ, содержащего Топ 300 песен iTunes с API NSXML и libxml2.
Хорошо, heres график, который показывает использование пиковой памяти парсером (это было получено путем запуска различных методов с помощью инструмента Object Allocations)
![a busy cat]()
Данные ясно показывают, что метод SAX libxml2s (который вы уже пробовали) является наилучшим вариантом использования пиковой памяти.
С другой стороны, внедрение XMLParser довольно просто. Ниже приведен фрагмент моего примера, который я попытался использовать.
В объявлении вашего класса реализуйте протокол XMLParserDelegate
class ViewController: UIViewController,XMLParserDelegate {
override func viewDidLoad() {
super.viewDidLoad()
Используйте метод ниже для инициализации анализатора:
func xmlParser()
{
posts = []
parser = XMLParser(contentsOf:(NSURL(string:"http://images.apple.com/main/rss/hotnews/hotnews.rss"))! as URL)!
parser.delegate = self
parser.parse()
}
После инициализации делегата ниже методы делегата будут вызваны как и когда необходимо:
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")
}
Существуют и другие методы делегата, которые вы можете вызывать в соответствии с вашими требованиями.
Ответ 3
Решение ниже может помочь вам. Я изучил ваш вопрос и преуспел в том, что в результате ниже. Но я не могу проверить нижеприведенное отсутствие API. Попробуйте использовать доступные конечные точки. Надеюсь, это поможет вам.
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
Более того этот может вам помочь.