Skip to content

Commit

Permalink
Move common objects into ApolloAPI
Browse files Browse the repository at this point in the history
  • Loading branch information
AnthonyMDev committed Sep 21, 2021
1 parent 1f4048b commit e7daf86
Show file tree
Hide file tree
Showing 14 changed files with 714 additions and 77 deletions.
44 changes: 40 additions & 4 deletions Apollo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@
DE05860D266978A100265760 /* ScalarTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE3C7B15260A6FCA00D2F4FF /* ScalarTypes.swift */; };
DE05860E266978A100265760 /* GraphQLOptional.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B68F06E241C649E00E97318 /* GraphQLOptional.swift */; };
DE058610266978A100265760 /* InputValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC9A9C11E2D3CAF0023C4D5 /* InputValue.swift */; };
DE058613266978A100265760 /* GraphQLSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE3C7B13260A6FCA00D2F4FF /* GraphQLSchema.swift */; };
DE058616266978A100265760 /* GraphQLEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE3C7B14260A6FCA00D2F4FF /* GraphQLEnum.swift */; };
DE05862D2669800000265760 /* Matchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BE071AE2368D34D00FA5952 /* Matchable.swift */; };
DE05862F266980C200265760 /* GraphQLSelectionSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC9A9C41E2D6CE70023C4D5 /* GraphQLSelectionSet.swift */; };
Expand All @@ -202,6 +201,14 @@
DE181A3226C5C401000C0B9C /* Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE181A3126C5C401000C0B9C /* Compression.swift */; };
DE181A3426C5D8D4000C0B9C /* CompressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE181A3326C5D8D4000C0B9C /* CompressionTests.swift */; };
DE181A3626C5DE4F000C0B9C /* WebSocketStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE181A3526C5DE4F000C0B9C /* WebSocketStream.swift */; };
DE2FCF1D26E806710057EA67 /* SchemaTypeFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2FCF1C26E806710057EA67 /* SchemaTypeFactory.swift */; };
DE2FCF1F26E807CC0057EA67 /* CacheTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2FCF1E26E807CC0057EA67 /* CacheTransaction.swift */; };
DE2FCF2126E807EF0057EA67 /* Cacheable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2FCF2026E807EF0057EA67 /* Cacheable.swift */; };
DE2FCF2726E8083A0057EA67 /* Union.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2FCF2326E8083A0057EA67 /* Union.swift */; };
DE2FCF2826E8083A0057EA67 /* Interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2FCF2426E8083A0057EA67 /* Interface.swift */; };
DE2FCF2926E8083A0057EA67 /* Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2FCF2526E8083A0057EA67 /* Object.swift */; };
DE2FCF2A26E8083A0057EA67 /* Field.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2FCF2626E8083A0057EA67 /* Field.swift */; };
DE2FCF2C26E808560057EA67 /* ObjectType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2FCF2B26E808560057EA67 /* ObjectType.swift */; };
DE3C7974260A646300D2F4FF /* dist in Resources */ = {isa = PBXBuildFile; fileRef = DE3C7973260A646300D2F4FF /* dist */; };
DE3C7A94260A6C1000D2F4FF /* ApolloUtils.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B68353E2463481A00337AE6 /* ApolloUtils.framework */; };
DE3C7A95260A6C1000D2F4FF /* ApolloUtils.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9B68353E2463481A00337AE6 /* ApolloUtils.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
Expand Down Expand Up @@ -791,6 +798,14 @@
DE181A3126C5C401000C0B9C /* Compression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Compression.swift; sourceTree = "<group>"; };
DE181A3326C5D8D4000C0B9C /* CompressionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompressionTests.swift; sourceTree = "<group>"; };
DE181A3526C5DE4F000C0B9C /* WebSocketStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocketStream.swift; sourceTree = "<group>"; };
DE2FCF1C26E806710057EA67 /* SchemaTypeFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaTypeFactory.swift; sourceTree = "<group>"; };
DE2FCF1E26E807CC0057EA67 /* CacheTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacheTransaction.swift; sourceTree = "<group>"; };
DE2FCF2026E807EF0057EA67 /* Cacheable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cacheable.swift; sourceTree = "<group>"; };
DE2FCF2326E8083A0057EA67 /* Union.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Union.swift; sourceTree = "<group>"; };
DE2FCF2426E8083A0057EA67 /* Interface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Interface.swift; sourceTree = "<group>"; };
DE2FCF2526E8083A0057EA67 /* Object.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Object.swift; sourceTree = "<group>"; };
DE2FCF2626E8083A0057EA67 /* Field.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Field.swift; sourceTree = "<group>"; };
DE2FCF2B26E808560057EA67 /* ObjectType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectType.swift; sourceTree = "<group>"; };
DE3C7973260A646300D2F4FF /* dist */ = {isa = PBXFileReference; lastKnownFileType = folder; path = dist; sourceTree = "<group>"; };
DE3C79A9260A6ACD00D2F4FF /* AnimalKingdomAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AnimalKingdomAPI.h; sourceTree = "<group>"; };
DE3C79AA260A6ACD00D2F4FF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand All @@ -804,7 +819,6 @@
DE3C7B10260A6FC900D2F4FF /* SelectionSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectionSet.swift; sourceTree = "<group>"; };
DE3C7B11260A6FC900D2F4FF /* ResponseDict.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResponseDict.swift; sourceTree = "<group>"; };
DE3C7B12260A6FC900D2F4FF /* FragmentProtocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FragmentProtocols.swift; sourceTree = "<group>"; };
DE3C7B13260A6FCA00D2F4FF /* GraphQLSchema.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphQLSchema.swift; sourceTree = "<group>"; };
DE3C7B14260A6FCA00D2F4FF /* GraphQLEnum.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphQLEnum.swift; sourceTree = "<group>"; };
DE3C7B15260A6FCA00D2F4FF /* ScalarTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScalarTypes.swift; sourceTree = "<group>"; };
DE56DC222683B2020090D6E4 /* DefaultInterceptorProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInterceptorProvider.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1682,6 +1696,18 @@
path = Sources/ApolloAPI;
sourceTree = "<group>";
};
DE2FCF2226E8082A0057EA67 /* SchemaTypes */ = {
isa = PBXGroup;
children = (
DE2FCF2626E8083A0057EA67 /* Field.swift */,
DE2FCF2426E8083A0057EA67 /* Interface.swift */,
DE2FCF2526E8083A0057EA67 /* Object.swift */,
DE2FCF2326E8083A0057EA67 /* Union.swift */,
DE2FCF2B26E808560057EA67 /* ObjectType.swift */,
);
path = SchemaTypes;
sourceTree = "<group>";
};
DE3C79A8260A6ACD00D2F4FF /* AnimalKingdomAPI */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1709,13 +1735,16 @@
DE3C7B0F260A6F7F00D2F4FF /* CodegenV1 */ = {
isa = PBXGroup;
children = (
DE2FCF2226E8082A0057EA67 /* SchemaTypes */,
DE2FCF1E26E807CC0057EA67 /* CacheTransaction.swift */,
9B68F06E241C649E00E97318 /* GraphQLOptional.swift */,
DE3C7B12260A6FC900D2F4FF /* FragmentProtocols.swift */,
DE3C7B14260A6FCA00D2F4FF /* GraphQLEnum.swift */,
DE3C7B13260A6FCA00D2F4FF /* GraphQLSchema.swift */,
DE3C7B11260A6FC900D2F4FF /* ResponseDict.swift */,
DE3C7B10260A6FC900D2F4FF /* SelectionSet.swift */,
DE2FCF1C26E806710057EA67 /* SchemaTypeFactory.swift */,
DE664ED326602AF60054DB4F /* Selection.swift */,
DE2FCF2026E807EF0057EA67 /* Cacheable.swift */,
);
path = CodegenV1;
sourceTree = "<group>";
Expand Down Expand Up @@ -2836,12 +2865,19 @@
DE058609266978A100265760 /* Selection.swift in Sources */,
DE05860A266978A100265760 /* ResponseDict.swift in Sources */,
DE05860B266978A100265760 /* SelectionSet.swift in Sources */,
DE2FCF1D26E806710057EA67 /* SchemaTypeFactory.swift in Sources */,
DE2FCF2C26E808560057EA67 /* ObjectType.swift in Sources */,
DE05860C266978A100265760 /* FragmentProtocols.swift in Sources */,
DE2FCF2A26E8083A0057EA67 /* Field.swift in Sources */,
DE05860D266978A100265760 /* ScalarTypes.swift in Sources */,
DE05860E266978A100265760 /* GraphQLOptional.swift in Sources */,
DE2FCF2126E807EF0057EA67 /* Cacheable.swift in Sources */,
DE2FCF1F26E807CC0057EA67 /* CacheTransaction.swift in Sources */,
DE2FCF2826E8083A0057EA67 /* Interface.swift in Sources */,
DE2FCF2926E8083A0057EA67 /* Object.swift in Sources */,
DE058610266978A100265760 /* InputValue.swift in Sources */,
DE058613266978A100265760 /* GraphQLSchema.swift in Sources */,
DE058616266978A100265760 /* GraphQLEnum.swift in Sources */,
DE2FCF2726E8083A0057EA67 /* Union.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
106 changes: 106 additions & 0 deletions Sources/ApolloAPI/CodegenV1/CacheTransaction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import Foundation

public protocol CacheKeyResolver {
func cacheKey(for: [String: Any]) -> CacheKey?
}

public class CacheTransaction {
let objectFactory: SchemaTypeFactory.Type
let keyResolver: CacheKeyResolver
private(set) var errors: [CacheError] = []
private var fetchedObjects: [CacheKey: Object] = [:]

init(
objectFactory: SchemaTypeFactory.Type,
keyResolver: CacheKeyResolver
) {
self.objectFactory = objectFactory
self.keyResolver = keyResolver
}

func object(withKey key: CacheKey) -> Object? {
fetchedObjects[key] // TODO: if not fetched yet, fetch from store
}

func object(withData data: [String: Any]) -> Object {
let cacheKey = keyResolver.cacheKey(for: data)

if let cacheKey = cacheKey, let object = fetchedObjects[cacheKey] {
return object
// TODO: should merge data objects if needed?
}

guard let typename = data["__typename"] as? String,
let type = objectFactory.objectType(forTypename: typename) else {
fatalError()
}

let object = type.init(transaction: self, data: data)
if let cacheKey = cacheKey {
fetchedObjects[cacheKey] = object
}

return object
}

func log(_ error: CacheError) {
errors.append(error)
}

func log(_ error: Error) {
// TODO
}

// func interface<T: Interface>(withData data: [String: Any]) -> T {
// return T.init(object: object(withData: data))
// }
}

struct CacheError: Error, Equatable {
enum Reason: Error {
case unrecognizedCacheData(_ data: Any, forType: Any.Type)
case invalidObjectType(_ type: Object.Type, forExpectedType: Cacheable.Type)
case invalidValue(_ value: Cacheable?, forCovariantFieldOfType: ObjectType.Type)
case objectNotFound(forCacheKey: CacheKey)
}

enum `Type` {
case read, write
}

let reason: Reason
let type: Type
let field: String
let object: ObjectType?

var message: String {
switch self.reason {
case let .unrecognizedCacheData(data, forType: type):
return "Cache data '\(data)' was unrecognized for conversion to type '\(type)'."

case let .invalidObjectType(type, forExpectedType: expectedType):
switch expectedType {
case is Interface.Type:
return "Object of type '\(type)' does not implement interface '\(expectedType)'."
case is AnyUnion.Type:
return "Object of type '\(type)' is not a valid type for union '\(expectedType)'."
default:
return "Object of type '\(type)' is not a valid type for '\(expectedType)'."
}

case let .invalidValue(value, forCovariantFieldOfType: fieldType):
return """
Value '\(value ?? "nil")' is not a valid value for covariant field '\(field)'.
Object of type '\(Swift.type(of: object))' expects value of type '\(fieldType)'.
"""

case let .objectNotFound(forCacheKey: key):
return "Object with cache key \(key.key) was not found in the cache."

}
}

static func ==(lhs: CacheError, rhs: CacheError) -> Bool {
lhs.message == rhs.message
}
}
12 changes: 12 additions & 0 deletions Sources/ApolloAPI/CodegenV1/Cacheable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// A type that can be the value of a `@CacheField` property. In other words, a `Cacheable` type
/// can be the value of a field on an `Object` or `Interface`
///
/// # Conforming Types:
/// - `Object`
/// - `Interface`
/// - `ScalarType` (`String`, `Int`, `Bool`, `Float`)
/// - `CustomScalarType`
/// - `GraphQLEnum` (via `CustomScalarType`)
public protocol Cacheable {
static func value(with cacheData: Any, in transaction: CacheTransaction) throws -> Self
}
35 changes: 26 additions & 9 deletions Sources/ApolloAPI/CodegenV1/GraphQLEnum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ where T: RawRepresentable & CaseIterable, T.RawValue == String {
/// The underlying enum case. If the value is `__unknown`, this will be `nil`.
public var value: T? {
switch self {
case .case(let value): return value
case let .case(value): return value
default: return nil
}
}

public var rawValue: String {
switch self {
case .case(let value): return value.rawValue
case .__unknown(let value): return value
case let .case(value): return value.rawValue
case let .__unknown(value): return value
}
}

Expand All @@ -48,7 +48,19 @@ where T: RawRepresentable & CaseIterable, T.RawValue == String {
}
}

/// Equatable
// MARK: CustomScalarType
extension GraphQLEnum: CustomScalarType {
public init(scalarData: Any) throws {
guard let stringData = scalarData as? String else {
throw CacheError.Reason.unrecognizedCacheData(scalarData, forType: Self.self)
}
self.init(rawValue: stringData)
}

public var jsonValue: Any { rawValue }
}

// MARK: Equatable
extension GraphQLEnum {
public static func ==(lhs: GraphQLEnum<T>, rhs: GraphQLEnum<T>) -> Bool {
return lhs.rawValue == rhs.rawValue
Expand All @@ -63,6 +75,8 @@ extension GraphQLEnum {
}
}

// MARK: Optional<GraphQLEnum<T>> Equatable

public func ==<T: RawRepresentable & CaseIterable>(lhs: GraphQLEnum<T>?, rhs: T) -> Bool
where T.RawValue == String {
return lhs?.rawValue == rhs.rawValue
Expand All @@ -73,10 +87,13 @@ where T.RawValue == String {
return lhs?.rawValue != rhs.rawValue
}

public func ~=<T>(lhs: T, rhs: GraphQLEnum<T>) -> Bool {
switch rhs {
case let .case(rhs) where rhs == lhs: return true
case let .__unknown(rhsRawValue) where rhsRawValue == lhs.rawValue: return true
default: return false
// MARK: Pattern Matching
extension GraphQLEnum {
public static func ~=(lhs: T, rhs: GraphQLEnum<T>) -> Bool {
switch rhs {
case let .case(rhs) where rhs == lhs: return true
case let .__unknown(rhsRawValue) where rhsRawValue == lhs.rawValue: return true
default: return false
}
}
}
32 changes: 0 additions & 32 deletions Sources/ApolloAPI/CodegenV1/GraphQLSchema.swift

This file was deleted.

22 changes: 11 additions & 11 deletions Sources/ApolloAPI/CodegenV1/ResponseDict.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,32 @@ public struct ResponseDict {
data[key] as! T
}

public subscript<T:ScalarType>(_ key: String) -> T? {
public subscript<T: ScalarType>(_ key: String) -> T? {
data[key] as? T
}

public subscript<T: SelectionSet>(_ key: String) -> T {
let entityData = data[key] as! [String: Any]
return T.init(data: ResponseDict(data: entityData))
let objectData = data[key] as! [String: Any]
return T.init(data: ResponseDict(data: objectData))
}

public subscript<T: SelectionSet>(_ key: String) -> T? {
guard let entityData = data[key] as? [String: Any] else { return nil }
return T.init(data: ResponseDict(data: entityData))
guard let objectData = data[key] as? [String: Any] else { return nil }
return T.init(data: ResponseDict(data: objectData))
}

public subscript<T: SelectionSet>(_ key: String) -> [T] {
let entityData = data[key] as! [[String: Any]]
return entityData.map { T.init(data: ResponseDict(data: $0)) }
let objectData = data[key] as! [[String: Any]]
return objectData.map { T.init(data: ResponseDict(data: $0)) }
}

public subscript<T>(_ key: String) -> GraphQLEnum<T> {
let entityData = data[key] as! String
return GraphQLEnum(rawValue: entityData)
let objectData = data[key] as! String
return GraphQLEnum(rawValue: objectData)
}

public subscript<T>(_ key: String) -> GraphQLEnum<T>? {
guard let entityData = data[key] as? String else { return nil }
return GraphQLEnum(rawValue: entityData)
guard let objectData = data[key] as? String else { return nil }
return GraphQLEnum(rawValue: objectData)
}
}
3 changes: 3 additions & 0 deletions Sources/ApolloAPI/CodegenV1/SchemaTypeFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public protocol SchemaTypeFactory {
static func objectType(forTypename __typename: String) -> Object.Type?
}
Loading

0 comments on commit e7daf86

Please sign in to comment.