URLSession.shared.dataTaskPublisher - 如何将收到的值转换为字符串?

URLSession.shared.dataTaskPublisher - how to convert received value to string?

作为 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)")
            }
        }

但是我得到语法错误:

Cannot convert value of type 'String' to expected argument type 'Data.Element' (aka 'UInt8')

请帮助我了解发生了什么。

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
}

从您的错误消息来看,handleOutput 似乎正在发布 Data。如果你想调用字符串函数,你可以将其映射到 String,例如

var cancellable: AnyCancellable?

func foo(_ url: URL) {
    cancellable = URLSession.shared.dataTaskPublisher(for: url)
        .tryMap(handleOutput)
        .compactMap { String(data: [=10=], 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
            ...
        }
}