import Apollo
import ApolloSQLite
import Foundation
final class Network {
static let backendURL = URL(string: "<https://example.com>")!
static let shared = Network()
// MARK: Apollo
private lazy var networkTransport: NetworkTransport = {
let client = URLSessionClient()
let provider = Provider(
client: client,
shouldInvalidateClientOnDeinit: true,
store: store ?? fallbackStore
)
return RequestChainNetworkTransport(
interceptorProvider: provider,
endpointURL: Network.backendURL.appendingPathComponent("graphql", isDirectory: true)
)
}()
private lazy var fallbackStore = ApolloStore(cache: InMemoryNormalizedCache())
private lazy var store: ApolloStore? = {
guard let sqliteUrl = FileManager()
.urls(for: .documentDirectory, in: .userDomainMask)
.first?.appendingPathComponent("project_name.sqlite") else { return nil }
guard let sqliteCache = try? SQLiteNormalizedCache(fileURL: sqliteUrl) else { return nil }
return ApolloStore(cache: sqliteCache)
}()
private(set) lazy var apollo: ApolloClient = {
let apollo = ApolloClient(networkTransport: networkTransport, store: store ?? fallbackStore)
apollo.cacheKeyForObject = { $0["id"] }
return apollo
}()
}
private class Provider: LegacyInterceptorProvider {
override func interceptors<Operation: GraphQLOperation>(for operation: Operation) -> [ApolloInterceptor] {
var interceptors = super.interceptors(for: operation)
interceptors.insert(TokenAddingInterceptor(), at: 0)
return interceptors
}
}
private class TokenAddingInterceptor: ApolloInterceptor {
func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void
) {
if let token = UserDefaults.authToken {
request.addHeader(name: "Authorization", value: "JWT \\(token)")
}
request.addHeader(name: "Accept-Language", value: NSLocale.preferredLanguages.joined(separator: ", "))
chain.proceedAsync(request: request, response: response, completion: completion)
}
}