Skip to content

Commit

Permalink
Update documentation (#52)
Browse files Browse the repository at this point in the history
* Update README to standard layout and improve wording

* Tidy up API doc

* Tidy up RestRequest API docs and add example usage

* [jazzy-doc] Documentation update
  • Loading branch information
helenmasters authored Feb 7, 2019
1 parent 523f389 commit c383584
Show file tree
Hide file tree
Showing 66 changed files with 3,480 additions and 1,206 deletions.
106 changes: 62 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
# SwiftyRequest
<p align="center">
<a href="http://kitura.io/">
<img src="https://raw.githubusercontent.com/IBM-Swift/Kitura/master/Sources/Kitura/resources/kitura-bird.svg?sanitize=true" height="100" alt="Kitura">
</a>
</p>

<p align="center">
<a href="https://ibm-swift.github.io/SwiftyRequest/index.html">
<img src="https://img.shields.io/badge/apidoc-SwiftyRequest-1FBCE4.svg?style=flat" alt="APIDoc">
</a>
<a href="https://travis-ci.org/IBM-Swift/SwiftyRequest">
<img src="https://travis-ci.org/IBM-Swift/SwiftyRequest.svg?branch=master" alt="Build Status - Master">
</a>
<img src="https://img.shields.io/badge/os-macOS-green.svg?style=flat" alt="macOS">
<img src="https://img.shields.io/badge/os-linux-green.svg?style=flat" alt="Linux">
<img src="https://img.shields.io/badge/license-Apache2-blue.svg?style=flat" alt="Apache 2">
<a href="http://swift-at-ibm-slack.mybluemix.net/">
<img src="http://swift-at-ibm-slack.mybluemix.net/badge.svg" alt="Slack Status">
</a>
</p>

[![Build Status - Master](https://travis-ci.org/IBM-Swift/SwiftyRequest.svg?branch=master)](https://travis-ci.org/IBM-Swift/SwiftyRequest)
![macOS](https://img.shields.io/badge/os-macOS-green.svg?style=flat)
![Linux](https://img.shields.io/badge/os-linux-green.svg?style=flat)
# SwiftyRequest

SwiftyRequest is an HTTP networking library built for Swift.
`SwiftyRequest` is an HTTP networking library built for Swift.

SwiftyRequest uses `URLSession` for the underlying transport. `URLSession` on Linux is not yet completely implemented so you may find that this library is less reliable on Linux than Darwin.
`SwiftyRequest` uses `URLSession` for the underlying transport. `URLSession` on Linux is not yet completely implemented so you may find that this library is less reliable on Linux than Darwin (reference issues [#24](https://github.com/IBM-Swift/SwiftyRequest/issues/24) and [#25](https://github.com/IBM-Swift/SwiftyRequest/issues/25), the second of these references a [Foundation PR](https://github.com/apple/swift-corelibs-foundation/pull/1629)).

## Contents
* [Features](#features)
Expand All @@ -18,53 +35,34 @@ SwiftyRequest uses `URLSession` for the underlying transport. `URLSession` on Li
## Features
- Several response methods (e.g. Data, Object, Array, String, etc.) to eliminate boilerplate code in your application.
- JSON encoding and decoding.
- Integration with [CircuitBreaker](https://github.com/IBM-Swift/CircuitBreaker) library.
- Authentication token.
- Integration with the [CircuitBreaker](https://github.com/IBM-Swift/CircuitBreaker) library.
- Authentication tokens.
- Multipart form data.

## Swift version
The 0.0.x releases were tested on macOS and Linux using the Swift 3.1 and 3.1.1 binaries.

The 1.x.x releases were tested on macOS and Linux using the Swift 4.0.3 and later.

*NOTE:* Because of issues with URLSession/URLRequest in Swift 4.0, Swift 4.0 projects should use the 0.0.x release of SwiftyRequest.

You can download this version of the Swift binaries by following this [link](https://swift.org/download/#releases).
This repository supports Swift 4.0.3 and later.

## Installation
To leverage the SwiftyRequest package in your Swift application, you should specify a dependency for it in your `Package.swift` file:
To leverage the `SwiftyRequest` package in your Swift application, you should specify a dependency for it in your `Package.swift` file:

```swift
import PackageDescription

let package = Package(
name: "MySwiftProject",
### Add dependencies

...
Add `SwiftyRequest` to the dependencies within your application's `Package.swift` file. Substitute `"x.x.x"` with the latest `SwiftyRequest` [release](https://github.com/IBM-Swift/SwiftyRequest/releases).

dependencies: [
// Swift 4.0
.package(url: "https://github.com/IBM-Swift/SwiftyRequest.git", .upToNextMajor(from: "0.0.0")),
// Swift 4.0.2 or newer
.package(url: "https://github.com/IBM-Swift/SwiftyRequest.git", .upToNextMajor(from: "1.0.0")),
...
```swift
.package(url: "https://github.com/IBM-Swift/SwiftyRequest.git", from: "x.x.x")
```
Add `SwiftyRequest` to your target's dependencies:

])
```Swift
.target(name: "example", dependencies: ["SwiftyRequest"]),
```

## Usage

### Make Requests
To make outbound HTTP calls using SwiftyRequest, You create a `RestRequest` instance. The `method` parameter is optional (defaulting to `.get`) and `url` is required

#### Below is the list of customizeable fields
- `headerParameters`
- `acceptType`
- `messageBody`
- `productInfo`
- `circuitParameters`
- `contentType` : Defaults to `application/json`
- `method` : Defaults to `application/json`
To make outbound HTTP calls using `SwiftyRequest`, create a `RestRequest` instance. The `method` parameter is optional (it defaults to `GET`), the `url` parameter is required.

Example usage of `RestRequest`:

Expand All @@ -75,6 +73,17 @@ let request = RestRequest(method: .get, url: "http://myApiCall/hello")
request.credentials = .apiKey
```

You can customize the following parameters in the HTTP request:
- `headerParameters` : The HTTP header fields which form the header section of the request message.
- `credentials` : The HTTP authentication credentials for the request.
- `acceptType` : The HTTP `Accept` header, defaults to `application/json`.
- `messageBody` : The HTTP message body of the request.
- `productInfo` : The HTTP `User-Agent` header.
- `circuitParameters` : A `CircuitParameters` object which includes a reference to a fallback function that will be invoked when the circuit is failing fast (see [CircuitBreaker Integration](#circuitbreaker-integration)).
- `contentType` : The HTTP `Content-Type header`, defaults to `application/json`.
- `method` : The HTTP method specified in the request, defaults to `.get`.


### Invoke Response
In this example, `responseToError` is simply an error handling function.
The `response` object we get back is of type `RestResponse<String>` so we can perform a switch on the `response.result` to determine if the network call was successful.
Expand All @@ -92,7 +101,7 @@ request.responseString(responseToError: responseToError) { response in

### Invoke Response with Template Parameters

In this example, we invoke a response method with some template parameters to be used in replacing the `{state}` and `{city}` values in the `url`. This allows us to create multiple response invocations with the same `RestRequest` object, but possibly using different url values. Additionally, the `RequestParameters` is a helper object to bundle up values used to create a `RestRequest` object.
In this example, we invoke a response method with two template parameters to be used to replace the `{state}` and `{city}` values in the `url`. This allows us to create multiple response invocations with the same `RestRequest` object, but possibly using different url values.

```swift
let request = RestRequest(url: "http://api.weather.com/api/123456/conditions/q/{state}/{city}.json")
Expand All @@ -105,7 +114,7 @@ request.responseData(templateParams: ["state": "TX", "city": "Austin"]) { respon

### Invoke Response with Query Parameters

In this example, we invoke a response method with a query parameter to be appended onto the `url` behind the scenes so that the `RestRequest` gets executed with the following url: `http://api.weather.com/api/123456/conditions/q/CA/San_Francisco.json?hour=9`. If no `queryItems` parameter is set, then all query parameters will be removed from the url if any existed.
In this example, we invoke a response method with a query parameter to be appended onto the `url` behind the scenes so that the `RestRequest` gets executed with the following url: `http://api.weather.com/api/123456/conditions/q/CA/San_Francisco.json?hour=9`. If there are query items already specified in the request URL they will be replaced.

```swift
let request = RestRequest(url: "http://api.weather.com/api/123456/conditions/q/CA/San_Francisco.json")
Expand All @@ -118,7 +127,7 @@ request.responseData(queryItems: [URLQueryItem(name: "hour", value: "9")]) { res

## CircuitBreaker Integration

SwiftyRequest now has additional built-in functionality for leveraging the [CircuitBreaker](https://github.com/IBM-Swift/CircuitBreaker) library to increase your application's stability. To make use of this functionality, you just need to provide a `CircuitParameters` object to the `RestRequest` initializer. A `CircuitParameters` object will include a reference to a fallback function that will be invoked when the circuit is failing fast.
`SwiftyRequest` now has additional built-in functionality for leveraging the [CircuitBreaker](https://github.com/IBM-Swift/CircuitBreaker) library to increase your application's stability. To make use of this functionality, you just need to provide a `CircuitParameters` object to the `RestRequest` initializer. A `CircuitParameters` object will include a reference to a fallback function that will be invoked when the circuit is failing fast.

### Fallback
Here is an example of a fallback closure:
Expand All @@ -130,7 +139,7 @@ let fallback = { (error: BreakerError, msg: String) in
```

### CircuitParameters
We just initialize the `CircuitParameters` object and create a `RestRequest` instance. The only required value you need to set for `CircuitParameters` is the `fallback` (everything else has default values).
We initialize the `CircuitParameters` object and create a `RestRequest` instance. The only required value you need to set for `CircuitParameters` is the `fallback` (everything else has default values).

```swift
let circuitParameters = CircuitParameters(timeout: 2000,
Expand All @@ -145,7 +154,7 @@ request.circuitParameters = circuitParameters
At this point, you can use any of the response methods mentioned in the section below.

## Response Methods
There are various response methods you can use based on what result type you want, here they are:
There are various response methods you can use based on the result type you want, here they are:

- `responseData` returns a `Data` object.
- `responseObject<T: Codable>` returns a Codable object of type `T`.
Expand All @@ -154,5 +163,14 @@ There are various response methods you can use based on what result type you wan
- `responseString` returns a `String`.
- `responseVoid` returns `Void`.

## API documentation

For more information visit our [API reference](http://ibm-swift.github.io/SwiftyRequest/).

## Community

We love to talk server-side Swift, and Kitura. Join our [Slack](http://swift-at-ibm-slack.mybluemix.net/) to meet the team!

## License
This Swift package is licensed under Apache 2.0. Full license text is available in [LICENSE](LICENSE).

This library is licensed under Apache 2.0. Full license text is available in [LICENSE](https://github.com/IBM-Swift/SwiftyRequest/blob/master/LICENSE).
54 changes: 27 additions & 27 deletions Sources/SwiftyRequest/MultipartFormData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@

import Foundation

/// Object encapsulating a Multipart Form
/// Object encapsulating a multipart form.
public class MultipartFormData {

/// String denoting the Content Type
/// String denoting the `Content-Type` of the request header.
public var contentType: String { return "multipart/form-data; boundary=\(boundary)" }

// add contentLength?
Expand All @@ -41,39 +41,39 @@ public class MultipartFormData {
return boundary.data(using: .utf8, allowLossyConversion: false)!
}

/// Initialize a `MultipartFormData` instance
/// Initialize a `MultipartFormData` instance.
public init() {
self.boundary = "swiftyrequest.boundary.bd0b4c6e3b9c2126"
}

/// Method to create append a new body part to the multipart form.
/// Append a new body part to the multipart form, where the original data is in a file described by the `fileName` string.
///
/// - Parameter Data: the data of the body part
/// - Parameter withName: the name/key of the body part
/// - Parameter mimeType: the mime type of the body part
/// - Parameter fileName: the name of the file the data came from
/// - Returns: returns a Data Object encompassing the combined body parts
/// - Parameter Data: The data of the body part.
/// - Parameter withName: The name/key of the body part.
/// - Parameter mimeType: The MIME type of the body part.
/// - Parameter fileName: The name of the file the data came from.
/// - Returns: Returns a `Data` object encompassing the combined body parts.
public func append(_ data: Data, withName: String, mimeType: String? = nil, fileName: String? = nil) {
let bodyPart = BodyPart(key: withName, data: data, mimeType: mimeType, fileName: fileName)
bodyParts.append(bodyPart)
}

/// Method to create append a new body part to the multipart form.
/// Append a new body part to the multipart form, where the original data is in a url described by `fileURL`.
///
/// - Parameter fileURL: the url with to extract the data from
/// - Parameter withName: the name/key of the body part
/// - Parameter mimeType: the mime type of the body part
/// - Returns: returns a Data Object encompassing the combined body parts
/// - Parameter fileURL: The url to extract the data from.
/// - Parameter withName: The name/key of the body part.
/// - Parameter mimeType: The MIME type of the body part.
/// - Returns: Returns a `Data` object encompassing the combined body parts.
public func append(_ fileURL: URL, withName: String, mimeType: String? = nil) {
if let data = try? Data.init(contentsOf: fileURL) {
let bodyPart = BodyPart(key: withName, data: data, mimeType: mimeType, fileName: fileURL.lastPathComponent)
bodyParts.append(bodyPart)
}
}

/// Method to combine the Multipart form body parts in to a single Data Object
/// Combine the multipart form body parts into a single `Data` object.
///
/// - Returns: returns a Data Object encompassing the combined body parts
/// - Returns: Returns a `Data` object encompassing the combined body parts.
public func toData() throws -> Data {
var data = Data()
for (index, bodyPart) in bodyParts.enumerated() {
Expand All @@ -96,7 +96,7 @@ public class MultipartFormData {
}
}

/// Object encapsulating a singular part of a Multipart form
/// Object encapsulating a singular part of a multipart form.
public struct BodyPart {

private(set) var key: String
Expand All @@ -116,11 +116,11 @@ public struct BodyPart {
return header
}

/// Initialize a `BodyPart` instance
/// Initialize a `BodyPart` instance.
///
/// - Parameters:
/// - key: the body part identifier
/// - value: the value of the BodyPart
/// - key: The body part identifier.
/// - value: The value of the `BodyPart`.
public init?(key: String, value: Any) {
let string = String(describing: value)
guard let data = string.data(using: .utf8) else {
Expand All @@ -131,23 +131,23 @@ public struct BodyPart {
self.data = data
}

/// Initialize a `BodyPart` instance
/// Initialize a `BodyPart` instance.
///
/// - Parameters:
/// - key: the body part identifier
/// - data: the data of the BodyPart
/// - mimeType: the mime type
/// - fileType: the data's file name
/// - key: The body part identifier.
/// - data: The data of the BodyPart.
/// - mimeType: The MIME type.
/// - fileType: The data's file name.
public init(key: String, data: Data, mimeType: String? = nil, fileName: String? = nil) {
self.key = key
self.data = data
self.mimeType = mimeType
self.fileName = fileName
}

/// Method to construct the content of the BodyPart
/// Construct the content of the `BodyPart`.
///
/// - Returns: returns a Data Object consisting of the header and data
/// - Returns: Returns a `Data` object consisting of the header and data.
public func content() throws -> Data {
var result = Data()
let headerString = header
Expand Down
Loading

0 comments on commit c383584

Please sign in to comment.