Skip to content

Commit 5a75a30

Browse files
committed
Update README, update method descriptions for better documentation
1 parent f3aa68c commit 5a75a30

4 files changed

Lines changed: 169 additions & 9 deletions

File tree

README.md

Lines changed: 128 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
# ObservableNetworking
22

3-
A simple, yet flexible, networking library that allows the use of RxSwift to return observable from network requests.
3+
A simple and flexible networking library that allows the use of RxSwift and Combine to return observable data from network requests.
44

55

6-
### How to use
6+
## How to use
77

8-
To get started, you will need to let the framework know a little about your different enviroments by defining an `enum` that conforms to `NetworkEnvironment`. This will allow the framework to build the desired network requests and URLs it needs.
8+
### Set up the Environment
9+
10+
To get started, the framework needs to know a little about the possible environments which can be accomplished by defining an `enum` that conforms to `NetworkEnvironment`. This will allow the framework to build the desired network requests and URLs it needs.
911

1012
```SWift
13+
import ObservableNetworking
14+
1115
enum Environment {
1216
case production
1317
case staging
1418
case dev
1519
}
1620

17-
extension Enviroment: NetworkEnvironment {
21+
extension Environment: NetworkEnvironment {
1822
var scheme: String {
1923
switch self {
2024
case .production:
@@ -41,9 +45,128 @@ extension Enviroment: NetworkEnvironment {
4145
}
4246
```
4347

44-
Once the environment has been defined it is time to initialize the framework by passing in your selected environment. This framework conforms to the `ObservableNetwork` protocol so that it is easily mocked for testing. All that is then needed is to grab the `network` from the instantiation.
48+
### Initializing `ObservableNetworking`
49+
50+
Once the environment has been defined it is time to initialize the framework by passing in the selected environment. This framework conforms to the `ObservableNetwork` protocol so that it is easily mocked for testing. All that is then needed is to grab the `network` from the instantiation.
4551

4652
```Swift
4753
let networkingFramework = ObservableNetworking(environment: .dev)
4854
let network = networkingFramework.network
4955
```
56+
57+
### Types of Requests
58+
59+
* **Authenticated Requests**
60+
```Swift
61+
// Authenticate request signature
62+
network.authenticatedRequest(method:endpoint:parameters:headers:)`
63+
```
64+
* **Unauthenticated Requests**
65+
```Swift
66+
// Unauthenticated request signature
67+
network.request(method:endpoint:parameters:headers:)
68+
```
69+
70+
### Request building blocks
71+
72+
No matter which reactive framework is chosen, each type of network request has the same basic components which will need to be configured for the request.
73+
74+
#### Method - `HTTPMethod`
75+
76+
The `method` is the HTTP verb that indicates what kind of request is to be made. Some of the most common methods are `.get` ("GET") and .post ("POST"). There are a number more that are supported, all of which can be found [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods).
77+
78+
#### Endpoint - `String`
79+
80+
The `endpoint` is the last portion of the URL and will specify to the API what information you wish to send or receive as a result of the request.
81+
82+
#### Parameters - `[String : Any]?`
83+
84+
The `parameters` comprise the information that the API needs to properly process the request. For `.get` requests the parameters are added on to the end of the URL. For `.post`, `.patch` or `.put` requests the parameters are serialized and added as the request's `httpBody`.
85+
86+
#### Headers - `[String : String]?`
87+
88+
The `headers` allow additional information to be passed along in a request or response. The framework automatically adds the `Accept: json` header in each request, but if needed, it will be overridden if another value is passed in with the `Accept` name.
89+
90+
For authenticated requests, an authorization cookie is automatically added (the cookie will automatically be stored when it comes back from the API) to be authenticated by the API. Authenticated requests also automatically get the `Accept: json` header as well.
91+
92+
### Choose the reactive framework
93+
94+
Now that the networking is instantiated and has an environment to build requests, it is time to decide which reactive framework to use for observing requests. `ObservableNetworking` supports both [RxSwift](https://github.com/ReactiveX/RxSwift) and [Combine](https://developer.apple.com/documentation/combine) out of the box.
95+
96+
**Important Note**:
97+
98+
Apple's Combine framework is only supported on:
99+
* iOS 13.0 and later
100+
* OSX 10.15 and later
101+
* Mac Catalyst 13.0 and later
102+
* tvOS 13.0 and later
103+
* watchOS 6.0 and later
104+
105+
If you need to support previous versions, then you will need to use RxSwift
106+
107+
## RxSwift Implementation Example
108+
```Swift
109+
110+
let disposeBag = DisposeBag()
111+
112+
// Construct the parameters for the request
113+
let params = [
114+
"q": "Bert"
115+
]
116+
117+
// Specify the request's endpoint
118+
let endpoint = "characters"
119+
120+
api.request(method: .get, endpoint: endpoint, parameters: params, headers: nil)
121+
.subscribe(onNext: { [weak self] result in
122+
guard let self = self else { return }
123+
124+
switch result {
125+
case .failure(let error):
126+
print("Error: \(error.localizedDescription)")
127+
case .success(let json):
128+
let decoder = JSONDecoder()
129+
130+
do {
131+
let character = decoder.decode(SesameStreetCharacter.self, from: json)
132+
DispatchQueue.main.async {
133+
self?.characterInfoLabel.text = character.bio
134+
}
135+
} catch {
136+
print("Error: decoding SesameStreetCharacter failed")
137+
}
138+
}
139+
})
140+
.disposed(by: disposeBag)
141+
```
142+
143+
## Combine Implementation Example
144+
```Swift
145+
// Custom error to handle decoding mishaps
146+
enum SampleError: Error {
147+
case decoding(description: String)
148+
}
149+
150+
// Construct the parameters for the request
151+
let params = [
152+
"q": "Bert"
153+
]
154+
155+
// Specify the request's endpoint
156+
let endpoint = "characters"
157+
158+
let _ = api.request(method: .get, endpoint: endpoint, parameters: params, headers: nil)
159+
.mapError { SampleError.decoding(description: $0.localizedDescription) }
160+
.flatMap(maxPublishers: .max(1), { json -AnyPublisher<SesameStreetCharacter, SampleError> in
161+
let decoder = JSONDecoder()
162+
163+
return Just(json)
164+
.decode(type: SesameStreetCharacter.self, decoder: decoder)
165+
.mapError { SampleError.decoding(description: $0.localizedDescription) }
166+
.eraseToAnyPublisher()
167+
})
168+
.assertNoFailure()
169+
.receive(on: RunLoop.main)
170+
.map { $0.bio }
171+
.assign(to: \.text, on: characterInfoLabel)
172+
```
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Steve Galbraith on 10/2/19.
6+
//
7+
8+
import Foundation
9+
10+
public enum AuthType {
11+
case cookie
12+
case jwt
13+
case none
14+
}

Sources/ObservableNetworking/NetworkManager.swift

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,39 @@ public final class NetworkManager: Network {
4040

4141
// MARK: - Network
4242

43+
/// Make a network request that does not require authentication
44+
/// - Parameter method: The HTTPMethod of the request
45+
/// - Parameter endpoint: The endpoint of the request
46+
/// - Parameter parameters: The parameters to be added to either the HTTP body or as query parameters
47+
/// - Parameter headers: The headers to be added to the request
4348
public func request(method: HTTPMethod, endpoint: String, parameters: [String : Any]? = nil, headers: [String : String]? = nil) -> Observable<Result<Data, NetworkError>> {
4449
dataRequest(method: method, endpoint: endpoint, parameters: parameters, headers: defaultHeaders(headers))
4550
}
46-
51+
52+
/// Make an authenticated network request. The authentication cookie must be saved prior to this call, or the call will fail.
53+
/// - Parameter method: The HTTPMethod of the request
54+
/// - Parameter endpoint: The endpoint of the request
55+
/// - Parameter parameters: The parameters to be added to either the HTTP body or as query parameters
56+
/// - Parameter headers: The headers to be added to the request. The authentication cookie will automatically be added without need to pass it in.
4757
public func authenticatedRequest(method: HTTPMethod, endpoint: String, parameters: [String : Any]? = nil, headers: [String : String]? = nil) -> Observable<Result<Data, NetworkError>> {
4858
dataRequest(method: method, endpoint: endpoint, parameters: parameters, headers: authenticatedHeaders(headers), requiresAuthentication: true)
4959
}
5060

61+
/// Make a network request that does not require authentication
62+
/// - Parameter method: The HTTPMethod of the request
63+
/// - Parameter endpoint: The endpoint of the request
64+
/// - Parameter parameters: The parameters to be added to either the HTTP body or as query parameters
65+
/// - Parameter headers: The headers to be added to the request
5166
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, macCatalyst 13.0, *)
5267
public func request(method: HTTPMethod, endpoint: String, parameters: [String : Any]? = nil, headers: [String : String]? = nil) -> AnyPublisher<Data, NetworkError> {
5368
dataRequest(method: method, endpoint: endpoint, parameters: parameters, headers: defaultHeaders(headers))
5469
}
5570

71+
/// Make an authenticated network request. The authentication cookie must be saved prior to this call, or the call will fail.
72+
/// - Parameter method: The HTTPMethod of the request
73+
/// - Parameter endpoint: The endpoint of the request
74+
/// - Parameter parameters: The parameters to be added to either the HTTP body or as query parameters
75+
/// - Parameter headers: The headers to be added to the request. The authentication cookie will automatically be added without need to pass it in.
5676
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, macCatalyst 13.0, *)
5777
public func authenticatedRequest(method: HTTPMethod, endpoint: String, parameters: [String : Any]? = nil, headers: [String : String]? = nil) -> AnyPublisher<Data, NetworkError> {
5878
dataRequest(method: method, endpoint: endpoint, parameters: parameters, headers: authenticatedHeaders(headers))
@@ -111,7 +131,7 @@ public final class NetworkManager: Network {
111131
switch method {
112132
case .get:
113133
urlString += "?\(queryString(for: parameters))"
114-
case .post:
134+
case .post, .put, .patch:
115135
httpBody = generateHTTPBody(from: parameters)
116136
default:
117137
break
@@ -173,9 +193,9 @@ public final class NetworkManager: Network {
173193
}
174194

175195
private func authenticatedHeaders(_ headers: [String: String]?) -> Header {
176-
guard let cookie = authCookie else { return Header() }
177-
178196
let headerDefaults = defaultHeaders(headers)
197+
guard let cookie = authCookie else { return headerDefaults }
198+
179199
let authHeader = HTTPCookie.requestHeaderFields(with: [cookie])
180200
guard let merged = merge(headerDefaults, with: authHeader) as? Header
181201
else {

Sources/ObservableNetworking/Protocols/Session.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ public protocol Session {
1919

2020
// Make URLSession connform to Session protocol
2121
extension URLSession: Session {
22+
23+
/// Creates a task that retrieves the contents of a URL based on the specified URL request object, and calls a handler upon completion.
2224
public func dataTask(with request: NSURLRequest, completionHandler: @escaping DataTaskResult) -> DataTask {
2325
dataTask(with: request as URLRequest, completionHandler: completionHandler) as DataTask
2426
}
2527

28+
/// Returns a publisher that wraps a URL session data task for a given NSURLRequest.
2629
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, macCatalyst 13.0, *)
2730
public func dataTaskPublisher(for request: NSURLRequest) -> AnyPublisher<Data, NetworkError> {
2831
dataTaskPublisher(for: request as URLRequest)

0 commit comments

Comments
 (0)