From f4a0b23cab1e231214634e3efce09cb9aed6efa1 Mon Sep 17 00:00:00 2001 From: Ellen Shapiro Date: Tue, 29 Oct 2019 13:06:13 -0700 Subject: [PATCH 1/7] Add collection extensions to check `isEmpty` in a more readable fashion, esp in guard statements --- Apollo.xcodeproj/project.pbxproj | 4 +++ Sources/Apollo/Collection+Helpers.swift | 42 +++++++++++++++++++++++ Sources/Apollo/GraphQLSelectionSet.swift | 8 +++-- Sources/Apollo/HTTPNetworkTransport.swift | 27 ++++++++------- Sources/Apollo/Promise.swift | 6 ++-- 5 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 Sources/Apollo/Collection+Helpers.swift diff --git a/Apollo.xcodeproj/project.pbxproj b/Apollo.xcodeproj/project.pbxproj index 5241743ae7..b1afc7b665 100644 --- a/Apollo.xcodeproj/project.pbxproj +++ b/Apollo.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 9BDE43D122C6655300FD7C7F /* Cancellable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BDE43D022C6655200FD7C7F /* Cancellable.swift */; }; 9BDE43DD22C6705300FD7C7F /* GraphQLHTTPResponseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BDE43DC22C6705300FD7C7F /* GraphQLHTTPResponseError.swift */; }; 9BDE43DF22C6708600FD7C7F /* GraphQLHTTPRequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BDE43DE22C6708600FD7C7F /* GraphQLHTTPRequestError.swift */; }; + 9BE071AD2368D08700FA5952 /* Collection+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BE071AC2368D08700FA5952 /* Collection+Helpers.swift */; }; 9BEDC79E22E5D2CF00549BF6 /* RequestCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BEDC79D22E5D2CF00549BF6 /* RequestCreator.swift */; }; 9BF1A94F22CA5784005292C2 /* HTTPTransportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BF1A94C22CA54F9005292C2 /* HTTPTransportTests.swift */; }; 9BF1A95122CA6E71005292C2 /* GraphQLGETTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BF1A95022CA6E71005292C2 /* GraphQLGETTransformer.swift */; }; @@ -283,6 +284,7 @@ 9BDE43D022C6655200FD7C7F /* Cancellable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cancellable.swift; sourceTree = ""; }; 9BDE43DC22C6705300FD7C7F /* GraphQLHTTPResponseError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLHTTPResponseError.swift; sourceTree = ""; }; 9BDE43DE22C6708600FD7C7F /* GraphQLHTTPRequestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLHTTPRequestError.swift; sourceTree = ""; }; + 9BE071AC2368D08700FA5952 /* Collection+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+Helpers.swift"; sourceTree = ""; }; 9BEDC79D22E5D2CF00549BF6 /* RequestCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestCreator.swift; sourceTree = ""; }; 9BF1A94C22CA54F9005292C2 /* HTTPTransportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPTransportTests.swift; sourceTree = ""; }; 9BF1A95022CA6E71005292C2 /* GraphQLGETTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLGETTransformer.swift; sourceTree = ""; }; @@ -727,6 +729,7 @@ children = ( 9FCDFD221E33A0D8007519DC /* AsynchronousOperation.swift */, 9B1CCDD82360F02C007C9032 /* Bundle+Helpers.swift */, + 9BE071AC2368D08700FA5952 /* Collection+Helpers.swift */, 9BA3130D2302BEA5007B7FC5 /* DispatchQueue+Optional.swift */, 9FE941CF1E62C771007CDD89 /* Promise.swift */, 9BA1245D22DE116B00BF1D24 /* Result+Helpers.swift */, @@ -1242,6 +1245,7 @@ 9F27D4641D40379500715680 /* JSONStandardTypeConversions.swift in Sources */, 9B1A38532332AF6F00325FB4 /* String+SHA.swift in Sources */, 9BEDC79E22E5D2CF00549BF6 /* RequestCreator.swift in Sources */, + 9BE071AD2368D08700FA5952 /* Collection+Helpers.swift in Sources */, 9FA6F3681E65DF4700BF8D73 /* GraphQLResultAccumulator.swift in Sources */, 9FF90A651DDDEB100034C3B6 /* GraphQLExecutor.swift in Sources */, 9FC750611D2A59C300458D91 /* GraphQLOperation.swift in Sources */, diff --git a/Sources/Apollo/Collection+Helpers.swift b/Sources/Apollo/Collection+Helpers.swift new file mode 100644 index 0000000000..6f25344cee --- /dev/null +++ b/Sources/Apollo/Collection+Helpers.swift @@ -0,0 +1,42 @@ +// +// Emptiable+Helpers.swift +// Apollo +// +// Created by Ellen Shapiro on 10/29/19. +// Copyright © 2019 Apollo GraphQL. All rights reserved. +// + +import Foundation + +extension Collection { + + /// Convenience helper to make `guard` statements more readable + /// + /// - returns: `true` if the collection has contents. + var isNotEmpty: Bool { + return !self.isEmpty + } +} + +extension Optional where Wrapped: Collection { + + /// - returns: `true` if the collection is empty or nil + var isEmptyOrNil: Bool { + switch self { + case .none: + return true + case .some(let collection): + return collection.isEmpty + } + } + + /// - returns: `true` if the collection is non-nil AND has contents. + var isNotEmpty: Bool { + switch self { + case .none: + return false + case .some(let collection): + return collection.isNotEmpty + } + } +} diff --git a/Sources/Apollo/GraphQLSelectionSet.swift b/Sources/Apollo/GraphQLSelectionSet.swift index 6be1219a5e..67c1b51d1d 100644 --- a/Sources/Apollo/GraphQLSelectionSet.swift +++ b/Sources/Apollo/GraphQLSelectionSet.swift @@ -51,9 +51,11 @@ public struct GraphQLField: GraphQLSelection { } func cacheKey(with variables: [String: JSONEncodable]?) throws -> String { - if let argumentValues = try arguments?.evaluate(with: variables), !argumentValues.isEmpty { - let argumentsKey = orderIndependentKey(for: argumentValues) - return "\(name)(\(argumentsKey))" + if + let argumentValues = try arguments?.evaluate(with: variables), + argumentValues.isNotEmpty { + let argumentsKey = orderIndependentKey(for: argumentValues) + return "\(name)(\(argumentsKey))" } else { return name } diff --git a/Sources/Apollo/HTTPNetworkTransport.swift b/Sources/Apollo/HTTPNetworkTransport.swift index 195432e538..ffacd3ad0d 100644 --- a/Sources/Apollo/HTTPNetworkTransport.swift +++ b/Sources/Apollo/HTTPNetworkTransport.swift @@ -224,9 +224,10 @@ public class HTTPNetworkTransport { files: [GraphQLFile]?, response: GraphQLResponse, completionHandler: @escaping (_ result: Result, Error>) -> Void) { - guard let delegate = self.delegate as? HTTPNetworkTransportGraphQLErrorDelegate, + guard + let delegate = self.delegate as? HTTPNetworkTransportGraphQLErrorDelegate, let graphQLErrors = response.parseErrorsOnlyFast(), - !graphQLErrors.isEmpty else { + graphQLErrors.isNotEmpty else { completionHandler(.success(response)) return } @@ -379,16 +380,18 @@ public class HTTPNetworkTransport { } case .POST: do { - if let files = files, !files.isEmpty { - let formData = try requestCreator.requestMultipartFormData( - for: operation, - files: files, - sendOperationIdentifiers: self.sendOperationIdentifiers, - serializationFormat: self.serializationFormat, - manualBoundary: nil) - - request.setValue("multipart/form-data; boundary=\(formData.boundary)", forHTTPHeaderField: "Content-Type") - request.httpBody = try formData.encode() + if + let files = files, + files.isNotEmpty { + let formData = try requestCreator.requestMultipartFormData( + for: operation, + files: files, + sendOperationIdentifiers: self.sendOperationIdentifiers, + serializationFormat: self.serializationFormat, + manualBoundary: nil) + + request.setValue("multipart/form-data; boundary=\(formData.boundary)", forHTTPHeaderField: "Content-Type") + request.httpBody = try formData.encode() } else { request.httpBody = try serializationFormat.serialize(value: body) } diff --git a/Sources/Apollo/Promise.swift b/Sources/Apollo/Promise.swift index c838279c94..ed778e033a 100644 --- a/Sources/Apollo/Promise.swift +++ b/Sources/Apollo/Promise.swift @@ -226,8 +226,10 @@ final class Promise { lock.withLock { // If the promise has been resolved and there are no existing result handlers, // there is no need to append the handler to the array first. - if case .resolved(let result) = state, resultHandlers.isEmpty { - handler(result) + if + case .resolved(let result) = state, + resultHandlers.isEmpty { + handler(result) } else { resultHandlers.append(handler) } From 566424a8579ab2539dd8461c003ebd1d915e7726 Mon Sep 17 00:00:00 2001 From: Ellen Shapiro Date: Tue, 29 Oct 2019 13:08:45 -0700 Subject: [PATCH 2/7] break up "utilities" into different files --- Apollo.xcodeproj/project.pbxproj | 12 ++++++++---- ...ilities.swift => HTTPURLResponse+Helpers.swift} | 6 ------ Sources/Apollo/Matchable.swift | 14 ++++++++++++++ 3 files changed, 22 insertions(+), 10 deletions(-) rename Sources/Apollo/{Utilities.swift => HTTPURLResponse+Helpers.swift} (82%) create mode 100644 Sources/Apollo/Matchable.swift diff --git a/Apollo.xcodeproj/project.pbxproj b/Apollo.xcodeproj/project.pbxproj index b1afc7b665..9e17333507 100644 --- a/Apollo.xcodeproj/project.pbxproj +++ b/Apollo.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 9BDE43DD22C6705300FD7C7F /* GraphQLHTTPResponseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BDE43DC22C6705300FD7C7F /* GraphQLHTTPResponseError.swift */; }; 9BDE43DF22C6708600FD7C7F /* GraphQLHTTPRequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BDE43DE22C6708600FD7C7F /* GraphQLHTTPRequestError.swift */; }; 9BE071AD2368D08700FA5952 /* Collection+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BE071AC2368D08700FA5952 /* Collection+Helpers.swift */; }; + 9BE071AF2368D34D00FA5952 /* Matchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BE071AE2368D34D00FA5952 /* Matchable.swift */; }; 9BEDC79E22E5D2CF00549BF6 /* RequestCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BEDC79D22E5D2CF00549BF6 /* RequestCreator.swift */; }; 9BF1A94F22CA5784005292C2 /* HTTPTransportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BF1A94C22CA54F9005292C2 /* HTTPTransportTests.swift */; }; 9BF1A95122CA6E71005292C2 /* GraphQLGETTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BF1A95022CA6E71005292C2 /* GraphQLGETTransformer.swift */; }; @@ -37,7 +38,7 @@ 9F438D081E6C30B1007BDC1A /* StarWarsAPI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9FCE2CFA1E6C213D00E34457 /* StarWarsAPI.framework */; }; 9F533AB31E6C4A4200CBE097 /* BatchedLoadTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F438D0B1E6C494C007BDC1A /* BatchedLoadTests.swift */; }; 9F55347B1DE1DB2100E54264 /* ApolloStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F55347A1DE1DB2100E54264 /* ApolloStore.swift */; }; - 9F578D901D8D2CB300C0EA36 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F578D8F1D8D2CB300C0EA36 /* Utilities.swift */; }; + 9F578D901D8D2CB300C0EA36 /* HTTPURLResponse+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F578D8F1D8D2CB300C0EA36 /* HTTPURLResponse+Helpers.swift */; }; 9F65B1211EC106F30090B25F /* Apollo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9FC750441D2A532C00458D91 /* Apollo.framework */; }; 9F69FFA91D42855900E000B1 /* NetworkTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F69FFA81D42855900E000B1 /* NetworkTransport.swift */; }; 9F7BA89922927A3700999B3B /* ResponsePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F7BA89822927A3700999B3B /* ResponsePath.swift */; }; @@ -285,6 +286,7 @@ 9BDE43DC22C6705300FD7C7F /* GraphQLHTTPResponseError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLHTTPResponseError.swift; sourceTree = ""; }; 9BDE43DE22C6708600FD7C7F /* GraphQLHTTPRequestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLHTTPRequestError.swift; sourceTree = ""; }; 9BE071AC2368D08700FA5952 /* Collection+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+Helpers.swift"; sourceTree = ""; }; + 9BE071AE2368D34D00FA5952 /* Matchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Matchable.swift; sourceTree = ""; }; 9BEDC79D22E5D2CF00549BF6 /* RequestCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestCreator.swift; sourceTree = ""; }; 9BF1A94C22CA54F9005292C2 /* HTTPTransportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPTransportTests.swift; sourceTree = ""; }; 9BF1A95022CA6E71005292C2 /* GraphQLGETTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLGETTransformer.swift; sourceTree = ""; }; @@ -298,7 +300,7 @@ 9F438D0B1E6C494C007BDC1A /* BatchedLoadTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatchedLoadTests.swift; sourceTree = ""; }; 9F4DAF2D1E48B84B00EBFF0B /* HTTPNetworkTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPNetworkTransport.swift; sourceTree = ""; }; 9F55347A1DE1DB2100E54264 /* ApolloStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApolloStore.swift; sourceTree = ""; }; - 9F578D8F1D8D2CB300C0EA36 /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; }; + 9F578D8F1D8D2CB300C0EA36 /* HTTPURLResponse+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HTTPURLResponse+Helpers.swift"; sourceTree = ""; }; 9F69FFA81D42855900E000B1 /* NetworkTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkTransport.swift; sourceTree = ""; }; 9F7BA89822927A3700999B3B /* ResponsePath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponsePath.swift; sourceTree = ""; }; 9F8622F71EC2004200C38162 /* ReadWriteFromStoreTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadWriteFromStoreTests.swift; sourceTree = ""; }; @@ -532,6 +534,7 @@ isa = PBXGroup; children = ( 9BDE43D022C6655200FD7C7F /* Cancellable.swift */, + 9BE071AE2368D34D00FA5952 /* Matchable.swift */, ); name = Protocols; sourceTree = ""; @@ -731,13 +734,13 @@ 9B1CCDD82360F02C007C9032 /* Bundle+Helpers.swift */, 9BE071AC2368D08700FA5952 /* Collection+Helpers.swift */, 9BA3130D2302BEA5007B7FC5 /* DispatchQueue+Optional.swift */, + 9F578D8F1D8D2CB300C0EA36 /* HTTPURLResponse+Helpers.swift */, 9FE941CF1E62C771007CDD89 /* Promise.swift */, 9BA1245D22DE116B00BF1D24 /* Result+Helpers.swift */, 9F19D8431EED568200C57247 /* ResultOrPromise.swift */, 9FEC15B31E681DAD00D461B4 /* Collections.swift */, 9FADC8491E6B0B2300C677E6 /* Locking.swift */, 9B1A38522332AF6F00325FB4 /* String+SHA.swift */, - 9F578D8F1D8D2CB300C0EA36 /* Utilities.swift */, ); name = Utilities; sourceTree = ""; @@ -1220,6 +1223,7 @@ 9F55347B1DE1DB2100E54264 /* ApolloStore.swift in Sources */, 9BDE43D122C6655300FD7C7F /* Cancellable.swift in Sources */, 9F69FFA91D42855900E000B1 /* NetworkTransport.swift in Sources */, + 9BE071AF2368D34D00FA5952 /* Matchable.swift in Sources */, 9FCDFD291E33D0CE007519DC /* GraphQLQueryWatcher.swift in Sources */, 9FC2333D1E66BBF7001E4541 /* GraphQLDependencyTracker.swift in Sources */, 9F19D8441EED568200C57247 /* ResultOrPromise.swift in Sources */, @@ -1236,7 +1240,7 @@ 9FC9A9CC1E2FD0760023C4D5 /* Record.swift in Sources */, 9FC4B9201D2A6F8D0046A641 /* JSON.swift in Sources */, 9FEC15B41E681DAD00D461B4 /* Collections.swift in Sources */, - 9F578D901D8D2CB300C0EA36 /* Utilities.swift in Sources */, + 9F578D901D8D2CB300C0EA36 /* HTTPURLResponse+Helpers.swift in Sources */, 9F7BA89922927A3700999B3B /* ResponsePath.swift in Sources */, 9FC9A9BD1E2C271C0023C4D5 /* RecordSet.swift in Sources */, 9BF1A95122CA6E71005292C2 /* GraphQLGETTransformer.swift in Sources */, diff --git a/Sources/Apollo/Utilities.swift b/Sources/Apollo/HTTPURLResponse+Helpers.swift similarity index 82% rename from Sources/Apollo/Utilities.swift rename to Sources/Apollo/HTTPURLResponse+Helpers.swift index 4778c3e0d3..128e7f60ca 100644 --- a/Sources/Apollo/Utilities.swift +++ b/Sources/Apollo/HTTPURLResponse+Helpers.swift @@ -15,9 +15,3 @@ extension HTTPURLResponse { return String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding(encodingName as CFString))) } } - -public protocol Matchable { - associatedtype Base - static func ~=(pattern: Self, value: Base) -> Bool -} - diff --git a/Sources/Apollo/Matchable.swift b/Sources/Apollo/Matchable.swift new file mode 100644 index 0000000000..d09ca9653d --- /dev/null +++ b/Sources/Apollo/Matchable.swift @@ -0,0 +1,14 @@ +// +// Matchable.swift +// Apollo +// +// Created by Ellen Shapiro on 10/29/19. +// Copyright © 2019 Apollo GraphQL. All rights reserved. +// + +import Foundation + +public protocol Matchable { + associatedtype Base + static func ~=(pattern: Self, value: Base) -> Bool +} From 094500e3c1f71a6bd47fcbd3235868fa9c7a8721 Mon Sep 17 00:00:00 2001 From: Ellen Shapiro Date: Tue, 29 Oct 2019 13:09:06 -0700 Subject: [PATCH 3/7] move zip/unzip tuple convenience methods to Collection + helpers --- Sources/Apollo/Collection+Helpers.swift | 39 +++++++++++++++++++++++++ Sources/Apollo/Collections.swift | 38 ------------------------ 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/Sources/Apollo/Collection+Helpers.swift b/Sources/Apollo/Collection+Helpers.swift index 6f25344cee..dca648f02b 100644 --- a/Sources/Apollo/Collection+Helpers.swift +++ b/Sources/Apollo/Collection+Helpers.swift @@ -40,3 +40,42 @@ extension Optional where Wrapped: Collection { } } } + +public func unzip(_ array: [(Element1, Element2)]) -> ([Element1], [Element2]) { + var array1: [Element1] = [] + var array2: [Element2] = [] + + for element in array { + array1.append(element.0) + array2.append(element.1) + } + + return (array1, array2) +} + +public func unzip(_ array: [(Element1, Element2, Element3)]) -> ([Element1], [Element2], [Element3]) { + var array1: [Element1] = [] + var array2: [Element2] = [] + var array3: [Element3] = [] + + for element in array { + array1.append(element.0) + array2.append(element.1) + array3.append(element.2) + } + + return (array1, array2, array3) +} + +public func unzip(_ array: [[Element]], count: Int) -> [[Element]] { + var unzippedArray: [[Element]] = Array(repeating: [], count: count) + + for valuesForElement in array { + for (index, value) in valuesForElement.enumerated() { + unzippedArray[index].append(value) + } + } + + return unzippedArray +} + diff --git a/Sources/Apollo/Collections.swift b/Sources/Apollo/Collections.swift index 6ad5532b1a..b3354c88dc 100644 --- a/Sources/Apollo/Collections.swift +++ b/Sources/Apollo/Collections.swift @@ -54,41 +54,3 @@ struct GroupedSequenceIterator: IteratorProtocol { } } } - -public func unzip(_ array: [(Element1, Element2)]) -> ([Element1], [Element2]) { - var array1: [Element1] = [] - var array2: [Element2] = [] - - for element in array { - array1.append(element.0) - array2.append(element.1) - } - - return (array1, array2) -} - -public func unzip(_ array: [(Element1, Element2, Element3)]) -> ([Element1], [Element2], [Element3]) { - var array1: [Element1] = [] - var array2: [Element2] = [] - var array3: [Element3] = [] - - for element in array { - array1.append(element.0) - array2.append(element.1) - array3.append(element.2) - } - - return (array1, array2, array3) -} - -public func unzip(_ array: [[Element]], count: Int) -> [[Element]] { - var unzippedArray: [[Element]] = Array(repeating: [], count: count) - - for valuesForElement in array { - for (index, value) in valuesForElement.enumerated() { - unzippedArray[index].append(value) - } - } - - return unzippedArray -} From 1230cc49e7a37c36f4350d7860f5bfa0bd38a500 Mon Sep 17 00:00:00 2001 From: Ellen Shapiro Date: Tue, 29 Oct 2019 13:11:01 -0700 Subject: [PATCH 4/7] pull apart "collections" into separate classes --- Apollo.xcodeproj/project.pbxproj | 14 +++++++++----- Sources/Apollo/Dictionary+Helpers.swift | 14 ++++++++++++++ .../{Collections.swift => GroupedSequence.swift} | 15 --------------- 3 files changed, 23 insertions(+), 20 deletions(-) create mode 100644 Sources/Apollo/Dictionary+Helpers.swift rename Sources/Apollo/{Collections.swift => GroupedSequence.swift} (75%) diff --git a/Apollo.xcodeproj/project.pbxproj b/Apollo.xcodeproj/project.pbxproj index 9e17333507..c15264c95d 100644 --- a/Apollo.xcodeproj/project.pbxproj +++ b/Apollo.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ 9BDE43DF22C6708600FD7C7F /* GraphQLHTTPRequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BDE43DE22C6708600FD7C7F /* GraphQLHTTPRequestError.swift */; }; 9BE071AD2368D08700FA5952 /* Collection+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BE071AC2368D08700FA5952 /* Collection+Helpers.swift */; }; 9BE071AF2368D34D00FA5952 /* Matchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BE071AE2368D34D00FA5952 /* Matchable.swift */; }; + 9BE071B12368D3F500FA5952 /* Dictionary+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BE071B02368D3F500FA5952 /* Dictionary+Helpers.swift */; }; 9BEDC79E22E5D2CF00549BF6 /* RequestCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BEDC79D22E5D2CF00549BF6 /* RequestCreator.swift */; }; 9BF1A94F22CA5784005292C2 /* HTTPTransportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BF1A94C22CA54F9005292C2 /* HTTPTransportTests.swift */; }; 9BF1A95122CA6E71005292C2 /* GraphQLGETTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BF1A95022CA6E71005292C2 /* GraphQLGETTransformer.swift */; }; @@ -103,7 +104,7 @@ 9FE1C6E71E634C8D00C02284 /* PromiseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE1C6E61E634C8D00C02284 /* PromiseTests.swift */; }; 9FE941D01E62C771007CDD89 /* Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE941CF1E62C771007CDD89 /* Promise.swift */; }; 9FEB050D1DB5732300DA3B44 /* JSONSerializationFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FEB050C1DB5732300DA3B44 /* JSONSerializationFormat.swift */; }; - 9FEC15B41E681DAD00D461B4 /* Collections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FEC15B31E681DAD00D461B4 /* Collections.swift */; }; + 9FEC15B41E681DAD00D461B4 /* GroupedSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FEC15B31E681DAD00D461B4 /* GroupedSequence.swift */; }; 9FF33D811E48B98200F608A4 /* HTTPNetworkTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F4DAF2D1E48B84B00EBFF0B /* HTTPNetworkTransport.swift */; }; 9FF90A611DDDEB100034C3B6 /* GraphQLResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF90A5B1DDDEB100034C3B6 /* GraphQLResponse.swift */; }; 9FF90A651DDDEB100034C3B6 /* GraphQLExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF90A5C1DDDEB100034C3B6 /* GraphQLExecutor.swift */; }; @@ -287,6 +288,7 @@ 9BDE43DE22C6708600FD7C7F /* GraphQLHTTPRequestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLHTTPRequestError.swift; sourceTree = ""; }; 9BE071AC2368D08700FA5952 /* Collection+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+Helpers.swift"; sourceTree = ""; }; 9BE071AE2368D34D00FA5952 /* Matchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Matchable.swift; sourceTree = ""; }; + 9BE071B02368D3F500FA5952 /* Dictionary+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+Helpers.swift"; sourceTree = ""; }; 9BEDC79D22E5D2CF00549BF6 /* RequestCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestCreator.swift; sourceTree = ""; }; 9BF1A94C22CA54F9005292C2 /* HTTPTransportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPTransportTests.swift; sourceTree = ""; }; 9BF1A95022CA6E71005292C2 /* GraphQLGETTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLGETTransformer.swift; sourceTree = ""; }; @@ -383,7 +385,7 @@ 9FE1C6E61E634C8D00C02284 /* PromiseTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PromiseTests.swift; sourceTree = ""; }; 9FE941CF1E62C771007CDD89 /* Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Promise.swift; sourceTree = ""; }; 9FEB050C1DB5732300DA3B44 /* JSONSerializationFormat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONSerializationFormat.swift; sourceTree = ""; }; - 9FEC15B31E681DAD00D461B4 /* Collections.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Collections.swift; sourceTree = ""; }; + 9FEC15B31E681DAD00D461B4 /* GroupedSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupedSequence.swift; sourceTree = ""; }; 9FF90A5B1DDDEB100034C3B6 /* GraphQLResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphQLResponse.swift; sourceTree = ""; }; 9FF90A5C1DDDEB100034C3B6 /* GraphQLExecutor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphQLExecutor.swift; sourceTree = ""; }; 9FF90A6A1DDDEB420034C3B6 /* InputValueEncodingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputValueEncodingTests.swift; sourceTree = ""; }; @@ -733,13 +735,14 @@ 9FCDFD221E33A0D8007519DC /* AsynchronousOperation.swift */, 9B1CCDD82360F02C007C9032 /* Bundle+Helpers.swift */, 9BE071AC2368D08700FA5952 /* Collection+Helpers.swift */, + 9BE071B02368D3F500FA5952 /* Dictionary+Helpers.swift */, 9BA3130D2302BEA5007B7FC5 /* DispatchQueue+Optional.swift */, + 9FEC15B31E681DAD00D461B4 /* GroupedSequence.swift */, 9F578D8F1D8D2CB300C0EA36 /* HTTPURLResponse+Helpers.swift */, + 9FADC8491E6B0B2300C677E6 /* Locking.swift */, 9FE941CF1E62C771007CDD89 /* Promise.swift */, 9BA1245D22DE116B00BF1D24 /* Result+Helpers.swift */, 9F19D8431EED568200C57247 /* ResultOrPromise.swift */, - 9FEC15B31E681DAD00D461B4 /* Collections.swift */, - 9FADC8491E6B0B2300C677E6 /* Locking.swift */, 9B1A38522332AF6F00325FB4 /* String+SHA.swift */, ); name = Utilities; @@ -1222,6 +1225,7 @@ 9F86B68B1E6438D700B885FF /* GraphQLSelectionSetMapper.swift in Sources */, 9F55347B1DE1DB2100E54264 /* ApolloStore.swift in Sources */, 9BDE43D122C6655300FD7C7F /* Cancellable.swift in Sources */, + 9BE071B12368D3F500FA5952 /* Dictionary+Helpers.swift in Sources */, 9F69FFA91D42855900E000B1 /* NetworkTransport.swift in Sources */, 9BE071AF2368D34D00FA5952 /* Matchable.swift in Sources */, 9FCDFD291E33D0CE007519DC /* GraphQLQueryWatcher.swift in Sources */, @@ -1239,7 +1243,7 @@ C377CCA922D798BD00572E03 /* GraphQLFile.swift in Sources */, 9FC9A9CC1E2FD0760023C4D5 /* Record.swift in Sources */, 9FC4B9201D2A6F8D0046A641 /* JSON.swift in Sources */, - 9FEC15B41E681DAD00D461B4 /* Collections.swift in Sources */, + 9FEC15B41E681DAD00D461B4 /* GroupedSequence.swift in Sources */, 9F578D901D8D2CB300C0EA36 /* HTTPURLResponse+Helpers.swift in Sources */, 9F7BA89922927A3700999B3B /* ResponsePath.swift in Sources */, 9FC9A9BD1E2C271C0023C4D5 /* RecordSet.swift in Sources */, diff --git a/Sources/Apollo/Dictionary+Helpers.swift b/Sources/Apollo/Dictionary+Helpers.swift new file mode 100644 index 0000000000..27832500f9 --- /dev/null +++ b/Sources/Apollo/Dictionary+Helpers.swift @@ -0,0 +1,14 @@ +public extension Dictionary { + static func += (lhs: inout Dictionary, rhs: Dictionary) { + lhs.merge(rhs) { (_, new) in new } + } +} + +extension Dictionary { + init(_ entries: S) where S.Iterator.Element == Element { + self = Dictionary(minimumCapacity: entries.underestimatedCount) + for (key, value) in entries { + self[key] = value + } + } +} diff --git a/Sources/Apollo/Collections.swift b/Sources/Apollo/GroupedSequence.swift similarity index 75% rename from Sources/Apollo/Collections.swift rename to Sources/Apollo/GroupedSequence.swift index b3354c88dc..231e759105 100644 --- a/Sources/Apollo/Collections.swift +++ b/Sources/Apollo/GroupedSequence.swift @@ -1,18 +1,3 @@ -public extension Dictionary { - static func += (lhs: inout Dictionary, rhs: Dictionary) { - lhs.merge(rhs) { (_, new) in new } - } -} - -extension Dictionary { - init(_ entries: S) where S.Iterator.Element == Element { - self = Dictionary(minimumCapacity: entries.underestimatedCount) - for (key, value) in entries { - self[key] = value - } - } -} - struct GroupedSequence { private(set) var keys: [Key] = [] fileprivate var groupsForKeys: [[Value]] = [] From bf273f5520723df9cdb459497d7b4f268f4c07ad Mon Sep 17 00:00:00 2001 From: Ellen Shapiro Date: Tue, 29 Oct 2019 13:14:17 -0700 Subject: [PATCH 5/7] get rid of headers and add marks --- Sources/Apollo/Bundle+Helpers.swift | 8 -------- Sources/Apollo/Collection+Helpers.swift | 13 +++++-------- Sources/Apollo/DispatchQueue+Optional.swift | 8 -------- Sources/Apollo/String+SHA.swift | 8 -------- 4 files changed, 5 insertions(+), 32 deletions(-) diff --git a/Sources/Apollo/Bundle+Helpers.swift b/Sources/Apollo/Bundle+Helpers.swift index 6730fa1376..e87f2481e8 100644 --- a/Sources/Apollo/Bundle+Helpers.swift +++ b/Sources/Apollo/Bundle+Helpers.swift @@ -1,11 +1,3 @@ -// -// Bundle+Helpers.swift -// Apollo -// -// Created by Ellen Shapiro on 10/23/19. -// Copyright © 2019 Apollo GraphQL. All rights reserved. -// - import Foundation extension Bundle { diff --git a/Sources/Apollo/Collection+Helpers.swift b/Sources/Apollo/Collection+Helpers.swift index dca648f02b..5d5f548da9 100644 --- a/Sources/Apollo/Collection+Helpers.swift +++ b/Sources/Apollo/Collection+Helpers.swift @@ -1,13 +1,7 @@ -// -// Emptiable+Helpers.swift -// Apollo -// -// Created by Ellen Shapiro on 10/29/19. -// Copyright © 2019 Apollo GraphQL. All rights reserved. -// - import Foundation +// MARK: - Emptiness + Optionality + extension Collection { /// Convenience helper to make `guard` statements more readable @@ -41,6 +35,9 @@ extension Optional where Wrapped: Collection { } } +// MARK: - Unzipping +// MARK: Arrays of tuples to tuples of arrays + public func unzip(_ array: [(Element1, Element2)]) -> ([Element1], [Element2]) { var array1: [Element1] = [] var array2: [Element2] = [] diff --git a/Sources/Apollo/DispatchQueue+Optional.swift b/Sources/Apollo/DispatchQueue+Optional.swift index 38e0bcf031..0847c53dea 100644 --- a/Sources/Apollo/DispatchQueue+Optional.swift +++ b/Sources/Apollo/DispatchQueue+Optional.swift @@ -1,11 +1,3 @@ -// -// DispatchQueue+Optional.swift -// Apollo -// -// Created by Ellen Shapiro on 8/13/19. -// Copyright © 2019 Apollo GraphQL. All rights reserved. -// - import Foundation public extension DispatchQueue { diff --git a/Sources/Apollo/String+SHA.swift b/Sources/Apollo/String+SHA.swift index a1d3c5b930..27912773a1 100644 --- a/Sources/Apollo/String+SHA.swift +++ b/Sources/Apollo/String+SHA.swift @@ -1,11 +1,3 @@ -// -// String+SHA.swift -// Apollo -// -// Created by Ellen Shapiro on 9/18/19. -// Copyright © 2019 Apollo GraphQL. All rights reserved. -// - import Foundation import CommonCrypto From 3f962d7d7faf069aed49b9569c221261b486cc10 Mon Sep 17 00:00:00 2001 From: Ellen Shapiro Date: Tue, 29 Oct 2019 13:14:28 -0700 Subject: [PATCH 6/7] remove unused unzip function --- Sources/Apollo/Collection+Helpers.swift | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Sources/Apollo/Collection+Helpers.swift b/Sources/Apollo/Collection+Helpers.swift index 5d5f548da9..eef0020680 100644 --- a/Sources/Apollo/Collection+Helpers.swift +++ b/Sources/Apollo/Collection+Helpers.swift @@ -63,16 +63,3 @@ public func unzip(_ array: [(Element1, Element2, E return (array1, array2, array3) } - -public func unzip(_ array: [[Element]], count: Int) -> [[Element]] { - var unzippedArray: [[Element]] = Array(repeating: [], count: count) - - for valuesForElement in array { - for (index, value) in valuesForElement.enumerated() { - unzippedArray[index].append(value) - } - } - - return unzippedArray -} - From 1abaeea39e0e849beacf31ed532363444df74294 Mon Sep 17 00:00:00 2001 From: Ellen Shapiro Date: Tue, 29 Oct 2019 13:18:31 -0700 Subject: [PATCH 7/7] add some extra hints around new error delegate --- Sources/Apollo/HTTPNetworkTransport.swift | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Sources/Apollo/HTTPNetworkTransport.swift b/Sources/Apollo/HTTPNetworkTransport.swift index ffacd3ad0d..1cfdc04ff9 100644 --- a/Sources/Apollo/HTTPNetworkTransport.swift +++ b/Sources/Apollo/HTTPNetworkTransport.swift @@ -47,6 +47,7 @@ public protocol HTTPNetworkTransportTaskCompletedDelegate: HTTPNetworkTransportD // MARK: - +/// Methods which will be called if an error is receieved at the network level. public protocol HTTPNetworkTransportRetryDelegate: HTTPNetworkTransportDelegate { /// Called when an error has been received after a request has been sent to the server to see if an operation should be retried or not. @@ -65,12 +66,19 @@ public protocol HTTPNetworkTransportRetryDelegate: HTTPNetworkTransportDelegate retryHandler: @escaping (_ shouldRetry: Bool) -> Void) } -/// Methods which will be called after some kind of response has been received and it contains GraphQLErrors -public protocol HTTPNetworkTransportGraphQLErrorDelegate: HTTPNetworkTransportDelegate { +// MARK: - +/// Methods which will be called after some kind of response has been received and it contains GraphQLErrors. +public protocol HTTPNetworkTransportGraphQLErrorDelegate: HTTPNetworkTransportDelegate { /// Called when response contains one or more GraphQL errors. - /// NOTE: Don't just call the `retryHandler` with `true` all the time, or you can potentially wind up in an infinite loop of errors + /// + /// NOTE: The mere presence of a GraphQL error does not necessarily mean a request failed! + /// GraphQL is design to allow partial success/failures to return, so make sure + /// you're validating the *type* of error you're getting in this before deciding whether to retry or not. + /// + /// ALSO NOTE: Don't just call the `retryHandler` with `true` all the time, or you can + /// potentially wind up in an infinite loop of errors /// /// - Parameters: /// - networkTransport: The network transport which received the error @@ -79,7 +87,6 @@ public protocol HTTPNetworkTransportGraphQLErrorDelegate: HTTPNetworkTransportDe func networkTransport(_ networkTransport: HTTPNetworkTransport, receivedGraphQLErrors errors: [GraphQLError], retryHandler: @escaping (_ shouldRetry: Bool) -> Void) } - // MARK: - /// A network transport that uses HTTP POST requests to send GraphQL operations to a server, and that uses `URLSession` as the networking implementation.