Разбирайте 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

Более того этот может вам помочь.