Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Networking Beta Tweaks #1399

Merged
merged 6 commits into from
Sep 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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