Skip to content

Commit aea6643

Browse files
Merge pull request #1399 from apollographql/update/moar-networking
Networking Beta Tweaks
2 parents 7bb480e + 69da90a commit aea6643

15 files changed

Lines changed: 197 additions & 54 deletions

File tree

Apollo.xcodeproj/project.pbxproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
9B260C04245A090600562176 /* RequestChainNetworkTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B260C03245A090600562176 /* RequestChainNetworkTransport.swift */; };
3333
9B260C08245A437400562176 /* InterceptorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B260C07245A437400562176 /* InterceptorProvider.swift */; };
3434
9B260C0A245A532500562176 /* LegacyParsingInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B260C09245A532500562176 /* LegacyParsingInterceptor.swift */; };
35+
9B2B66F42513FAFE00B53ABF /* CancellationHandlingInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B2B66F32513FAFE00B53ABF /* CancellationHandlingInterceptor.swift */; };
3536
9B2DFBBF24E1FA1A00ED3AE6 /* Apollo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9FC750441D2A532C00458D91 /* Apollo.framework */; };
3637
9B2DFBC024E1FA1A00ED3AE6 /* Apollo.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9FC750441D2A532C00458D91 /* Apollo.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
3738
9B2DFBC724E1FA4800ED3AE6 /* UploadAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B2DFBC524E1FA3E00ED3AE6 /* UploadAPI.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -491,6 +492,7 @@
491492
9B260C03245A090600562176 /* RequestChainNetworkTransport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestChainNetworkTransport.swift; sourceTree = "<group>"; };
492493
9B260C07245A437400562176 /* InterceptorProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterceptorProvider.swift; sourceTree = "<group>"; };
493494
9B260C09245A532500562176 /* LegacyParsingInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyParsingInterceptor.swift; sourceTree = "<group>"; };
495+
9B2B66F32513FAFE00B53ABF /* CancellationHandlingInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancellationHandlingInterceptor.swift; sourceTree = "<group>"; };
494496
9B2DFBB624E1FA0D00ED3AE6 /* UploadAPI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = UploadAPI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
495497
9B2DFBC524E1FA3E00ED3AE6 /* UploadAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UploadAPI.h; sourceTree = "<group>"; };
496498
9B2DFBC624E1FA3E00ED3AE6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -627,10 +629,10 @@
627629
9BAEEC14234C132600808306 /* CLIExtractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLIExtractorTests.swift; sourceTree = "<group>"; };
628630
9BAEEC16234C275600808306 /* ApolloSchemaTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApolloSchemaTests.swift; sourceTree = "<group>"; };
629631
9BAEEC18234C297800808306 /* ApolloCodegenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApolloCodegenTests.swift; sourceTree = "<group>"; };
632+
9BB1DAC624A66B2500396235 /* ApolloMacPlayground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = ApolloMacPlayground.playground; path = Playgrounds/ApolloMacPlayground.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
630633
9BC139A224EDCA4400876D29 /* InterceptorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterceptorTests.swift; sourceTree = "<group>"; };
631634
9BC139A524EDCAD900876D29 /* BlindRetryingTestInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlindRetryingTestInterceptor.swift; sourceTree = "<group>"; };
632635
9BC139A724EDCE4F00876D29 /* RetryToCountThenSucceedInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetryToCountThenSucceedInterceptor.swift; sourceTree = "<group>"; };
633-
9BB1DAC624A66B2500396235 /* ApolloMacPlayground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = ApolloMacPlayground.playground; path = Playgrounds/ApolloMacPlayground.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
634636
9BC2D9CE233C3531007BD083 /* Apollo-Target-ApolloCodegen.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Apollo-Target-ApolloCodegen.xcconfig"; sourceTree = "<group>"; };
635637
9BC2D9D1233C6DC0007BD083 /* Basher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Basher.swift; sourceTree = "<group>"; };
636638
9BC742AB24CFB2FF0029282C /* ApolloErrorInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApolloErrorInterceptor.swift; sourceTree = "<group>"; };
@@ -932,6 +934,7 @@
932934
9B4F4540244A2A9200C2CF7D /* HTTPBinAPI.swift */,
933935
9BC139A524EDCAD900876D29 /* BlindRetryingTestInterceptor.swift */,
934936
9BC139A724EDCE4F00876D29 /* RetryToCountThenSucceedInterceptor.swift */,
937+
9B2B66F32513FAFE00B53ABF /* CancellationHandlingInterceptor.swift */,
935938
);
936939
name = TestHelpers;
937940
sourceTree = "<group>";
@@ -2526,6 +2529,7 @@
25262529
9FE1C6E71E634C8D00C02284 /* PromiseTests.swift in Sources */,
25272530
9B64F6762354D219002D1BB5 /* URL+QueryDict.swift in Sources */,
25282531
9FADC8541E6B86D900C677E6 /* DataLoaderTests.swift in Sources */,
2532+
9B2B66F42513FAFE00B53ABF /* CancellationHandlingInterceptor.swift in Sources */,
25292533
9B21FD772422C8CC00998B5C /* TestFileHelper.swift in Sources */,
25302534
9BC139A624EDCAD900876D29 /* BlindRetryingTestInterceptor.swift in Sources */,
25312535
9B96500A24BE62B7003C29C0 /* RequestChainTests.swift in Sources */,

Playgrounds/ApolloMacPlayground.playground/Pages/SQLiteCache.xcplaygroundpage/Contents.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ import PlaygroundSupport
66

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

9-
//: First, you'll need to set up a network transport, since you will also need that to set up the client:
10-
let serverURL = URL(string: "http://localhost:8080/graphql")!
11-
let networkTransport = HTTPNetworkTransport(url: serverURL)
12-
139
//: 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):
1410
import ApolloSQLite
1511

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

25+
//: Next, you'll need to set up a network transport, since you will also need that to set up the client:
26+
let serverURL = URL(string: "http://localhost:8080/graphql")!
27+
let networkTransport = RequestChainNetworkTransport(interceptorProvider: LegacyInterceptorProvider(store: store), endpointURL: serverURL)
28+
2929
//: Finally, pass that into your `ApolloClient` initializer, and you're now set up to use the SQLite cache for persistent storage:
3030
let apolloClient = ApolloClient(networkTransport: networkTransport, store: store)
3131

32-
33-
//: Now, let's test
32+
//: Now, let's test it out against the Star Wars API!
3433
import StarWarsAPI
34+
3535
let query = HeroDetailsQuery(episode: .newhope)
3636
apolloClient.fetch(query: query) { result in
3737
// This is the outer Result, which has either a `GraphQLResult` or an `Error`

Playgrounds/ApolloMacPlayground.playground/Pages/Subscriptions.xcplaygroundpage/Contents.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ Your web backend must declare support for subscriptions in the Schema just like
1717

1818
To use subscriptions, you need to have a `NetworkTransport` implementation which supports them. Fortunately, with the `ApolloWebSocket` package, there are two!
1919

20-
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.
20+
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.
2121

2222
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.
2323
*/
2424

25-
//:First, setup the `HTTPNetworkTransport`:
25+
//:First, setup the `RequestChainNetworkTransport` that will handle your HTTP requests:
2626

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

3030
//: 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:
3131

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

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

37-
let splitTransport = SplitNetworkTransport(httpNetworkTransport: normalTransport, webSocketNetworkTransport: webSocketTransport)
37+
let splitTransport = SplitNetworkTransport(uploadingNetworkTransport: normalTransport, webSocketNetworkTransport: webSocketTransport)
3838

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

Sources/Apollo/ApolloStore.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ public final class ApolloStore {
4444

4545
/// Designated initializer
4646
///
47-
/// - Parameter cache: An instance of `normalizedCache` to use to cache results.
48-
public init(cache: NormalizedCache) {
47+
/// - Parameter cache: An instance of `normalizedCache` to use to cache results. Defaults to an `InMemoryNormalizedCache`.
48+
public init(cache: NormalizedCache = InMemoryNormalizedCache()) {
4949
self.cache = cache
5050
queue = DispatchQueue(label: "com.apollographql.ApolloStore", attributes: .concurrent)
5151
}

Sources/Apollo/InterceptorProvider.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@ public protocol InterceptorProvider {
99
///
1010
/// - Parameter operation: The operation to provide interceptors for
1111
func interceptors<Operation: GraphQLOperation>(for operation: Operation) -> [ApolloInterceptor]
12+
13+
/// Provides an additional error interceptor for any additional handling of errors
14+
/// before returning to the UI, such as logging.
15+
/// - Parameter operation: The oper
16+
func additionalErrorInterceptor<Operation: GraphQLOperation>(for operation: Operation) -> ApolloErrorInterceptor?
17+
}
18+
19+
/// MARK: - Default Implementation
20+
21+
public extension InterceptorProvider {
22+
23+
func additionalErrorInterceptor<Operation: GraphQLOperation>(for operation: Operation) -> ApolloErrorInterceptor? {
24+
return nil
25+
}
1226
}
1327

1428
// MARK: - Default implementation for typescript codegen
@@ -25,10 +39,10 @@ open class LegacyInterceptorProvider: InterceptorProvider {
2539
/// - Parameters:
2640
/// - client: The `URLSessionClient` to use. Defaults to the default setup.
2741
/// - 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.
28-
/// - store: The `ApolloStore` to use when reading from or writing to the cache.
42+
/// - store: The `ApolloStore` to use when reading from or writing to the cache. Defaults to the default initializer for ApolloStore.
2943
public init(client: URLSessionClient = URLSessionClient(),
3044
shouldInvalidateClientOnDeinit: Bool = true,
31-
store: ApolloStore) {
45+
store: ApolloStore = ApolloStore()) {
3246
self.client = client
3347
self.shouldInvalidateClientOnDeinit = shouldInvalidateClientOnDeinit
3448
self.store = store

Sources/Apollo/JSONRequest.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Foundation
22

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

66
public let requestCreator: RequestCreator
77

@@ -52,11 +52,11 @@ public class JSONRequest<Operation: GraphQLOperation>: HTTPRequest<Operation> {
5252
cachePolicy: cachePolicy)
5353
}
5454

55-
public var sendOperationIdentifier: Bool {
55+
open var sendOperationIdentifier: Bool {
5656
self.operation.operationIdentifier != nil
5757
}
5858

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

6262
let useGetMethod: Bool

Sources/Apollo/RequestChainNetworkTransport.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public class RequestChainNetworkTransport: NetworkTransport {
7373

7474
let interceptors = self.interceptorProvider.interceptors(for: operation)
7575
let chain = RequestChain(interceptors: interceptors, callbackQueue: callbackQueue)
76+
chain.additionalErrorHandler = self.interceptorProvider.additionalErrorInterceptor(for: operation)
7677
let request = self.constructJSONRequest(for: operation,
7778
cachePolicy: cachePolicy,
7879
contextIdentifier: contextIdentifier)

Tests/ApolloTests/AutomaticPersistedQueriesTests.swift

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {
231231

232232
func testRequestBody() throws {
233233
let mockClient = MockURLSessionClient()
234-
let store = ApolloStore(cache: InMemoryNormalizedCache())
235-
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
234+
let provider = LegacyInterceptorProvider(client: mockClient)
236235
let network = RequestChainNetworkTransport(interceptorProvider: provider,
237236
endpointURL: self.endpoint)
238237

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

258257
func testRequestBodyWithVariable() throws {
259258
let mockClient = MockURLSessionClient()
260-
let store = ApolloStore(cache: InMemoryNormalizedCache())
261-
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
259+
let provider = LegacyInterceptorProvider(client: mockClient)
262260
let network = RequestChainNetworkTransport(interceptorProvider: provider,
263261
endpointURL: self.endpoint)
264262

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

284282
func testRequestBodyForAPQsWithVariable() throws {
285283
let mockClient = MockURLSessionClient()
286-
let store = ApolloStore(cache: InMemoryNormalizedCache())
287-
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
284+
let provider = LegacyInterceptorProvider(client: mockClient)
288285
let network = RequestChainNetworkTransport(interceptorProvider: provider,
289286
endpointURL: self.endpoint,
290287
autoPersistQueries: true)
@@ -310,8 +307,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {
310307

311308
func testMutationRequestBodyForAPQs() throws {
312309
let mockClient = MockURLSessionClient()
313-
let store = ApolloStore(cache: InMemoryNormalizedCache())
314-
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
310+
let provider = LegacyInterceptorProvider(client: mockClient)
315311
let network = RequestChainNetworkTransport(interceptorProvider: provider,
316312
endpointURL: self.endpoint,
317313
autoPersistQueries: true)
@@ -337,8 +333,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {
337333

338334
func testQueryStringForAPQsUseGetMethod() throws {
339335
let mockClient = MockURLSessionClient()
340-
let store = ApolloStore(cache: InMemoryNormalizedCache())
341-
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
336+
let provider = LegacyInterceptorProvider(client: mockClient)
342337
let network = RequestChainNetworkTransport(interceptorProvider: provider,
343338
endpointURL: self.endpoint,
344339
autoPersistQueries: true,
@@ -363,8 +358,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {
363358

364359
func testQueryStringForAPQsUseGetMethodWithVariable() throws {
365360
let mockClient = MockURLSessionClient()
366-
let store = ApolloStore(cache: InMemoryNormalizedCache())
367-
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
361+
let provider = LegacyInterceptorProvider(client: mockClient)
368362
let network = RequestChainNetworkTransport(interceptorProvider: provider,
369363
endpointURL: self.endpoint,
370364
autoPersistQueries: true,
@@ -391,8 +385,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {
391385

392386
func testUseGETForQueriesRequest() throws {
393387
let mockClient = MockURLSessionClient()
394-
let store = ApolloStore(cache: InMemoryNormalizedCache())
395-
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
388+
let provider = LegacyInterceptorProvider(client: mockClient)
396389
let network = RequestChainNetworkTransport(interceptorProvider: provider,
397390
endpointURL: self.endpoint,
398391
useGETForQueries: true)
@@ -418,8 +411,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {
418411

419412
func testNotUseGETForQueriesRequest() throws {
420413
let mockClient = MockURLSessionClient()
421-
let store = ApolloStore(cache: InMemoryNormalizedCache())
422-
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
414+
let provider = LegacyInterceptorProvider(client: mockClient)
423415
let network = RequestChainNetworkTransport(interceptorProvider: provider,
424416
endpointURL: self.endpoint)
425417

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

445437
func testNotUseGETForQueriesAPQsRequest() throws {
446438
let mockClient = MockURLSessionClient()
447-
let store = ApolloStore(cache: InMemoryNormalizedCache())
448-
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
439+
let provider = LegacyInterceptorProvider(client: mockClient)
449440
let network = RequestChainNetworkTransport(interceptorProvider: provider,
450441
endpointURL: self.endpoint,
451442
autoPersistQueries: true)
@@ -471,8 +462,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {
471462

472463
func testUseGETForQueriesAPQsRequest() throws {
473464
let mockClient = MockURLSessionClient()
474-
let store = ApolloStore(cache: InMemoryNormalizedCache())
475-
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
465+
let provider = LegacyInterceptorProvider(client: mockClient)
476466
let network = RequestChainNetworkTransport(interceptorProvider: provider,
477467
endpointURL: self.endpoint,
478468
autoPersistQueries: true,
@@ -499,8 +489,7 @@ class AutomaticPersistedQueriesTests: XCTestCase {
499489

500490
func testNotUseGETForQueriesAPQsGETRequest() throws {
501491
let mockClient = MockURLSessionClient()
502-
let store = ApolloStore(cache: InMemoryNormalizedCache())
503-
let provider = LegacyInterceptorProvider(client: mockClient, store: store)
492+
let provider = LegacyInterceptorProvider(client: mockClient)
504493
let network = RequestChainNetworkTransport(interceptorProvider: provider,
505494
endpointURL: self.endpoint,
506495
autoPersistQueries: true,

Tests/ApolloTests/BlindRetryingTestInterceptor.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import Apollo
1212
// An interceptor which blindly retries every time it receives a request.
1313
class BlindRetryingTestInterceptor: ApolloInterceptor {
1414
var hitCount = 0
15-
15+
private(set) var hasBeenCancelled = false
16+
1617
func interceptAsync<Operation: GraphQLOperation>(
1718
chain: RequestChain,
1819
request: HTTPRequest<Operation>,
@@ -22,4 +23,9 @@ class BlindRetryingTestInterceptor: ApolloInterceptor {
2223
chain.retry(request: request,
2324
completion: completion)
2425
}
26+
27+
// Purposely not adhering to `Cancellable` here to make sure non `Cancellable` interceptors don't have this called.
28+
func cancel() {
29+
self.hasBeenCancelled = true
30+
}
2531
}

0 commit comments

Comments
 (0)