如何使此发布者扩展通用

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