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, you’re basically all set for
basic usage:
let provider = MoyaProvider<MyService>()
After that simple setup, you’re off to the races:
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 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 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.
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 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
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.
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.
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.
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.
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.
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.
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:
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.
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.