Created
November 5, 2017 20:20
-
-
Save cerupcat/e3b04a523f023813db3f6aad8a667201 to your computer and use it in GitHub Desktop.
Apollo HTTPNetworkClient with Mock
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// AuthHTTPNetworkTransport.swift | |
// | |
// Created by Seth Sandler on 9/13/17. | |
// Copyright © 2017 Workpop. All rights reserved. | |
// | |
import Foundation | |
import Apollo | |
/// Based on HTTPNetworkTransport from Apollo to add Authentication header dynamically | |
public class AuthHTTPNetworkTransport: NetworkTransport { | |
enum GQLError: Error { | |
case errorResponse(body: Data?, response: HTTPURLResponse) | |
case invalidResponse(body: Data?, response: HTTPURLResponse) | |
} | |
let url: URL | |
let session: URLSession | |
let serializationFormat = JSONSerializationFormat.self | |
public var useMockData: Bool | |
public init(url: URL, configuration: URLSessionConfiguration = URLSessionConfiguration.default, sendOperationIdentifiers: Bool = false) { | |
self.url = url | |
self.session = URLSession(configuration: configuration) | |
self.sendOperationIdentifiers = sendOperationIdentifiers | |
self.useMockData = false | |
} | |
fileprivate func handleRequest<Operation: GraphQLOperation>(operation: Operation, completionHandler: @escaping (GraphQLResponse<Operation>?, Error?) -> Void) -> Cancellable { | |
var request = URLRequest(url: url) | |
request.httpMethod = "POST" | |
request.setValue("application/json", forHTTPHeaderField: "Content-Type") | |
// set meteor resume token | |
// if let currentResumeToken = MeteorClient().resumeToken() { | |
// request.setValue(currentResumeToken, forHTTPHeaderField: "meteor-login-token") | |
// } | |
let body = requestBody(for: operation) | |
request.httpBody = try! serializationFormat.serialize(value: body) | |
let task = session.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in | |
if error != nil { | |
completionHandler(nil, error) | |
return | |
} | |
guard let httpResponse = response as? HTTPURLResponse else { | |
fatalError("Response should be an HTTPURLResponse") | |
} | |
if (httpResponse.statusCode != 200) { | |
completionHandler(nil, GQLError.errorResponse(body: data, response: httpResponse)) | |
return | |
} | |
guard let data = data else { | |
completionHandler(nil, GQLError.invalidResponse(body: nil, response: httpResponse)) | |
return | |
} | |
do { | |
guard let body = try self.serializationFormat.deserialize(data: data) as? JSONObject else { | |
completionHandler(nil, GQLError.invalidResponse(body: data, response: httpResponse)) | |
return | |
} | |
let response = GraphQLResponse(operation: operation, body: body) | |
completionHandler(response, nil) | |
} catch { | |
completionHandler(nil, error) | |
} | |
} | |
task.resume() | |
return task | |
} | |
public func send<Operation: GraphQLOperation>(operation: Operation, completionHandler: @escaping (GraphQLResponse<Operation>?, Error?) -> Void) -> Cancellable { | |
// if using mock data | |
if useMockData { | |
// get mock json file | |
if let mockJSON = getMockJSON(forOperationString: type(of: operation).operationString, variables: operation.variables) { | |
DispatchQueue.global(qos: .default).async { | |
// skip network call and return mock json file | |
completionHandler(GraphQLResponse(operation: operation, body: mockJSON), nil) | |
} | |
return MockTask() | |
} else { | |
// send request over network | |
return handleRequest(operation: operation, completionHandler: completionHandler) | |
} | |
} else { | |
// send request over network | |
return handleRequest(operation: operation, completionHandler: completionHandler) | |
} | |
} | |
private let sendOperationIdentifiers: Bool | |
private func requestBody<Operation: GraphQLOperation>(for operation: Operation) -> GraphQLMap { | |
if sendOperationIdentifiers { | |
guard let operationIdentifier = type(of: operation).operationIdentifier else { | |
preconditionFailure("To send operation identifiers, Apollo types must be generated with operationIdentifiers") | |
} | |
return ["id": operationIdentifier, "variables": operation.variables] | |
} | |
return ["query": type(of: operation).requestString, "variables": operation.variables] | |
} | |
// MARK: - Mock Data | |
fileprivate func getMockJSON(forOperationString operationString: String, variables: GraphQLMap?) -> JSONObject? { | |
var body: JSONObject? | |
switch operationString { | |
// if the operation string mattaches GraphQLQuery.operationString | |
case ExampleQuery.operationString: | |
if let json = "exampleQuery_1".loadJsonSchema() { | |
body = json | |
} | |
case ExampleQuery2.operationString: | |
guard let exampleId: String = variables!["exampleId"].jsonValue as? String else { | |
break | |
} | |
if exampleId == "1" { | |
if let json = "exampleQuery2_1".loadJsonSchema() { | |
body = json | |
} | |
} else if exampleId == "2" { | |
if let json = "exampleQuery2_2".loadJsonSchema() { | |
body = json | |
} | |
} | |
default: | |
break | |
} | |
// return json body | |
return body | |
} | |
private final class MockTask: Cancellable { | |
func cancel() { | |
} | |
} | |
} | |
public extension String { | |
func loadJsonSchema() -> JSONObject? { | |
// loading from cocoapod bundle we use | |
let bundle = Bundle(identifier: "org.cocoapods.NameOfCocoapod") | |
if let url = bundle?.url(forResource: self, withExtension: "json") { | |
if let data = NSData(contentsOf: url) { | |
do { | |
let jsonObject = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as? JSONObject | |
return jsonObject | |
} catch { | |
print("Error!! Unable to parse \(self).json") | |
} | |
} | |
print("Error!! Unable to load \(self).json") | |
} | |
return nil | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment