订阅
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) }