Словарь NSNetServiceFromTXTRecord терпит неудачу в утверждении о недопустимом вводе

Входные данные для dictionary(fromTXTRecord:) поступают из сети, потенциально извне приложения или даже устройства. Однако документы Apple сказать:

... Сбой утверждения, если txtData не может быть представлен как объект NSDictionary.

Невыполнение утверждения оставляет программиста (меня) без возможности обработки ошибки, что кажется нелогичным для метода, обрабатывающего внешние данные.

Если я запускаю это в Терминале на Mac:

dns-sd -R 'My Service Name' _myservice._tcp local 4567 asdf asdf

мое приложение, работающее на iPhone, дает сбой.

dictionary(fromTXTRecord:) ожидает, что данные записи TXT (asdf asdf) будут иметь форму key=val. Если, как и выше, слово не содержит =, метод не сможет его разобрать и не выполнить утверждение.

Я не вижу другого способа решить эту проблему, кроме как вообще не использовать этот метод и реализовать свой собственный синтаксический анализ, что кажется неправильным.

Я что-то упускаю?


person ateijelo    schedule 22.10.2016    source источник
comment
Вот ссылка на довольно хорошее решение, которое я использовал: lapcatsoftware.com/articles/netservice-nuthouse. html   -  person polech    schedule 09.06.2020


Ответы (3)


Вот решение в Swift 4.2, предполагая, что запись TXT содержит только строки:

/// Decode the TXT record as a string dictionary, or [:] if the data is malformed
public func dictionary(fromTXTRecord txtData: Data) -> [String: String] {

    var result = [String: String]()
    var data = txtData

    while !data.isEmpty {
        // The first byte of each record is its length, so prefix that much data
        let recordLength = Int(data.removeFirst())
        guard data.count >= recordLength else { return [:] }
        let recordData = data[..<(data.startIndex + recordLength)]
        data = data.dropFirst(recordLength)

        guard let record = String(bytes: recordData, encoding: .utf8) else { return [:] }
        // The format of the entry is "key=value"
        // (According to the reference implementation, = is optional if there is no value,
        // and any equals signs after the first are part of the value.)
        // `ommittingEmptySubsequences` is necessary otherwise an empty string will crash the next line
        let keyValue = record.split(separator: "=", maxSplits: 1, omittingEmptySubsequences: false)
        let key = String(keyValue[0])
        // If there's no value, make the value the empty string
        switch keyValue.count {
        case 1:
            result[key] = ""
        case 2:
            result[key] = String(keyValue[1])
        default:
            fatalError()
        }
    }

    return result
}
person spudwaffle    schedule 07.09.2018

Я все еще надеюсь, что я что-то здесь упускаю, но тем временем я проверил данные на правильность и только потом вызвал собственный метод Apple.

Вот мой обходной путь:

func dictionaryFromTXTRecordData(data: NSData) -> [String:NSData] {
    let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(data.bytes), count: data.length)
    var pos = 0
    while pos < buffer.count {
        let len = Int(buffer[pos])
        if len > (buffer.count - pos + 1) {
            return [:]
        }
        let subdata = data.subdataWithRange(NSRange(location: pos + 1, length: len))
        guard let substring = String(data: subdata, encoding: NSUTF8StringEncoding) else {
            return [:]
        }
        if !substring.containsString("=") {
            return [:]
        }
        pos = pos + len + 1
    }
    return NSNetService.dictionaryFromTXTRecordData(data)
}

Я использую Swift 2 здесь. Все вклады приветствуются. Версии Swift 3, версии Objective-C, улучшения, исправления.

person ateijelo    schedule 07.11.2016

Я только что столкнулся с этим, используя Swift 3. В моем случае проблема возникла только при использовании NetService.dictionary(fromTXTRecord:), но не возникла, когда я переключился на Objective-C и вызвал NSNetService dictionaryFromTXTRecord:. Когда вызов Objective-C встречает запись без знака равенства, он создает ключ, содержащий данные, и помещает его в словарь со значением NSNull. Из того, что я могу сказать, версия Swift затем перечисляет этот словарь и приходит в ярость, когда видит NSNull. Мое решение состояло в том, чтобы добавить файл Objective-C и служебную функцию, которая вызывает dictionaryFromTXTRecord: и очищает результаты, прежде чем передать их обратно в мой код Swift.

person Belden Fox    schedule 29.12.2016
comment
Хороший звонок. Я не думал о том, чтобы попробовать с Objective-C. Но, просто чтобы повторить мой вопрос, не является ли это ошибкой со стороны Apple? - person ateijelo; 29.12.2016
comment
Такое поведение делает Swift-версию этого вызова бесполезной, поэтому это похоже на ошибку. С другой стороны, неясно, как Apple это исправит. NSNull нельзя перенести в словарь Swift типа [String:Data]. Его нельзя превратить в ноль, потому что это эквивалентно удалению ключа. Его нельзя сопоставить с пустым объектом Data без потери различия между hello и hello=; в Objective-C первое возвращает значение NSNull, а второе возвращает значение пустого объекта NSData. Так что Apple просто опустила руки на это. - person Belden Fox; 30.12.2016