如何使此发布者扩展通用
How to make this publisher extension generic
我在 Publisher
上有以下扩展,它允许我对 URL 请求进行分页。我最初在特定用例中使用它,其中发布者的 Output
类型为 CustomType
.
extension Publisher where Output == CustomType,
Failure == Error {
func paginate(pageIdPublisher: CurrentValueSubject<String?, Never>) -> AnyPublisher<[User], Never> {
return self
.handleEvents(receiveOutput: { response in
if let maxId = response.pageId {
pageIdPublisher.send(maxId)
} else {
pageIdPublisher.send(completion: .finished)
}
})
.reduce([]) { allUsers, response in
return response.users + allUsers
}
.catch { error in
Just([])
}
.eraseToAnyPublisher()
}
}
struct CustomType: Codable {
let users: [User]
let pageId: String?
}
这是这样称呼的:
func loadItem() async throws -> [String] {
let pageIdPublisher = CurrentValueSubject<String?, Never>(nil)
return try await pageIdPublisher
.flatMap { pageId in
urlSession
.publisher(
for: .item(pageId: pageId),
receiveOn: queue
)
}
.paginate(pageIdPublisher: pageIdPublisher) // <- This part
.singleOutput()
}
但是,我现在想让它成为通用的,以便它可以用于任何 Output
类型,只要它具有 pageId
和某种数组。
我尝试使用这样的协议 Pageable
:
protocol Pageable {
associatedtype T
var pageId: String? {get}
var items: [T] {get}
}
但我不能将它与扩展一起使用,因为 Output
不能与包含 associatedType
.
的协议一起使用
这可能吗?
如果您使用 :
将 Output
类型约束到您的 Pageable
协议,并使用 Output.T
作为返回的发布者输出类型,则 paginate
方法应该编译:
extension Publisher where Output: Pageable,
Failure == Error {
func paginate(pageIdPublisher: CurrentValueSubject<String?, Never>) -> AnyPublisher<[Output.T], Never> {
return self
.handleEvents(receiveOutput: { response in
if let maxId = response.pageId {
pageIdPublisher.send(maxId)
} else {
pageIdPublisher.send(completion: .finished)
}
})
.reduce([]) { allItems, response in
return response.items + allItems
}
.catch { error in
Just([])
}
.eraseToAnyPublisher()
}
}
另一种使它更通用的想法是将 Output
限制为 Identifiable
类型,其中 ID
是可选的,并提供额外的闭包参数来指定从 Output
映射到数组:
extension Publisher where Output: Identifiable,
Failure == Error {
func paginate<T, ID>(
pageIdPublisher: CurrentValueSubject<Output.ID?, Never>,
items: @escaping (Output) -> [T]
)
-> AnyPublisher<[T], Never>
where Self.Output.ID == ID?
{
...
}
}
您将在实施中使用 response.id
而不是 response.pageId
,并使用 items(response)
而不是 response.items
。例如,在调用点,您将为闭包参数传入 \.users
。
我在 Publisher
上有以下扩展,它允许我对 URL 请求进行分页。我最初在特定用例中使用它,其中发布者的 Output
类型为 CustomType
.
extension Publisher where Output == CustomType,
Failure == Error {
func paginate(pageIdPublisher: CurrentValueSubject<String?, Never>) -> AnyPublisher<[User], Never> {
return self
.handleEvents(receiveOutput: { response in
if let maxId = response.pageId {
pageIdPublisher.send(maxId)
} else {
pageIdPublisher.send(completion: .finished)
}
})
.reduce([]) { allUsers, response in
return response.users + allUsers
}
.catch { error in
Just([])
}
.eraseToAnyPublisher()
}
}
struct CustomType: Codable {
let users: [User]
let pageId: String?
}
这是这样称呼的:
func loadItem() async throws -> [String] {
let pageIdPublisher = CurrentValueSubject<String?, Never>(nil)
return try await pageIdPublisher
.flatMap { pageId in
urlSession
.publisher(
for: .item(pageId: pageId),
receiveOn: queue
)
}
.paginate(pageIdPublisher: pageIdPublisher) // <- This part
.singleOutput()
}
但是,我现在想让它成为通用的,以便它可以用于任何 Output
类型,只要它具有 pageId
和某种数组。
我尝试使用这样的协议 Pageable
:
protocol Pageable {
associatedtype T
var pageId: String? {get}
var items: [T] {get}
}
但我不能将它与扩展一起使用,因为 Output
不能与包含 associatedType
.
这可能吗?
如果您使用 :
将 Output
类型约束到您的 Pageable
协议,并使用 Output.T
作为返回的发布者输出类型,则 paginate
方法应该编译:
extension Publisher where Output: Pageable,
Failure == Error {
func paginate(pageIdPublisher: CurrentValueSubject<String?, Never>) -> AnyPublisher<[Output.T], Never> {
return self
.handleEvents(receiveOutput: { response in
if let maxId = response.pageId {
pageIdPublisher.send(maxId)
} else {
pageIdPublisher.send(completion: .finished)
}
})
.reduce([]) { allItems, response in
return response.items + allItems
}
.catch { error in
Just([])
}
.eraseToAnyPublisher()
}
}
另一种使它更通用的想法是将 Output
限制为 Identifiable
类型,其中 ID
是可选的,并提供额外的闭包参数来指定从 Output
映射到数组:
extension Publisher where Output: Identifiable,
Failure == Error {
func paginate<T, ID>(
pageIdPublisher: CurrentValueSubject<Output.ID?, Never>,
items: @escaping (Output) -> [T]
)
-> AnyPublisher<[T], Never>
where Self.Output.ID == ID?
{
...
}
}
您将在实施中使用 response.id
而不是 response.pageId
,并使用 items(response)
而不是 response.items
。例如,在调用点,您将为闭包参数传入 \.users
。