Artsy's API requires something called an XApp token in order to perform requests. The token comes back with an expiry date, after which the token will no longer work. A new token will have to be fetched.
1 2 3 4
In our previous iOS apps, tragically written in Objective-C, we have a lot of
code that looks like the following.
getXappTokenWithCompletion: checks to
make sure that there is a valid token. If there is, it invokes the completion
block immediately. Otherwise, it fetches a token, sets it in a static variable,
and then invokes the completion block.
1 2 3 4 5 6 7
That's kind of ugly. A better approach might be to embed the token-requesting
logic within the
getSomething: method. But that kind of sucks, since we'd have
to reproduce that logic for every network-accessing method. If we have ten
methods, that's ten times we need to duplicate that logic.
With our new app (written in Swift), we're
using a network abstraction layer we've created called Moya.
Moya sits on top of Alamofire and
provides an abstraction for API endpoints. Instead of having ten different
network-accessing methods, there is only one method to which you pass one of
the ten different possible
enum values. This means you have compile-time
safety in your networking code, but that's not really what we're here to talk
Moya has this cool last-minute closure that it invokes to sign requests, so we can sign these requests like this.
1 2 3 4 5 6 7 8 9 10 11
So that's kind of cool.
Since there is only one method for accessing the API, we can easily inject the token-checking method there. Something like
1 2 3 4 5 6 7 8 9 10
That's better, but it's still kind of ugly. We've got duplicated code in there, and we're just kind of abstracting away the callback mess; it still exists, we just don't see if as often.
OK, so what alternative is there? Well, Moya supports a ReactiveCocoa
extension that uses signals instead of callback closures. Super-cool. So we
can rewrite our
XAppRequest function to be the following.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Neato. So we have abstracted the "check if there is a valid token and get one if
there isn't" into its own private method called
XAppTokenRequest. If the token
exists and is valid, then the function returns
RACSignal.empty(), a signal
which completes immediately. Otherwise, we perform a fetch, which completes
when the XApp token request is finished.
Then we just need to use
RACSignal to create a new signal that is
generated once the
XAppTokenRequest signal completes. Since the
is only invoked once the
XAppTokenRequest signal completes, the newly created
request signal will be generated after the token is set, which is ideal.
All the code above is kind of simplified. That's OK, since it's just a proof of concept. If you want the full code, it's all available on GitHub and the conversation surrounding this change is in a merged pull request.
If you have run into this problem and have a different solution, we'd love to hear from you.