订阅
GraphQL支持订阅,允许客户端在服务器上的数据发生更改时立即进行更新。
Apollo iOS库主要通过ApolloWebSocket支持订阅的使用,这是一个可选的附加库,它使用流行的iOS WebSocket库Starscream来连接到GraphQL服务器。
通过代码生成还支持订阅:每当声明订阅字段时,都会生成符合GraphQLSubscription的操作,该操作允许传递订阅字段所接受的任何参数。
生成这些操作之后,可以使用支持订阅的网络传输使用ApolloClient实例进行订阅,并继续接收有关更改的更新,直到取消订阅。
支持订阅的传输类型
有两个不同的类符合ApolloWebSocket库中的网络传输协议:
WebSocketTransport通过一个web套接字发送所有操作。 SplitNetworkTransport挂载于WebSocketTransport实例和UploadingNetworkTransport实例(通常是HTTPNetworkTransport),以便创建单个网络传输,该传输可以使用http进行查询和转换,使用web sockets进行订阅。 通常,使用SplitNetworkTransport,因为这允许保留单个网络传输设置,并避免使用多个客户端对象的任何潜在问题。
import Foundation
import Apollo
import ApolloWebSocket
// MARK: - Singleton Wrapper
class Apollo {
static let shared = Apollo()
/// A web socket transport to use for subscriptions
private lazy var webSocketTransport: WebSocketTransport = {
let url = URL(string: "ws://localhost:8080/websocket")!
let request = URLRequest(url: url)
return WebSocketTransport(request: request)
}()
/// An HTTP transport to use for queries and mutations
private lazy var httpTransport: HTTPNetworkTransport = {
let url = URL(string: "http://localhost:8080/graphql")!
return HTTPNetworkTransport(url: url)
}()
/// A split network transport to allow the use of both of the above
/// transports through a single `NetworkTransport` instance.
private lazy var splitNetworkTransport = SplitNetworkTransport(
httpNetworkTransport: self.httpTransport,
webSocketNetworkTransport: self.webSocketTransport
)
/// Create a client using the `SplitNetworkTransport`.
private(set) lazy var client = ApolloClient(networkTransport: self.splitNetworkTransport)
}
class ReviewViewController: UIViewController {
private var subscription: Cancellable?
private var reviewList = [Review]()
// Assume data source and delegate are hooked up in Interface Builder
@IBOutlet private var reviewTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Set the subscription variable up - be careful not to create a retain cycle!
self.subscription = Apollo.shared.client
.subscribe(subscription: ReviewAddedSubscription()) { [weak self] result in
guard let self = self else {
return
}
switch result {
case .success(let graphQLResult):
if let review = graphQLResult.data?.reviewAdded {
// A review was added - append it to the list then reload the data.
self.reviewList.append(review)
self.reviewTableView.reloadData()
} // else, something went wrong and you should check `graphQLResult.error` for problems
case .failure(let error):
// Not included here: Show some kind of alert
}
}
}
deinit {
// Make sure the subscription is cancelled, if it exists, when this object is deallocated.
self.subscription?.cancel()
}
// MARK: - Standard TableView Stuff
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return self.reviewList.count
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Assume `ReviewCell` is a cell for displaying reviews created elsewhere
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? ReviewCell else {
return UITableViewCell()
}
let review = self.reviewList[indexPath.row]
cell.episode = review.episode
cell.stars = review.stars
cell.commentary = review.commentary
return cell
}
}
在标准的HTTP操作中,如果需要身份验证,通常会发送一个授权头和请求。但是,对于web套接字,由于需要持久连接,因此不能将其与每个有效负载一起发送。
对于web套接字,connectingPayload提供了那些通常作为请求头的一部分指定的参数。
import Foundation
import Apollo
import ApolloWebSocket
// MARK: - Singleton Wrapper
let magicToken = "So long and thanks for all the fish"
class Apollo {
static let shared = Apollo()
/// 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 lazy var webSocketTransport: WebSocketTransport = {
let url = URL(string: "ws://localhost:8080/websocket")!
let request = URLRequest(url: url)
let authPayload = ["authToken": magicToken]
return WebSocketTransport(request: request, connectingPayload: authPayload)
}()
/// An HTTP transport to use for queries and mutations.
private lazy var httpTransport: HTTPNetworkTransport = {
let url = URL(string: "http://localhost:8080/graphql")!
return HTTPNetworkTransport(url: url)
}()
/// A split network transport to allow the use of both of the above
/// transports through a single `NetworkTransport` instance.
private lazy var splitNetworkTransport = SplitNetworkTransport(
httpNetworkTransport: self.httpTransport,
webSocketNetworkTransport: self.webSocketTransport
)
/// Create a client using the `SplitNetworkTransport`.
private(set) lazy var client = ApolloClient(networkTransport: self.splitNetworkTransport)
}