forked from lingfengmarskey/NetworkSample
-
Notifications
You must be signed in to change notification settings - Fork 0
/
NetWork.swift
149 lines (126 loc) · 5.66 KB
/
NetWork.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import Apollo
import ApolloWebSocket
import Foundation
/// xxx need to be changed with yours.
let hostValue = "xxx.appsync-api.ap-northeast-1.amazonaws.com"
let hostKey = "host"
let normalEndPoint = "https://xxx.appsync-api.ap-northeast-1.amazonaws.com/graphql"
let realtimeEndPoint = "wss://xxx.appsync-realtime-api.ap-northeast-1.amazonaws.com/graphql"
let authKey = "x-api-key"
/// yyy need to be changed with yours.
let authValue = "da2-yyy"
/**
Appsync payload format
{
data:{
query: string josnstring base64
variables: object optional
}
extension: {
authorization: object optional
}
}
*/
enum PayloadKey: String {
case data
case query
case variables
case extensions
case authorization
}
class Network {
static let shared = Network()
private init() {
let store = makeStore()
let normalTransport = makeNormalTransport(store: store)
let wsRequest = makeWebSocketEndpoinRequesttForAppsnc()
let webSocketTransport = makeWebSocketTransport(wsRequest)
let splitNetworkTransport = SplitNetworkTransport(uploadingNetworkTransport: normalTransport,
webSocketNetworkTransport: webSocketTransport)
self.apollo = ApolloClient(networkTransport: splitNetworkTransport, store: store)
}
/// A web socket transport to use for subscriptions
///
/// This web socket will have to provide the connecting payload which
/// initializes the connection as an authorized channel.
private func makeWebSocketTransport(_ wsRequest: URLRequest) -> WebSocketTransport {
let webSocketClient = WebSocket(request: wsRequest, protocol: .graphql_ws)
let requestBody = AppSyncRequestBodyCreator([authKey: authValue])
let webSocketTransport = WebSocketTransport(websocket: webSocketClient,
requestBodyCreator: requestBody)
return webSocketTransport
}
/// A endpoint request url that web socket conneting.
///
/// header-parameter-format-based-on-appsync-api-authorization-mode
/// https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html#header-parameter-format-based-on-appsync-api-authorization-mode)
private func makeWebSocketEndpoinRequesttForAppsnc() -> URLRequest {
let authDict = [
authKey: authValue,
hostKey: hostValue,
]
let headerData: Data = try! JSONSerialization.data(withJSONObject: authDict, options: JSONSerialization.WritingOptions.prettyPrinted)
let headerBase64 = headerData.base64EncodedString()
let payloadData = try! JSONSerialization.data(withJSONObject: [:], options: JSONSerialization.WritingOptions.prettyPrinted)
let payloadBase64 = payloadData.base64EncodedString()
let url = URL(string: realtimeEndPoint + "?header=\(headerBase64)&payload=\(payloadBase64)")!
let request = URLRequest(url: url)
return request
}
/// An HTTP transport to use for queries and mutations.
private func makeNormalTransport(store: ApolloStore) -> RequestChainNetworkTransport {
let url = URL(string: normalEndPoint)!
let transport = RequestChainNetworkTransport(interceptorProvider: DefaultInterceptorProvider(store: store), endpointURL: url, additionalHeaders: [authKey: authValue])
return transport
}
private(set) var apollo:ApolloClient!
/// A common store to use for `normalTransport` and `client`.
private func makeStore() -> ApolloStore {
let cache = InMemoryNormalizedCache()
return ApolloStore(cache: cache)
}
}
/// Communication message payload body creator
class AppSyncRequestBodyCreator: RequestBodyCreator {
init(_ authorization: [String: String]) {
self.authorization = authorization
}
private var authorization: [String: String]
public func requestBody<Operation>(for operation: Operation,
sendOperationIdentifiers _: Bool,
sendQueryDocument _: Bool,
autoPersistQuery: Bool) -> GraphQLMap where Operation: GraphQLOperation {
var body: GraphQLMap = [:]
var dataInfo: [String: Any] = [:]
if let variables = operation.variables {
dataInfo[PayloadKey.variables.rawValue] = variables
}
dataInfo[PayloadKey.query.rawValue] = operation.queryDocument
// The data portion of the body needs to have the query and variables as well.
guard let data = try? JSONSerialization.data(withJSONObject: dataInfo, options: .prettyPrinted) else {
fatalError("Somehow the query and variables aren't valid JSON!")
}
let jsonString = String(data: data, encoding: .utf8)
body[PayloadKey.data.rawValue] = jsonString
if autoPersistQuery {
guard let operationIdentifier = operation.operationIdentifier else {
preconditionFailure("To enable `autoPersistQueries`, Apollo types must be generated with operationIdentifiers")
}
body[PayloadKey.extensions.rawValue] = [
"persistedQuery": ["sha256Hash": operationIdentifier, "version": 1],
PayloadKey.authorization.rawValue: [
authKey: authValue,
hostKey: hostValue,
],
]
} else {
body[PayloadKey.extensions.rawValue] = [
PayloadKey.authorization.rawValue: [
authKey: authValue,
hostKey: hostValue,
],
]
}
return body
}
}