Skip to content

Commit

Permalink
Merge pull request #1399 from apollographql/update/moar-networking
Browse files Browse the repository at this point in the history
Networking Beta Tweaks
  • Loading branch information
designatednerd authored Sep 18, 2020
2 parents 7bb480e + 69da90a commit aea6643
Show file tree
Hide file tree
Showing 15 changed files with 197 additions and 54 deletions.
6 changes: 5 additions & 1 deletion Apollo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
9B260C04245A090600562176 /* RequestChainNetworkTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B260C03245A090600562176 /* RequestChainNetworkTransport.swift */; };
9B260C08245A437400562176 /* InterceptorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B260C07245A437400562176 /* InterceptorProvider.swift */; };
9B260C0A245A532500562176 /* LegacyParsingInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B260C09245A532500562176 /* LegacyParsingInterceptor.swift */; };
9B2B66F42513FAFE00B53ABF /* CancellationHandlingInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B2B66F32513FAFE00B53ABF /* CancellationHandlingInterceptor.swift */; };
9B2DFBBF24E1FA1A00ED3AE6 /* Apollo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9FC750441D2A532C00458D91 /* Apollo.framework */; };
9B2DFBC024E1FA1A00ED3AE6 /* Apollo.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9FC750441D2A532C00458D91 /* Apollo.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9B2DFBC724E1FA4800ED3AE6 /* UploadAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B2DFBC524E1FA3E00ED3AE6 /* UploadAPI.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -491,6 +492,7 @@
9B260C03245A090600562176 /* RequestChainNetworkTransport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestChainNetworkTransport.swift; sourceTree = "<group>"; };
9B260C07245A437400562176 /* InterceptorProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterceptorProvider.swift; sourceTree = "<group>"; };
9B260C09245A532500562176 /* LegacyParsingInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyParsingInterceptor.swift; sourceTree = "<group>"; };
9B2B66F32513FAFE00B53ABF /* CancellationHandlingInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancellationHandlingInterceptor.swift; sourceTree = "<group>"; };
9B2DFBB624E1FA0D00ED3AE6 /* UploadAPI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = UploadAPI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9B2DFBC524E1FA3E00ED3AE6 /* UploadAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UploadAPI.h; sourceTree = "<group>"; };
9B2DFBC624E1FA3E00ED3AE6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -627,10 +629,10 @@
9BAEEC14234C132600808306 /* CLIExtractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLIExtractorTests.swift; sourceTree = "<group>"; };
9BAEEC16234C275600808306 /* ApolloSchemaTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApolloSchemaTests.swift; sourceTree = "<group>"; };
9BAEEC18234C297800808306 /* ApolloCodegenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApolloCodegenTests.swift; sourceTree = "<group>"; };
9BB1DAC624A66B2500396235 /* ApolloMacPlayground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = ApolloMacPlayground.playground; path = Playgrounds/ApolloMacPlayground.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
9BC139A224EDCA4400876D29 /* InterceptorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterceptorTests.swift; sourceTree = "<group>"; };
9BC139A524EDCAD900876D29 /* BlindRetryingTestInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlindRetryingTestInterceptor.swift; sourceTree = "<group>"; };
9BC139A724EDCE4F00876D29 /* RetryToCountThenSucceedInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetryToCountThenSucceedInterceptor.swift; sourceTree = "<group>"; };
9BB1DAC624A66B2500396235 /* ApolloMacPlayground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = ApolloMacPlayground.playground; path = Playgrounds/ApolloMacPlayground.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
9BC2D9CE233C3531007BD083 /* Apollo-Target-ApolloCodegen.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Apollo-Target-ApolloCodegen.xcconfig"; sourceTree = "<group>"; };
9BC2D9D1233C6DC0007BD083 /* Basher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Basher.swift; sourceTree = "<group>"; };
9BC742AB24CFB2FF0029282C /* ApolloErrorInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApolloErrorInterceptor.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -932,6 +934,7 @@
9B4F4540244A2A9200C2CF7D /* HTTPBinAPI.swift */,
9BC139A524EDCAD900876D29 /* BlindRetryingTestInterceptor.swift */,
9BC139A724EDCE4F00876D29 /* RetryToCountThenSucceedInterceptor.swift */,
9B2B66F32513FAFE00B53ABF /* CancellationHandlingInterceptor.swift */,
);
name = TestHelpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -2526,6 +2529,7 @@
9FE1C6E71E634C8D00C02284 /* PromiseTests.swift in Sources */,
9B64F6762354D219002D1BB5 /* URL+QueryDict.swift in Sources */,
9FADC8541E6B86D900C677E6 /* DataLoaderTests.swift in Sources */,
9B2B66F42513FAFE00B53ABF /* CancellationHandlingInterceptor.swift in Sources */,
9B21FD772422C8CC00998B5C /* TestFileHelper.swift in Sources */,
9BC139A624EDCAD900876D29 /* BlindRetryingTestInterceptor.swift in Sources */,
9B96500A24BE62B7003C29C0 /* RequestChainTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ import PlaygroundSupport

//: # Setting up a client with a SQLite cache

//: First, you'll need to set up a network transport, since you will also need that to set up the client:
let serverURL = URL(string: "http://localhost:8080/graphql")!
let networkTransport = HTTPNetworkTransport(url: serverURL)

//: You'll need to make sure you import the ApolloSQLite library IF you are not using CocoaPods (CocoaPods will automatically flatten everything down to a single Apollo import):
import ApolloSQLite

Expand All @@ -26,12 +22,16 @@ let sqliteCache = try SQLiteNormalizedCache(fileURL: sqliteFileURL)
//: And then instantiate an instance of `ApolloStore` with the cache you've just created:
let store = ApolloStore(cache: sqliteCache)

//: Next, you'll need to set up a network transport, since you will also need that to set up the client:
let serverURL = URL(string: "http://localhost:8080/graphql")!
let networkTransport = RequestChainNetworkTransport(interceptorProvider: LegacyInterceptorProvider(store: store), endpointURL: serverURL)

//: Finally, pass that into your `ApolloClient` initializer, and you're now set up to use the SQLite cache for persistent storage:
let apolloClient = ApolloClient(networkTransport: networkTransport, store: store)


//: Now, let's test
//: Now, let's test it out against the Star Wars API!
import StarWarsAPI

let query = HeroDetailsQuery(episode: .newhope)
apolloClient.fetch(query: query) { result in
// This is the outer Result, which has either a `GraphQLResult` or an `Error`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ Your web backend must declare support for subscriptions in the Schema just like
To use subscriptions, you need to have a `NetworkTransport` implementation which supports them. Fortunately, with the `ApolloWebSocket` package, there are two!
The first is the `WebSocketTransport`, which works with the web socket, and the second is the `SplitNetworkTransport`, which uses a web socket for subscriptions but a normal `HTTPNetworkTransport` for everything else.
The first is the `WebSocketTransport`, which works with the web socket, and the second is the `SplitNetworkTransport`, which uses a web socket for subscriptions but a normal `RequestChainNetworkTransport` for everything else.
In this instance, we'll use a `SplitNetworkTransport` since we want to demonstrate subscribing to changes, but we need to also be able to send changes for that subscription to come through.
*/

//:First, setup the `HTTPNetworkTransport`:
//:First, setup the `RequestChainNetworkTransport` that will handle your HTTP requests:

let url = URL(string: "http://localhost:8080/graphql")!
let normalTransport = HTTPNetworkTransport(url: url)
let normalTransport = RequestChainNetworkTransport(interceptorProvider: LegacyInterceptorProvider(), endpointURL: url)

//: Next, set up the `WebSocketTransport` to talk to the websocket endpoint. Note that this may take a different URL, sometimes with a `ws` prefix, than your normal http endpoint:

Expand All @@ -34,7 +34,7 @@ let webSocketTransport = WebSocketTransport(request: URLRequest(url: webSocketUR

//: Then, set up the split transport with the two transports you've just created:

let splitTransport = SplitNetworkTransport(httpNetworkTransport: normalTransport, webSocketNetworkTransport: webSocketTransport)
let splitTransport = SplitNetworkTransport(uploadingNetworkTransport: normalTransport, webSocketNetworkTransport: webSocketTransport)

//: Finally, instantiate your client with the split transport:

Expand Down
4 changes: 2 additions & 2 deletions Sources/Apollo/ApolloStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ public final class ApolloStore {

/// Designated initializer
///
/// - Parameter cache: An instance of `normalizedCache` to use to cache results.
public init(cache: NormalizedCache) {
/// - Parameter cache: An instance of `normalizedCache` to use to cache results. Defaults to an `InMemoryNormalizedCache`.
public init(cache: NormalizedCache = InMemoryNormalizedCache()) {
self.cache = cache
queue = DispatchQueue(label: "com.apollographql.ApolloStore", attributes: .concurrent)
}
Expand Down
18 changes: 16 additions & 2 deletions Sources/Apollo/InterceptorProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ public protocol InterceptorProvider {
///
/// - Parameter operation: The operation to provide interceptors for
func interceptors<Operation: GraphQLOperation>(for operation: Operation) -> [ApolloInterceptor]

/// Provides an additional error interceptor for any additional handling of errors
/// before returning to the UI, such as logging.
/// - Parameter operation: The oper
func additionalErrorInterceptor<Operation: GraphQLOperation>(for operation: Operation) -> ApolloErrorInterceptor?
}

/// MARK: - Default Implementation

public extension InterceptorProvider {

func additionalErrorInterceptor<Operation: GraphQLOperation>(for operation: Operation) -> ApolloErrorInterceptor? {
return nil
}
}

// MARK: - Default implementation for typescript codegen
Expand All @@ -25,10 +39,10 @@ open class LegacyInterceptorProvider: InterceptorProvider {
/// - Parameters:
/// - client: The `URLSessionClient` to use. Defaults to the default setup.
/// - shouldInvalidateClientOnDeinit: If the passed-in client should be invalidated when this interceptor provider is deinitialized. If you are recreating the `URLSessionClient` every time you create a new provider, you should do this to prevent memory leaks. Defaults to true, since by default we provide a `URLSessionClient` to new instances.
/// - store: The `ApolloStore` to use when reading from or writing to the cache.
/// - store: The `ApolloStore` to use when reading from or writing to the cache. Defaults to the default initializer for ApolloStore.
public init(client: URLSessionClient = URLSessionClient(),
shouldInvalidateClientOnDeinit: Bool = true,
store: ApolloStore) {
store: ApolloStore = ApolloStore()) {
self.client = client
self.shouldInvalidateClientOnDeinit = shouldInvalidateClientOnDeinit
self.store = store
Expand Down
6 changes: 3 additions & 3 deletions Sources/Apollo/JSONRequest.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

/// A request which sends JSON related to a GraphQL operation.
public class JSONRequest<Operation: GraphQLOperation>: HTTPRequest<Operation> {
open class JSONRequest<Operation: GraphQLOperation>: HTTPRequest<Operation> {

public let requestCreator: RequestCreator

Expand Down Expand Up @@ -52,11 +52,11 @@ public class JSONRequest<Operation: GraphQLOperation>: HTTPRequest<Operation> {
cachePolicy: cachePolicy)
}

public var sendOperationIdentifier: Bool {
open var sendOperationIdentifier: Bool {
self.operation.operationIdentifier != nil
}

public override func toURLRequest() throws -> URLRequest {
open override func toURLRequest() throws -> URLRequest {
var request = try super.toURLRequest()

let useGetMethod: Bool
Expand Down
1 change: 1 addition & 0 deletions Sources/Apollo/RequestChainNetworkTransport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public class RequestChainNetworkTransport: NetworkTransport {

let interceptors = self.interceptorProvider.interceptors(for: operation)
let chain = RequestChain(interceptors: interceptors, callbackQueue: callbackQueue)
chain.additionalErrorHandler = self.interceptorProvider.additionalErrorInterceptor(for: operation)
let request = self.constructJSONRequest(for: operation,
cachePolicy: cachePolicy,
contextIdentifier: contextIdentifier)
Expand Down
33 changes: 11 additions & 22 deletions Tests/ApolloTests/AutomaticPersistedQueriesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {

func testRequestBody() throws {
let mockClient = MockURLSessionClient()
let store = ApolloStore(cache: InMemoryNormalizedCache())
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
let provider = LegacyInterceptorProvider(client: mockClient)
let network = RequestChainNetworkTransport(interceptorProvider: provider,
endpointURL: self.endpoint)

Expand All @@ -257,8 +256,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {

func testRequestBodyWithVariable() throws {
let mockClient = MockURLSessionClient()
let store = ApolloStore(cache: InMemoryNormalizedCache())
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
let provider = LegacyInterceptorProvider(client: mockClient)
let network = RequestChainNetworkTransport(interceptorProvider: provider,
endpointURL: self.endpoint)

Expand All @@ -283,8 +281,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {

func testRequestBodyForAPQsWithVariable() throws {
let mockClient = MockURLSessionClient()
let store = ApolloStore(cache: InMemoryNormalizedCache())
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
let provider = LegacyInterceptorProvider(client: mockClient)
let network = RequestChainNetworkTransport(interceptorProvider: provider,
endpointURL: self.endpoint,
autoPersistQueries: true)
Expand All @@ -310,8 +307,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {

func testMutationRequestBodyForAPQs() throws {
let mockClient = MockURLSessionClient()
let store = ApolloStore(cache: InMemoryNormalizedCache())
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
let provider = LegacyInterceptorProvider(client: mockClient)
let network = RequestChainNetworkTransport(interceptorProvider: provider,
endpointURL: self.endpoint,
autoPersistQueries: true)
Expand All @@ -337,8 +333,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {

func testQueryStringForAPQsUseGetMethod() throws {
let mockClient = MockURLSessionClient()
let store = ApolloStore(cache: InMemoryNormalizedCache())
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
let provider = LegacyInterceptorProvider(client: mockClient)
let network = RequestChainNetworkTransport(interceptorProvider: provider,
endpointURL: self.endpoint,
autoPersistQueries: true,
Expand All @@ -363,8 +358,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {

func testQueryStringForAPQsUseGetMethodWithVariable() throws {
let mockClient = MockURLSessionClient()
let store = ApolloStore(cache: InMemoryNormalizedCache())
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
let provider = LegacyInterceptorProvider(client: mockClient)
let network = RequestChainNetworkTransport(interceptorProvider: provider,
endpointURL: self.endpoint,
autoPersistQueries: true,
Expand All @@ -391,8 +385,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {

func testUseGETForQueriesRequest() throws {
let mockClient = MockURLSessionClient()
let store = ApolloStore(cache: InMemoryNormalizedCache())
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
let provider = LegacyInterceptorProvider(client: mockClient)
let network = RequestChainNetworkTransport(interceptorProvider: provider,
endpointURL: self.endpoint,
useGETForQueries: true)
Expand All @@ -418,8 +411,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {

func testNotUseGETForQueriesRequest() throws {
let mockClient = MockURLSessionClient()
let store = ApolloStore(cache: InMemoryNormalizedCache())
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
let provider = LegacyInterceptorProvider(client: mockClient)
let network = RequestChainNetworkTransport(interceptorProvider: provider,
endpointURL: self.endpoint)

Expand All @@ -444,8 +436,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {

func testNotUseGETForQueriesAPQsRequest() throws {
let mockClient = MockURLSessionClient()
let store = ApolloStore(cache: InMemoryNormalizedCache())
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
let provider = LegacyInterceptorProvider(client: mockClient)
let network = RequestChainNetworkTransport(interceptorProvider: provider,
endpointURL: self.endpoint,
autoPersistQueries: true)
Expand All @@ -471,8 +462,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {

func testUseGETForQueriesAPQsRequest() throws {
let mockClient = MockURLSessionClient()
let store = ApolloStore(cache: InMemoryNormalizedCache())
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
let provider = LegacyInterceptorProvider(client: mockClient)
let network = RequestChainNetworkTransport(interceptorProvider: provider,
endpointURL: self.endpoint,
autoPersistQueries: true,
Expand All @@ -499,8 +489,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {

func testNotUseGETForQueriesAPQsGETRequest() throws {
let mockClient = MockURLSessionClient()
let store = ApolloStore(cache: InMemoryNormalizedCache())
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
let provider = LegacyInterceptorProvider(client: mockClient)
let network = RequestChainNetworkTransport(interceptorProvider: provider,
endpointURL: self.endpoint,
autoPersistQueries: true,
Expand Down
8 changes: 7 additions & 1 deletion Tests/ApolloTests/BlindRetryingTestInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import Apollo
// An interceptor which blindly retries every time it receives a request.
class BlindRetryingTestInterceptor: ApolloInterceptor {
var hitCount = 0

private(set) var hasBeenCancelled = false

func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
Expand All @@ -22,4 +23,9 @@ class BlindRetryingTestInterceptor: ApolloInterceptor {
chain.retry(request: request,
completion: completion)
}

// Purposely not adhering to `Cancellable` here to make sure non `Cancellable` interceptors don't have this called.
func cancel() {
self.hasBeenCancelled = true
}
}
Loading

0 comments on commit aea6643

Please sign in to comment.