Я столкнулся с проблемой в моей реализации кэширования в памяти, из-за которой fetchValue не захватывает второй обратный вызов. Моя текущая настройка включает получение данных с использованием Apollo GraphQL с Future от объединения, но второй обратный вызов не запускается, как ожидалось.
func myRequest<T, Q: GraphQLQuery>(query: Q) -> Future<Response<T>, CYGErrorType>? where T: RootSelectionSet {
return Future<Response<T>, CYGErrorType> { promise in
guard let futureFetchValue = self.fetchValue(query: query, cachePolicy: self.model.cachePolicy) as Future<Response<T>, CYGErrorType>? else {
promise(.failure(.default))
return
}
futureFetchValue
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
break
case .failure(let error):
promise(.failure(error))
}
}, receiveValue: { result in
print("API>> API>> response")
promise(.success(result))
})
.store(in: &self.cancellable)
}
}
==============
private func fetchValue<T, Query: GraphQLQuery>(query: Query, cachePolicy: CachePolicy) -> Future<Response<T>, CYGErrorType>? where T: RootSelectionSet {
return Future<Response<T>, CYGErrorType> { promise in
print("API>> hit")
apolloClient.fetch(query: query, cachePolicy: cachePolicy) { result in
switch result {
case .success(let graphQLResult):
let validateResponse = graphQLResult.isValid(query: query)
switch validateResponse {
case .success:
guard let data = graphQLResult.data as? T else {
promise(.failure(CYGErrorType.default))
return
}
let response = Response(value: data, response: nil, error: nil)
promise(.success(response))
print("API>> response")
case .failureWithError(let error):
promise(.failure(error))
}
case .failure(let error):
let cygError = self.graphQLErrorParsing(error: error, queryName: Query.operationName)
promise(.failure(cygError))
}
}
}
}
выход:
print("API>> hit")
print("API>> response")
print("API>> API>> response")
print("API>> response")
Ожидаемый результат
print("API>> hit")
print("API>> response")
print("API>> API>> response")
print("API>> response")
print("API>> API>> response")
Второй обратный вызов из fetchValue не захватывается из myRequest. Я ожидаю увидеть в выводе оба ответа API, но второй отсутствует. Кажется, что Combine
Future
поддерживает только первый обратный вызов, а не второй, но я не уверен, как с этим правильно справиться.
Почему fetchValue не захватывает второй обратный вызов? Как я могу гарантировать, что оба обратных вызова правильно захватываются и обрабатываются? Будем очень признательны за любую помощь или предложения по решению этой проблемы!
Ваш код тесно связан с библиотеками (клиент Apollo), с которыми люди, отвечающие на ваш вопрос, могут быть не знакомы, а для тестовой среды потребуется сервер GraphQL, которого у них нет, поэтому напрямую ответить на ваш вопрос будет сложно.
В приведенном вами примере функция myRequest
вызывает fetchValue
. fetchValue
возвращает Future
, а myRequest
, кажется, оборачивает это Future
в другое Future
, которое на самом деле не служит никакой цели, кроме повторения результатов 'fetchValue`.
Я не понимаю, почему ты это сделал. Действительно кажется, что myRequest
и fetchValue
эквивалентны — вы сможете вообще удалить myRequest
и просто вызвать fetchValue
.
Вы подразумеваете, что apolloClient.fetch
может вызывать функцию обратного вызова завершения более одного раза для данного запроса. Так ли это?
Если да, то, как предложено в комментариях, вы можете использовать PassthroughSubject
(представляющий тип возвращаемого значения как AnyPublisher
).
Future
представляет один запрос, который возвращает один результат в какой-то момент в будущем. Это поток, который выдает одно значение, а затем завершается. Поэтому модель apolloClient.fetch
будет не очень хорошей, если она будет возвращать более одного результата.
PassthroughSubject
— это общий поток, который может содержать несколько значений. Он завершится при возникновении ошибки или при явном завершении потока. (Из вашего кода неясно, как определить, когда apolloClient.fetch
завершает отправку значений и поток должен завершиться)
Не обращая внимания на эту деталь, вам может понадобиться что-то вроде этого:
private func fetchValue<T, Query: GraphQLQuery>(query: Query, cachePolicy: CachePolicy) -> AnyPublisher<Response<T>, CYGErrorType>? where T: RootSelectionSet {
let resultSubject = PassthroughSubject<Response<T>, CYGErrorType>()
print("API>> hit")
apolloClient.fetch(query: query, cachePolicy: cachePolicy) { result in
switch result {
case .success(let graphQLResult):
let validateResponse = graphQLResult.isValid(query: query)
switch validateResponse {
case .success:
guard let data = graphQLResult.data as? T else {
resultSubject.send(completion: .failure(CYGErrorType.default))
return
}
let response = Response(value: data, response: nil, error: nil)
resultSubject.send(response)
print("API>> response")
case .failure(let error):
resultSubject.send(completion: .failure(error))
}
case .failure(let error):
resultSubject.send(completion: .failure(error))
}
}
return resultSubject.eraseToAnyPublisher()
}
Здесь код создает PassthroughSubject
, и каждый раз, когда вызывается обработчик завершения, он выдает значение, переданное в обратный вызов через этот субъект. В нижней части функции мы конвертируем тему в AnyPublisher
, поскольку тот факт, что это PassthroughSubject
, является деталью реализации, которую людям, не входящим в эту функцию, знать не обязательно.