es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Cómo hacer genérica esta extensión de Publisher

Tengo la siguiente extensión en un Publisher que me permite paginar una solicitud de URL. Originalmente lo usé en un caso de uso específico, donde la salida (Output) del publicador era de tipo 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()
  }
}

La estructura CustomType tiene la siguiente forma:

struct CustomType: Codable {
  let users: [User]
  let pageId: String?
}

Esto se llama así:

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) // <- Esta parte
    .singleOutput()
}

Sin embargo, ahora quiero hacerlo genérico para que se pueda utilizar en cualquier tipo de Output, siempre y cuando tenga un pageId y algún tipo de arreglo.

Intenté utilizar un protocolo Pageable de esta manera:

protocol Pageable {
  associatedtype T

  var pageId: String? {get}
  var items: [T] {get}
}

Pero no puedo usarlo con la extensión porque Output no puede ser utilizado con un protocolo que contiene un associatedType.

¿Es posible hacer esto?</string?,></string?,>

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Desafortunadamente, no es posible utilizar un protocolo con un tipo asociado como restricción en una extensión genérica. Esto se debe a que el tipo asociado no se conoce hasta que un tipo concreto se ajusta al protocolo.

    Una posible solución sería crear un tipo genérico PaginatedResponse que se ajuste a Decodable y Pageable. Puede tener un parámetro de tipo genérico T para el array de elementos, y una propiedad pageId.

    Aquí tienes un ejemplo de implementación:

    struct PaginatedResponse<t: decodable="">: Codable, Pageable {
      var items: [T]
      var pageId: String?
    
      enum CodingKeys: String, CodingKey {
        case items = "data"
        case pageId = "page_id"
      }
    }
    

    Luego, puedes actualizar la extensión para usar PaginatedResponse en lugar de CustomType:

    extension Publisher where Output: Pageable, Failure == Error {
      func paginate<t>(pageIdPublisher: CurrentValueSubject<string?, never="">)
        -> AnyPublisher<[T], Never> where Output.T == T {
    
        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()
          }
          .eraseToAnyPublisher()
      }
    }
    

    Ahora puedes llamar a paginate en cualquier publicador que tenga un tipo de salida que se ajuste a Pageable:

    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)
        .singleOutput()
    }
    

    ¡Espero que esto te ayude! Avísame si tienes alguna pregunta adicional.</string?,></string?,>

Comments are closed.