Moya / Moya

Network abstraction layer written in Swift.
https://moya.github.io
MIT License
15.08k stars 1.98k forks source link

Looking for modularity in Moya? #591

Closed rlam3 closed 7 years ago

rlam3 commented 8 years ago

When your API grows. It looks like the enum of the Target swift file grows too. Is there a way that this could made more modular?

Is it to use multiple providers as the modular approach when architecting the API routing with Moya? Like a 1 resource per 1 target approach is all I can think of.

But problem arises when a situation like if checking JSON Web Token are expired prior firing off an async request. If authentication/JSON Web Token is one resourceTarget and obtaining a resource like user profile is dependent on JSON Web Token. How do we collectively provide enough modularity that both will work together synchronously?

I'm not sure if it is be a good idea to use a class based router instead of enums approach.


//API.swift
class API{
// base url
// token
// generic calls
}

//authentication.swift
extension API{
// func authenticateUser
}

//getUserProfile.swift
extension API{
// func getResource
// func postResource
// func deleteResource
}

Or is this it recommended to just use one provider in a large file?

I'm new to Moya. Thanks!

justinmakaila commented 8 years ago

The man @colinta always throws around the Ello API as an example. See the networking/ dir for examples: https://github.com/ello/ello-ios/tree/master/Sources/Networking

The real magic is in ElloProvider though.

I've borrowed heavily from it in the past. IMO, the provider set up is all about what your project needs. TargetType provides enough flexibility that you can request different URLs with the same provider, however, as you mentioned, you might have different requirements for different APIs.

Personally, I set up a provider per API service. In my current project, I created a MyProjectAPI struct, which manages providers and a singleton instance. The struct has a static method named

request(target: MyProjectAPI.Target, queue: NSOperationQueue, progress: ReactiveMoya.ProgressBlock)

on it. When you invoke something like MyProjectAPI.request(.SignIn(username, password)), it grabs the default provider, which is configured with a custom endpointClosure (to add headers/auth fields to the request based on the Target), and plugins. Since I use ReactiveMoya, I just added parsing the JSON response as an extension on SignalProducerType, so my API looks like this:

let target: MyProjectAPI.Target = .SignIn(username, password)
MyProjectAPI.request(target)
  .mapSignInResponse()
  .flatMap(.Latest) { // Do some work on the mapped response }

The request method also handles checking the response for errors related to authentication, and will post a notification if the user is not authenticated, to which I respond by kicking the user back to sign in.

Ultimately, the decision is up to you. In the example above, I'm only interacting with a single API, so wrapping it into a single struct seemed best. Ello has a very similar set up. In the Moya example project, you can see that we set up a provider per API service.

rlam3 commented 8 years ago

@justinmakaila Would love to your take on how to implement Moya with an authentication of using JWT. But prior to firing off each request back to the server, the token is to be checked, and if expired, the token should be refreshed prior to firing off the next request. I know this has something to do with promises, but I'm sure where I can some how do a callback wrapper per request with moya.

Looking at the OAuth approach, I cannot seem to see the connection between how to refresh a token prior to firing off the next request.

  1. What is the difference between requestClosure and endpointClosure when we create a MoyaProvider instance?

I've been trying to follow the Authentication.md documentation from Moya but still have a few question that I need to clear up.

  1. Is YourAPI the Target for which we have to seperate Authentication Target and another one for GET/POST a resource Target? Or Do we keep it as one cohesive structure and use the same provider for authentication / fetch resource?
  2. YourAwesomeOauthProvider... Is this a seperate Provider/Library I have to get for JWT to work or is it another instance of from MoyaProvider?
  3. At the let provider = MoyaProvider line, is this now being wrapped by requestClosure? where it now knows
let requestClosure = { (endpoint: Endpoint<YourAPI>, done: NSURLRequest -> Void) in
    let request = endpoint.urlRequest 
    YourAwesomeOAuthProvider.signRequest(request, completion: { signedRequest in
      // The OAuth provider can make its own network calls to sign your request.
      // However, you *must* call `done()` with the signed so that Moya can
      // actually send it!
      done(signedRequest)
    })
}
let provider = MoyaProvider(requestClosure: requestClosure) <<< So is every request with this provider wrapped with request closure to check for token expiration?

Would greatly appreciate it if you could point me to how to refresh an access token prior to firing off another request.

Thanks!

sunshinejr commented 7 years ago

@rlam3 I think that the discussion for difference between endpointClosure and requestClosure is moved to #617 so I will take on the next questions:

  1. YourAPI is in this example is the Target you use for requests. Let's say you have one Target in your app and let's call it TwitterAPI here instead of YourAPI.
  2. The YourAwesomeOauthProvider is just an example. Here you can use third party library to get your token, or you can just use Moya to get it - your choice. There are times you are working with existing SDK's for fetching tokens, but also there might be time that you will need to handle it yourself and Moya can be a perfect fit for it as well. You can even have it in the one TwitterAPI if you want.
  3. Yes, now every time you use your provider, it will first use the requestClosure to map Endpoint to NSURLRequest.
scottrhoyt commented 7 years ago

Closing this for now. @rlam3, please reopen if you have any additional questions related to this.