合并从模型到 viewModel 的转换会给出错误“当 运行 .eraseToAnyPublisher() 时表达式类型不明确且没有更多上下文
Combine conversion from model to viewModel gives error "Type of expression is ambiguous without more context when running .eraseToAnyPublisher()
我想弄清楚为什么最后一个 .eraseToAnyPublisher() 会出现上述错误,对我来说似乎所有类型都已明确指定,不是吗?
static func searchUsers(query: String) -> AnyPublisher<[UserViewModel], Never> {
// prepare URL
let urlString = "\(baseURL)/search/users?q=\(query)"
guard let url = URL(string: urlString) else {
return Just([]).eraseToAnyPublisher()
}
// get handle of native data task publisher
let publisher = URLSession.shared.dataTaskPublisher(for: url)
.handleEvents(
receiveSubscription: { _ in
activityIndicatorPublisher.send(true)
}, receiveCompletion: { _ in
activityIndicatorPublisher.send(false)
}, receiveCancel: {
activityIndicatorPublisher.send(false)
})
.tryMap { data, response -> Data in
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw NetworkError.httpError
}
print(String(data: data, encoding: .utf8) ?? "")
return data
}
.decode(type: SearchUserResponse.self, decoder: JSONDecoder())
.map { [=11=].items }
.flatMap({ users in
var userViewModels = [UserViewModel]()
users.forEach { user in
userViewModels.append(contentsOf: UserViewModel(with: user))
}
return userViewModels
})
.catch { err -> Just<[UserViewModel]> in
print(err)
return Just([])
}
.eraseToAnyPublisher() // <-- HERE IS THE ERROR
return publisher
}
不幸的是,对于那些复杂的 Combine
管道,有时编译器错误会显示在错误的行上。在你的情况下有两个问题,但不是编译器指向的地方。
一个是使用 flatMap
而不是 map
。
管道的这一部分:
let publisher = URLSession.shared.dataTaskPublisher(for: url)
.handleEvents(
receiveSubscription: { _ in
activityIndicatorPublisher.send(true)
}, receiveCompletion: { _ in
activityIndicatorPublisher.send(false)
}, receiveCancel: {
activityIndicatorPublisher.send(false)
})
.tryMap { data, response -> Data in
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw NetworkError.httpError
}
print(String(data: data, encoding: .utf8) ?? "")
return data
}
.decode(type: SearchUserResponse.self, decoder: JSONDecoder())
.map { [=10=].items }
returns Publisher<[User], Error>
.
接下来您要将其转换为 Publisher<[UserViewModel], Error>
,为此您需要一个函数 map
:
func map<T>(_ transform: @escaping (Output) -> T) -> Publishers.Map<Upstream, T>
将一种类型的 Output
转换为另一种类型的 Output
而不是 flatMap
:
func flatMap<T, P>(maxPublishers: Subscribers.Demand = .unlimited, _ transform: @escaping (Self.Output) -> P) -> Publishers.FlatMap<P, Self> where T == P.Output, P : Publisher, Self.Failure == P.Failure
将 Output
转换为新的 Publisher
第二个问题是 append(contentsOf;)
,它需要 Sequence
个元素,如果是单个元素,您应该使用 append()
,但更简单的方法是映射 [User]
到 [UserViewModel]
:
{ users in
users.map { user in
UserViewModel(with: user)
}
}
所以整个功能应该适用于这些更改:
static func searchUsers(query: String) -> AnyPublisher<[UserViewModel], Never> {
// prepare URL
let urlString = "\(baseURL)/search/users?q=\(query)"
guard let url = URL(string: urlString) else {
return Just([]).eraseToAnyPublisher()
}
// get handle of native data task publisher
let publisher = URLSession.shared.dataTaskPublisher(for: url)
.handleEvents(
receiveSubscription: { _ in
activityIndicatorPublisher.send(true)
}, receiveCompletion: { _ in
activityIndicatorPublisher.send(false)
}, receiveCancel: {
activityIndicatorPublisher.send(false)
})
.tryMap { data, response -> Data in
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw NetworkError.httpError
}
print(String(data: data, encoding: .utf8) ?? "")
return data
}
.decode(type: SearchUserResponse.self, decoder: JSONDecoder())
.map { [=14=].items }
.map { users in
users.map { user in
UserViewModel(with: user)
}
}
.catch { err -> Just<[UserViewModel]> in
print(err)
return Just([])
}
.eraseToAnyPublisher()
return publisher
}
我想弄清楚为什么最后一个 .eraseToAnyPublisher() 会出现上述错误,对我来说似乎所有类型都已明确指定,不是吗?
static func searchUsers(query: String) -> AnyPublisher<[UserViewModel], Never> {
// prepare URL
let urlString = "\(baseURL)/search/users?q=\(query)"
guard let url = URL(string: urlString) else {
return Just([]).eraseToAnyPublisher()
}
// get handle of native data task publisher
let publisher = URLSession.shared.dataTaskPublisher(for: url)
.handleEvents(
receiveSubscription: { _ in
activityIndicatorPublisher.send(true)
}, receiveCompletion: { _ in
activityIndicatorPublisher.send(false)
}, receiveCancel: {
activityIndicatorPublisher.send(false)
})
.tryMap { data, response -> Data in
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw NetworkError.httpError
}
print(String(data: data, encoding: .utf8) ?? "")
return data
}
.decode(type: SearchUserResponse.self, decoder: JSONDecoder())
.map { [=11=].items }
.flatMap({ users in
var userViewModels = [UserViewModel]()
users.forEach { user in
userViewModels.append(contentsOf: UserViewModel(with: user))
}
return userViewModels
})
.catch { err -> Just<[UserViewModel]> in
print(err)
return Just([])
}
.eraseToAnyPublisher() // <-- HERE IS THE ERROR
return publisher
}
不幸的是,对于那些复杂的 Combine
管道,有时编译器错误会显示在错误的行上。在你的情况下有两个问题,但不是编译器指向的地方。
一个是使用 flatMap
而不是 map
。
管道的这一部分:
let publisher = URLSession.shared.dataTaskPublisher(for: url)
.handleEvents(
receiveSubscription: { _ in
activityIndicatorPublisher.send(true)
}, receiveCompletion: { _ in
activityIndicatorPublisher.send(false)
}, receiveCancel: {
activityIndicatorPublisher.send(false)
})
.tryMap { data, response -> Data in
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw NetworkError.httpError
}
print(String(data: data, encoding: .utf8) ?? "")
return data
}
.decode(type: SearchUserResponse.self, decoder: JSONDecoder())
.map { [=10=].items }
returns Publisher<[User], Error>
.
接下来您要将其转换为 Publisher<[UserViewModel], Error>
,为此您需要一个函数 map
:
func map<T>(_ transform: @escaping (Output) -> T) -> Publishers.Map<Upstream, T>
将一种类型的 Output
转换为另一种类型的 Output
而不是 flatMap
:
func flatMap<T, P>(maxPublishers: Subscribers.Demand = .unlimited, _ transform: @escaping (Self.Output) -> P) -> Publishers.FlatMap<P, Self> where T == P.Output, P : Publisher, Self.Failure == P.Failure
将 Output
转换为新的 Publisher
第二个问题是 append(contentsOf;)
,它需要 Sequence
个元素,如果是单个元素,您应该使用 append()
,但更简单的方法是映射 [User]
到 [UserViewModel]
:
{ users in
users.map { user in
UserViewModel(with: user)
}
}
所以整个功能应该适用于这些更改:
static func searchUsers(query: String) -> AnyPublisher<[UserViewModel], Never> {
// prepare URL
let urlString = "\(baseURL)/search/users?q=\(query)"
guard let url = URL(string: urlString) else {
return Just([]).eraseToAnyPublisher()
}
// get handle of native data task publisher
let publisher = URLSession.shared.dataTaskPublisher(for: url)
.handleEvents(
receiveSubscription: { _ in
activityIndicatorPublisher.send(true)
}, receiveCompletion: { _ in
activityIndicatorPublisher.send(false)
}, receiveCancel: {
activityIndicatorPublisher.send(false)
})
.tryMap { data, response -> Data in
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw NetworkError.httpError
}
print(String(data: data, encoding: .utf8) ?? "")
return data
}
.decode(type: SearchUserResponse.self, decoder: JSONDecoder())
.map { [=14=].items }
.map { users in
users.map { user in
UserViewModel(with: user)
}
}
.catch { err -> Just<[UserViewModel]> in
print(err)
return Just([])
}
.eraseToAnyPublisher()
return publisher
}