URLSession.shared.dataTaskPublisher — как преобразовать полученное значение в строку?

#ios #swift #string #csv #combine

Вопрос:

Будучи новичком в Swift, я пытаюсь загрузить и проанализировать CSV-файл с помощью кода:

 URLSession.shared.dataTaskPublisher(for: url)
    .tryMap(handleOutput)
    .sink { completion in
    } receiveValue: { csvWords in

        let lines = csvWords.split(separator: "n")
        for line in lines {
            let columns = line.split(separator: ",")
            for column in columns {
                print("column: (column)")
            }
        }
 

однако я получаю синтаксическую ошибку:

Не удается преобразовать значение типа «Строка» в данные типа «ожидаемый аргумент».Элемент» (он же «UInt8»)

Xcode

Пожалуйста, помогите мне понять, что происходит.

Какое значение возвращается значением receiveValue, разве это не содержимое удаленного файла в виде строки?

Обновить:

Вот недостающий метод

 func handleOutput(output: URLSession.DataTaskPublisher.Output) throws -> Data {
    guard
        // as? means "this might be nil"
        let response = output.response as? HTTPURLResponse,
        response.statusCode >= 200,
        response.statusCode < 300
        else {
            throw URLError(.badServerResponse)
        }
    
    return output.data
}
 

Комментарии:

1. Показать handleOutput . Иначе как мы узнаем, что такое csvWords??? Но нет, то, что поступает из сети, является данными до тех пор, пока вы не преобразуете их в строку.

2. Хорошо, я добавил handleOutput к своему вопросу извинения за то, что пропустил

3. Ага, вот и ты. Ты возвращаешься return output.data . Это данные. Так вот что проходит по трубопроводу в раковину.

4. Нет никакого «должен». Лично я бы просто сделал именно то, что советует Robs answer: добавьте оператора компактной карты. В любом случае, то, что вы здесь делаете, сильно перегружает раковину; раковина не должна обрабатывать, а только получать.

5. Да! Вот как бы я это сделал. Но многое зависит от того, какие значения вы хотите получить в конце конвейера.

Ответ №1:

Судя по вашему сообщению об ошибке, handleOutput похоже, что это публикация Data . Если вы хотите вызывать строковые функции, вы должны сопоставить это String , например,

 var cancellable: AnyCancellable?

func foo(_ url: URL) {
    cancellable = URLSession.shared.dataTaskPublisher(for: url)
        .tryMap(handleOutput)
        .compactMap { String(data: $0, encoding: .utf8) }
        .sink { completion in
            ...
        } receiveValue: { string in
            ...
        }
}
 

Или, если вы хотите, вы можете выдать ошибку, если она не может быть преобразована в строку:

 var cancellable: AnyCancellable?

func foo(_ url: URL) {
    cancellable = URLSession.shared.dataTaskPublisher(for: url)
        .tryMap(handleOutput)
        .tryMap { data -> String in
            guard let string = String(data: data, encoding: .utf8) else {
                throw URLError(.badServerResponse)
            }
            return string
        }
        .sink { completion in
            ...
        } receiveValue: { string in
            ...
        }
}
 

В своем пересмотренном вопросе вы поделились handleOutput . Вы могли бы просто изменить это, чтобы создать свой String для вас:

 func handleOutput(output: URLSession.DataTaskPublisher.Output) throws -> String {
    guard
        let response = output.response as? HTTPURLResponse,
        200 ..< 300 ~= response.statusCode,
        let string = String(data: output.data, encoding: .utf8)
    else {
        throw URLError(.badServerResponse)
    }

    return string
}
 

И тогда вам не понадобится эта дополнительная compactMap / tryMap :

 func foo(_ url: URL) {
    cancellable = URLSession.shared.dataTaskPublisher(for: url)
        .tryMap(handleOutput)
        .sink { completion in
            ...
        } receiveValue: { string in
            ...
        }
}