1

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()
    }
AlexanderZ
  • 2,128
  • 3
  • 20
  • 24
  • Use `.eraseToAnyPublisher()` on the returned publisher within the condition. And you also need to `.flatMap` to a publisher that actually returns the `T` value – New Dev Feb 12 '21 at 17:44
  • @NewDev I tried that but got the same result. Btw it is used inside `refreshToken()` – AlexanderZ Feb 12 '21 at 17:46
  • Sure, but you augment it with `.flatMap`, which returns a different type. You need to add it to the end of that chain: `refreshToken().flatMap {..}.eraseToAnyPublisher()` – New Dev Feb 12 '21 at 17:47

1 Answers1

2

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)

New Dev
  • 48,427
  • 12
  • 87
  • 129
  • true, thanks! I tried that but maybe the other issue was there. I'll accept your answer in a bit :) Now is too early. – AlexanderZ Feb 12 '21 at 17:53