I have a generic function used to send requests to the server. Now before I send a request I need to check if the session token is expired and update it if needed.
my function looks like this
func upload<T: Decodable>(some parameters here) -> AnyPublisher<T, Error>
I wanted to check and update the token inside that function before calling the main request but in this case, I can not return AnyPublisher<T, Error>
func upload<T: Decodable>(some parameters here) -> AnyPublisher<T, Error> {
if shouldUpdateToken {
let request = // prepare request
let session = // prepare session
return session.dataTaskPublisher(for: request)
.map(\.data)
.decode(type: SomeTokenObject.self, decoder: JSONDecoder())
// here I wanted to save token and continue with
// the previous request
// but using .map, .flatMap, .compactMap will not return needed publisher
// the error message I'll post below
.map {
// update token with $0
// and continue with the main request
}
} else {
return upload() // this will return AnyPublisher<T, Error> so it's ok here
}
}
This error I get when using .flatMap
Cannot convert return expression of type 'Publishers.FlatMap<AnyPublisher<T, Error>, Publishers.Decode<Publishers.MapKeyPath<URLSession.DataTaskPublisher, JSONDecoder.Input>, SomeTokenObject, JSONDecoder>>' (aka 'Publishers.FlatMap<AnyPublisher<T, Error>, Publishers.Decode<Publishers.MapKeyPath<URLSession.DataTaskPublisher, Data>, SomeTokenObject, JSONDecoder>>') to return type 'AnyPublisher<T, Error>'
And similar for .map.
I added another function that was returning AnyPublisher<SomeTokenObject, Error> and thought to use inside shouldUpdateToken
like that
func upload<T: Decodable>(some parameters here) -> AnyPublisher<T, Error> {
if shouldUpdateToken {
return refreshToken() // returns AnyPublisher<Void, Error>
// now I need to continue with original request
// and I'd like to use something like
.flatMap { result -> AnyPublisher<T, Error>
upload()
}
// but using .map, .flatMap, .compactMap will not return needed publisher
// the error message I'll post below
} else {
return upload() // this will return AnyPublisher<T, Error> so it's ok here
}
}
for flatMap:
Cannot convert return expression of type 'Publishers.FlatMap<AnyPublisher<T, Error>, AnyPublisher<Void, Error>>' to return type 'AnyPublisher<T, Error>'
for map: Cannot convert return expression of type 'Publishers.Map<AnyPublisher<Void, Error>, AnyPublisher<T, Error>>' to return type 'AnyPublisher<T, Error>'
Maybe I need to change to another approach? I have a lot of requests all around the app so updating the token in one place is a good idea, but how can it be done?
Here is the refreshToken()
function
func refreshToken() -> AnyPublisher<Void, Error> {
let request = ...
let session = ...
return session.dataTaskPublisher(for: request)
.map(\.data)
.decode(type: SomeTokenObject.self, decoder: JSONDecoder())
.map {
// saved new token
}
.eraseToAnyPublisher()
}
You're almost there. You need to eraseToAnyPublisher()
to type-erase the returned publisher.
Remember, that an operator like .flatMap
(or .map
and others) return their own publisher, like the type you see in the error Publishers.FlatMap<AnyPublisher<T, Error>, AnyPublisher<Void, Error>>
- you need to type-erase that:
func upload<T: Decodable>(some parameters here) -> AnyPublisher<T, Error> {
if shouldUpdateToken {
return refreshToken() // returns AnyPublisher<Void, Error>
.flatMap { _ -> AnyPublisher<T, Error> in
upload()
}
.eraseToAnyPublisher() // <- type-erase here
} else {
return upload() // actually "return"
}
}
(and make sure that you're not constantly calling the same upload
function recursively without any stop condition)