Skip to content

Commit 2f09075

Browse files
committed
first commit
0 parents  commit 2f09075

File tree

10 files changed

+505
-0
lines changed

10 files changed

+505
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
/*.xcodeproj
5+
xcuserdata/

.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// swift-tools-version:5.3
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "structured-networking",
8+
products: [
9+
// Products define the executables and libraries a package produces, and make them visible to other packages.
10+
.library(
11+
name: "structured-networking",
12+
targets: ["structured-networking"]),
13+
],
14+
dependencies: [
15+
// Dependencies declare other packages that this package depends on.
16+
// .package(url: /* package url */, from: "1.0.0"),
17+
],
18+
targets: [
19+
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
20+
// Targets can depend on other targets in this package, and on products in packages this package depends on.
21+
.target(
22+
name: "structured-networking",
23+
dependencies: []),
24+
.testTarget(
25+
name: "structured-networkingTests",
26+
dependencies: ["structured-networking"]),
27+
]
28+
)

README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Structured Networking
2+
3+
Strongly typed networking built on top of Combine
4+
5+
#### What the API looks like
6+
7+
```
8+
let networking = Networking(
9+
baseURLStringProvider: { (request) -> String in
10+
return "http://exampleapi.com"
11+
}
12+
)
13+
14+
LoginEndpoint(body: LoginRequestBody(useername: "me", password: "something"))
15+
.execute(with: networking)
16+
.sink { (completion) in
17+
// error checking
18+
} receiveValue: { (response: AuthResponse) in
19+
print(response)
20+
}
21+
```
22+
23+
24+
#### How to define endpoints
25+
Endpoints define all aspects of the request and response in types. If an endpoint doesnt have a params, query or a body you can use Void to indicate this is unused.
26+
27+
*NOTE*: It is your responsibility to define how `Parameters` fit in the endpoint
28+
29+
```swift
30+
public struct LoginEndpoint: APIRequest {
31+
32+
public typealias Parameters = Void
33+
public typealias Query = Void
34+
public typealias Body = LoginRequestBody
35+
36+
public typealias ResponseBody = AuthResponse
37+
38+
public static let method: HTTPMethod = .post
39+
40+
public var parameters: Parameters = ()
41+
public var query: Query = ()
42+
public var body: LoginRequestBody
43+
44+
public var endpoint: String {
45+
"/users/login"
46+
}
47+
}
48+
49+
```
50+
51+
#### Accompanying endpoint structures
52+
53+
```
54+
public struct LoginRequestBody: Codable {
55+
public var password: String
56+
public var username: String
57+
}
58+
59+
```
60+
61+
```swift
62+
public struct AuthResponse: Codable {
63+
public var refreshToken: String
64+
public var refreshTokenExpiry: Date
65+
public var token: String
66+
public var tokenExpiry: Date
67+
}
68+
69+
```
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Foundation
2+
import Combine
3+
4+
public extension APIRequest where Self.ResponseBody: Decodable {
5+
func execute(with networking: Networking) -> AnyPublisher<ResponseBody, NetworkingError> {
6+
return networking.call(self)
7+
}
8+
}
9+
10+
public extension APIRequest where Self.ResponseBody == Void {
11+
func execute(with networking: Networking) -> AnyPublisher<ResponseBody, NetworkingError> {
12+
return networking.call(self)
13+
}
14+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import Foundation
2+
3+
public enum HTTPMethod: String {
4+
case get
5+
case put
6+
case post
7+
case patch
8+
case delete
9+
}
10+
11+
public protocol AnyAPIRequest {
12+
static var method: HTTPMethod { get }
13+
var endpoint: String { get }
14+
}
15+
16+
public protocol APIRequest: AnyAPIRequest {
17+
18+
associatedtype Parameters
19+
associatedtype Query
20+
associatedtype Body
21+
associatedtype ResponseBody
22+
23+
static var method: HTTPMethod { get }
24+
static var requiresAuth: Bool { get }
25+
26+
var endpoint: String { get }
27+
var additionalHeaders: [String: String] { get }
28+
29+
var body: Body { get }
30+
31+
func getContentType() throws -> String?
32+
func getBody() throws -> Data?
33+
}
34+
35+
public extension APIRequest {
36+
37+
var description: String {
38+
return "\(Self.method.rawValue.uppercased()) \(endpoint)"
39+
}
40+
41+
static var requiresAuth: Bool { false }
42+
43+
var additionalHeaders: [String: String] { [:] }
44+
45+
func getContentType() -> String? { nil }
46+
func getBody() throws -> Data? { nil }
47+
}
48+
49+
public extension APIRequest where Body: Encodable {
50+
51+
func getContentType() -> String? { "application/json" }
52+
53+
func getBody() throws -> Data? {
54+
try JSONEncoder().encode(self.body)
55+
}
56+
}
57+
58+
public protocol BinaryEncodable {
59+
var fileUrl: URL { get }
60+
}

0 commit comments

Comments
 (0)