Files
Moya/docs/Providers.md

178 lines
7.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Providers
When using Moya, you make all API requests through a `MoyaProvider` instance,
passing in a value of your enum that specifies which endpoint you want to call.
After setting up your [Endpoint](Endpoints.md), you're basically all set for
basic usage:
```swift
let provider = MoyaProvider<MyService>()
```
After that simple setup, you're off to the races:
```swift
provider.request(.zen) { result in
// `result` is either .success(response) or .failure(error)
}
```
That's it! The `request()` method returns a `Cancellable`, which has
only one public function, `cancel()`, which you can use to cancel the
request. See [Examples](Examples) for more information about the `Result`
type.
Remember, *where* you put your target and the provider, are completely up
to you. You can check out [Artsy's implementation](https://github.com/artsy/eidolon/blob/master/Kiosk/App/Networking/ArtsyAPI.swift)
for an example.
Always remember to retain your providers, as they will get deallocated if you fail to do so. Deallocation will return a `-999 "canceled"` error on response.
The same reminder applies also to Moya Reactive implementations, but you will not receive any response because the whole Observable will be disposed, releasing any subscription that you may have configured.
## Advanced Usage
To explain all configuration options you have with a `MoyaProvider` we will cover each parameter one by one in the following sections.
### endpointClosure:
The first (optional) parameter for the `MoyaProvider` initializer is an
endpoints closure, which is responsible for mapping a value of your enum to a
concrete `Endpoint` instance. Let's take a look at what one might look like.
```swift
let endpointClosure = { (target: MyTarget) -> Endpoint in
let url = URL(target: target).absoluteString
return Endpoint(url: url, sampleResponseClosure: {.networkResponse(200, target.sampleData)}, method: target.method, task: target.task, httpHeaderFields: target.headers)
}
let provider = MoyaProvider(endpointClosure: endpointClosure)
```
Notice that we don't have to specify the generic type in the `MoyaProvider`
initializer anymore, since Swift will infer it from the type of our
`endpointClosure`. Neat!
You may also notice the `URL(target:)` initializer, Moya provides a convenient extension to create a `URL` from any `TargetType`.
This `endpointClosure` is about as simple as you can get. It's actually the
default implementation, too, stored in `MoyaProvider.defaultEndpointMapping`.
Check out the [Endpoints](Endpoints.md) documentation for more on _why_ you
might want to customize this.
### requestClosure:
The next optional initializer parameter is `requestClosure`, which resolves
an `Endpoint` to an actual `URLRequest`. Again, check out the [Endpoints](Endpoints.md)
documentation for how and why you'd do this.
### stubClosure:
The next option is to provide a `stubClosure`. This returns one of either `.never` (the
default), `.immediate` or `.delayed(seconds)`, where you can delay the stubbed
request by a certain number of seconds. For example, `.delayed(0.2)` would delay
every stubbed request. This can be good for simulating network delays in unit tests.
What's nice is that if you need to stub some requests differently than others,
you can use your own closure.
```swift
let provider = MoyaProvider<MyTarget>(stubClosure: { target: MyTarget -> Moya.StubBehavior in
switch target {
/* Return something different based on the target. */
}
})
```
But usually you want the same stubbing behavior for all your targets. There are
three class methods on `MoyaProvider` you can use instead.
```swift
MoyaProvider.neverStub
MoyaProvider.immediatelyStub
MoyaProvider.delayedStub(seconds)
```
So, in the above example, if you wanted immediate stubbing behavior for all
targets, either of the following would work.
```swift
let provider = MoyaProvider<MyTarget>(stubClosure: { (_: MyTarget) -> Moya.StubBehavior in return .immediate })
let provider = MoyaProvider<MyTarget>(stubClosure: MoyaProvider.immediatelyStub)
```
### manager:
Next, there's the `manager` parameter. By default you'll get a custom `Alamofire.Manager` instance with basic configurations.
```swift
public final class func defaultAlamofireManager() -> Manager {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = Alamofire.Manager.defaultHTTPHeaders
let manager = Alamofire.Manager(configuration: configuration)
manager.startRequestsImmediately = false
return manager
}
```
There is only one particular thing: since construct an `Alamofire.Request` in AF will fire the request immediately by default, even when "stubbing" the requests for unit testing. Therefore in Moya, `startRequestsImmediately` is set to `false` by default.
If you'd like to customize your own manager, for example, to add SSL pinning, create one and pass it in,
all requests will route through the custom configured manager.
```swift
let policies: [String: ServerTrustPolicy] = [
"example.com": .PinPublicKeys(
publicKeys: ServerTrustPolicy.publicKeysInBundle(),
validateCertificateChain: true,
validateHost: true
)
]
let manager = Manager(
configuration: URLSessionConfiguration.default,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: policies)
)
let provider = MoyaProvider<MyTarget>(manager: manager)
```
### plugins:
Finally, you may also provide an array of `plugins` to the provider. These receive callbacks
before a request is sent and after a response is received. There are a few plugins
included already: one for network activity (`NetworkActivityPlugin`), one for logging
all network activity (`NetworkLoggerPlugin`), and another for [HTTP Authentication](Authentication.md).
For example you can enable the logger plugin by simply passing `[NetworkLoggerPlugin()]` alongside the `plugins` parameter of your `Endpoint`. Note that a plugin can also be configurable, for example the already included `NetworkActivityPlugin` requires a `networkActivityClosure` parameter. The configurable plugin implementation looks like this:
```swift
public final class NetworkActivityPlugin: PluginType {
public typealias NetworkActivityClosure = (_ change: NetworkActivityChangeType, _ target: TargetType) -> Void
let networkActivityClosure: NetworkActivityClosure
public init(networkActivityClosure: @escaping NetworkActivityClosure) {
self.networkActivityClosure = networkActivityClosure
}
// MARK: Plugin
/// Called by the provider as soon as the request is about to start
public func willSend(_ request: RequestType, target: TargetType) {
networkActivityClosure(.began, target)
}
/// Called by the provider as soon as a response arrives, even if the request is canceled.
public func didReceive(_ result: Result<Moya.Response, MoyaError>, target: TargetType) {
networkActivityClosure(.ended, target)
}
}
```
The `networkActivityClosure` is a closure that you can provide to be notified whenever a network request begins or
ends. This is useful for working with the [network activity indicator](https://github.com/thoughtbot/BOTNetworkActivityIndicator).
Note that signature of this closure is `(_ change: NetworkActivityChangeType, _ target: TargetType) -> Void`,
so you will only be notified when a request has `.began`/`.ended` and for which `target` –
you aren't provided any other details about the request itself.