Files
Moya/docs/Targets.md
2017-11-29 13:42:25 -06:00

157 lines
6.1 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.
# Targets
Using Moya starts with defining a target – typically some `enum` that conforms
to the `TargetType` protocol. Then, the rest of your app deals *only* with
those targets. Targets are some action that you want to take on the API,
like "`favoriteTweet(tweetID: String)`".
Here's an example:
```swift
public enum GitHub {
case zen
case userProfile(String)
case userRepositories(String)
case branches(String, Bool)
}
```
Targets must conform to `TargetType`. The `TargetType` protocol requires a
`baseURL` property to be defined on the enum. Note that this should *not* depend
on the value of `self`, but should just return a single value (if you're using
more than one API base URL, separate them out into separate enums and Moya
providers). Here's the beginning of our extension:
```swift
extension GitHub: TargetType {
public var baseURL: URL { return URL(string: "https://api.github.com")! }
}
```
This protocol specifies the locations of
your API endpoints, relative to its base URL (more on that below).
```swift
public var path: String {
switch self {
case .zen:
return "/zen"
case .userProfile(let name):
return "/users/\(name.urlEscaped)"
case .userRepositories(let name):
return "/users/\(name.urlEscaped)/repos"
case .branches(let repo, _):
return "/repos/\(repo.urlEscaped)/branches"
}
}
```
Notice that we're ignoring the second associated value of our `branches` Target using the Swift `_` ignored-value symbol. That's because we don't need it to define the `branches` path.
Note: we're cheating here and using a `urlEscaped` extension on String.
A sample implementation is given at the end of this document.
OK, cool. So now we need to have a `method` for our enum values. In our case, we
are always using the GET HTTP method, so this is pretty easy:
```swift
public var method: Moya.Method {
return .get
}
```
Nice. If some of your endpoints require POST or another method, then you can switch
on `self` to return the appropriate value. This kind of switching technique is what
we saw when calculating our `path` property.
Our `TargetType` is shaping up, but we're not done yet. We also need a `task`
computed property that returns the task type potentially including parameters.
Here's an example:
```swift
public var task: Task {
switch self {
case .userRepositories:
return .requestParameters(parameters: ["sort": "pushed"], encoding: URLEncoding.default)
case .branches(_, let protected):
return .requestParameters(parameters: ["protected": "\(protected)"], encoding: URLEncoding.default)
default:
return .requestPlain
}
}
```
Unlike our `path` property earlier, we don't actually care about the associated values of our `userRepositories` case, so we just skip parenthesis.
Let's take a look at the `branches` case: we'll use our `Bool` associated value (`protected`) as a request parameter by assigning it to the `"protected"` key. We're parsing our `Bool` value to `String`. (Alamofire does not encode `Bool` parameters automatically, so we need to do it by our own).
While we are talking about parameters, we needed to indicate how we want our
parameters to be encoded into our request. We do this by returning a
`ParameterEncoding` alongside the `.requestParameters` task type. Out of the
box, Moya has `URLEncoding`, `JSONEncoding`, and `PropertyListEncoding`. You can
also create your own encoder that conforms to `ParameterEncoding` (e.g.
`XMLEncoder`).
A `task` property represents how you are sending / receiving data and allows you to add data, files and streams to the request body. There are several `.request` types:
- `.requestPlain` with nothing to send at all
- `.requestData(_:)` with which you can send `Data`
- `.requestJSONEncodable(_:)` with which you can send objects that conform to the `Encodable` protocol
- `.requestCustomJSONEncodable(_:encoder:)` which allows you to send objects conforming to `Encodable` encoded with provided custom JSONEncoder
- `.requestParameters(parameters:encoding:)` which allows you to send parameters with an encoding
- `.requestCompositeData(bodyData:urlParameters:)` & `.requestCompositeParameters(bodyParameters:bodyEncoding:urlParameters)` which allow you to combine url encoded parameters with another type (data / parameters)
Also, there are three upload types:
- `.uploadFile(_:)` to upload a file from a URL,
- `.uploadMultipart(_:)` for multipart uploads
- `.uploadCompositeMultipart(_:urlParameters:)` which allows you to pass multipart data and url parameters at once
And two download types:
- `.downloadDestination(_:)` for a plain file download
- `.downloadParameters(parameters:encoding:destination:)` for downloading with parameters sent alongside the request.
Next, notice the `sampleData` property on the enum. This is a requirement of
the `TargetType` protocol. Any target you want to hit must provide some non-nil
`Data` that represents a sample response. This can be used later for tests or
for providing offline support for developers. This *should* depend on `self`.
```swift
public var sampleData: Data {
switch self {
case .zen:
return "Half measures are as bad as nothing at all.".data(using: String.Encoding.utf8)!
case .userProfile(let name):
return "{\"login\": \"\(name)\", \"id\": 100}".data(using: String.Encoding.utf8)!
case .userRepositories(let name):
return "[{\"name\": \"Repo Name\"}]".data(using: String.Encoding.utf8)!
case .branches:
return "[{\"name\": \"master\"}]".data(using: String.Encoding.utf8)!
}
}
```
Finally, the `headers` property stores header fields that should be sent on the request.
```swift
public var headers: [String: String]? {
return ["Content-Type": "application/json"]
}
```
After this setup, creating our [Provider](Providers.md) is as easy as the following:
```swift
let GitHubProvider = MoyaProvider<GitHub>()
```
Escaping URLs
-------------
Here's an example extension that allows you to easily escape normal strings
"like this" to URL-encoded strings "like%20this":
```swift
extension String {
var urlEscaped: String {
return addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
}
}
```