diff --git a/final/RocketReserver.xcodeproj/project.pbxproj b/final/RocketReserver.xcodeproj/project.pbxproj index 063bd9e..4d78892 100644 --- a/final/RocketReserver.xcodeproj/project.pbxproj +++ b/final/RocketReserver.xcodeproj/project.pbxproj @@ -27,6 +27,8 @@ 66F96FE629FC070600713B80 /* NetworkInterceptorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F96FE529FC070600713B80 /* NetworkInterceptorProvider.swift */; }; 66F96FE829FC0D7700713B80 /* View+Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F96FE729FC0D7700713B80 /* View+Alert.swift */; }; 66F96FEA29FC183200713B80 /* NotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F96FE929FC183200713B80 /* NotificationView.swift */; }; + E67B2DF02C7D02BC0095602B /* UserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E67B2DEF2C7D02BC0095602B /* UserView.swift */; }; + E67B2DF22C7D03180095602B /* UserViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E67B2DF12C7D03180095602B /* UserViewModel.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -54,6 +56,9 @@ 66F96FE729FC0D7700713B80 /* View+Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Alert.swift"; sourceTree = ""; }; 66F96FE929FC183200713B80 /* NotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationView.swift; sourceTree = ""; }; 66F96FEB29FC2BF400713B80 /* RocketReserverAPI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = RocketReserverAPI; sourceTree = ""; }; + E67B2DEF2C7D02BC0095602B /* UserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserView.swift; sourceTree = ""; }; + E67B2DF12C7D03180095602B /* UserViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserViewModel.swift; sourceTree = ""; }; + E67B2DF32C7D27620095602B /* Me.graphql */ = {isa = PBXFileReference; lastKnownFileType = text; path = Me.graphql; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -82,6 +87,7 @@ 66D7A53F2A056886009DDB89 /* TripsBooked.graphql */, 66D7A5402A056886009DDB89 /* LaunchList.graphql */, 66D7A5412A056886009DDB89 /* CancelTrip.graphql */, + E67B2DF32C7D27620095602B /* Me.graphql */, ); path = graphql; sourceTree = ""; @@ -123,6 +129,8 @@ 66F96FE929FC183200713B80 /* NotificationView.swift */, 66F96F9E29FAC0D700713B80 /* Assets.xcassets */, 66F96FA029FAC0D700713B80 /* Preview Content */, + E67B2DEF2C7D02BC0095602B /* UserView.swift */, + E67B2DF12C7D03180095602B /* UserViewModel.swift */, ); path = RocketReserver; sourceTree = ""; @@ -245,6 +253,8 @@ 66F96F9B29FAC0D600713B80 /* RocketReserverApp.swift in Sources */, 66F96FE229FB0A9600713B80 /* LoginViewModel.swift in Sources */, 66F96FE429FC069D00713B80 /* AuthorizationInterceptor.swift in Sources */, + E67B2DF22C7D03180095602B /* UserViewModel.swift in Sources */, + E67B2DF02C7D02BC0095602B /* UserView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/final/RocketReserver.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/final/RocketReserver.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index bcfd007..2a2a0af 100644 --- a/final/RocketReserver.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/final/RocketReserver.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apollographql/apollo-ios", "state" : { - "revision" : "e218b565661b12c3a1e5c0964eac805d44da7c76", - "version" : "1.14.0" + "revision" : "e70850751f6b21acd1c7871dbcd2cee5d6103c7e", + "version" : "1.15.1" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/SDWebImage/SDWebImage.git", "state" : { - "revision" : "86e9185ef41c4238a93ad8efe61ddeb701e80bbf", - "version" : "5.19.5" + "revision" : "8a1be70a625683bc04d6903e2935bf23f3c6d609", + "version" : "5.19.7" } }, { diff --git a/final/RocketReserver/DetailView.swift b/final/RocketReserver/DetailView.swift index d0aac98..de0e540 100644 --- a/final/RocketReserver/DetailView.swift +++ b/final/RocketReserver/DetailView.swift @@ -6,7 +6,8 @@ struct DetailView: View { private let placeholderImg = Image("placeholder") @StateObject private var viewModel: DetailViewModel - + @State private var isShowingUser = false + init(launchID: RocketReserverAPI.ID) { _viewModel = StateObject(wrappedValue: DetailViewModel(launchID: launchID)) } diff --git a/final/RocketReserver/LaunchListView.swift b/final/RocketReserver/LaunchListView.swift index 070696f..2e61ee3 100644 --- a/final/RocketReserver/LaunchListView.swift +++ b/final/RocketReserver/LaunchListView.swift @@ -1,15 +1,18 @@ import SwiftUI +import KeychainSwift struct LaunchListView: View { @StateObject private var viewModel = LaunchListViewModel() - + @State private var isShowingLogin = false + @State private var isShowingUser = false + var body: some View { NavigationStack { List { ForEach(0.. Bool { + let keychain = KeychainSwift() + return keychain.get(LoginView.loginKeychainKey) != nil + } + } struct LaunchListView_Previews: PreviewProvider { diff --git a/final/RocketReserver/LaunchRow.swift b/final/RocketReserver/LaunchRow.swift index 14cf2df..d429983 100644 --- a/final/RocketReserver/LaunchRow.swift +++ b/final/RocketReserver/LaunchRow.swift @@ -3,7 +3,7 @@ import SDWebImageSwiftUI import SwiftUI struct LaunchRow: View { - let launch: LaunchListQuery.Data.Launches.Launch + let launch: LaunchListDetail private let placeholderImg = Image("placeholder") var body: some View { diff --git a/final/RocketReserver/Network.swift b/final/RocketReserver/Network.swift index bdb402f..84e0101 100644 --- a/final/RocketReserver/Network.swift +++ b/final/RocketReserver/Network.swift @@ -11,11 +11,11 @@ class Network { let cache = InMemoryNormalizedCache() let store = ApolloStore(cache: cache) let provider = NetworkInterceptorProvider(client: client, store: store) - let url = URL(string: "https://apollo-fullstack-tutorial.herokuapp.com/graphql")! + let url = URL(string: "https://ios-spacex-router-1da713d90333.herokuapp.com/")! let transport = RequestChainNetworkTransport(interceptorProvider: provider, endpointURL: url) let webSocket = WebSocket( - url: URL(string: "wss://apollo-fullstack-tutorial.herokuapp.com/graphql")!, + url: URL(string: "wss://ios-spacex-subgraph-c17ee3a300af.herokuapp.com/graphql")!, protocol: .graphql_ws ) diff --git a/final/RocketReserver/UserView.swift b/final/RocketReserver/UserView.swift new file mode 100644 index 0000000..6062382 --- /dev/null +++ b/final/RocketReserver/UserView.swift @@ -0,0 +1,37 @@ +import SwiftUI + +struct UserView: View { + + @StateObject private var viewModel: UserViewModel + + init() { + _viewModel = StateObject(wrappedValue: UserViewModel()) + } + + var body: some View { + NavigationStack { + List { + if let trips = viewModel.trips { + ForEach(0.. @@ -22,7 +23,7 @@ public class LaunchListQuery: GraphQLQuery { public let __data: DataDict public init(_dataDict: DataDict) { __data = _dataDict } - public static var __parentType: ApolloAPI.ParentType { RocketReserverAPI.Objects.Query } + public static var __parentType: any ApolloAPI.ParentType { RocketReserverAPI.Objects.Query } public static var __selections: [ApolloAPI.Selection] { [ .field("launches", Launches.self, arguments: ["after": .variable("cursor")]), ] } @@ -36,7 +37,7 @@ public class LaunchListQuery: GraphQLQuery { public let __data: DataDict public init(_dataDict: DataDict) { __data = _dataDict } - public static var __parentType: ApolloAPI.ParentType { RocketReserverAPI.Objects.LaunchConnection } + public static var __parentType: any ApolloAPI.ParentType { RocketReserverAPI.Objects.LaunchConnection } public static var __selections: [ApolloAPI.Selection] { [ .field("__typename", String.self), .field("cursor", String.self), @@ -55,36 +56,25 @@ public class LaunchListQuery: GraphQLQuery { public let __data: DataDict public init(_dataDict: DataDict) { __data = _dataDict } - public static var __parentType: ApolloAPI.ParentType { RocketReserverAPI.Objects.Launch } + public static var __parentType: any ApolloAPI.ParentType { RocketReserverAPI.Objects.Launch } public static var __selections: [ApolloAPI.Selection] { [ .field("__typename", String.self), - .field("id", RocketReserverAPI.ID.self), - .field("site", String?.self), - .field("mission", Mission?.self), + .fragment(LaunchListDetail.self), ] } public var id: RocketReserverAPI.ID { __data["id"] } public var site: String? { __data["site"] } public var mission: Mission? { __data["mission"] } - /// Launches.Launch.Mission - /// - /// Parent Type: `Mission` - public struct Mission: RocketReserverAPI.SelectionSet { + public struct Fragments: FragmentContainer { public let __data: DataDict public init(_dataDict: DataDict) { __data = _dataDict } - public static var __parentType: ApolloAPI.ParentType { RocketReserverAPI.Objects.Mission } - public static var __selections: [ApolloAPI.Selection] { [ - .field("__typename", String.self), - .field("name", String?.self), - .field("missionPatch", String?.self, arguments: ["size": "SMALL"]), - ] } - - public var name: String? { __data["name"] } - public var missionPatch: String? { __data["missionPatch"] } + public var launchListDetail: LaunchListDetail { _toFragment() } } + + public typealias Mission = LaunchListDetail.Mission } } } -} +} \ No newline at end of file diff --git a/final/RocketReserverAPI/Sources/Operations/Queries/MeQuery.graphql.swift b/final/RocketReserverAPI/Sources/Operations/Queries/MeQuery.graphql.swift new file mode 100644 index 0000000..dc5cd2c --- /dev/null +++ b/final/RocketReserverAPI/Sources/Operations/Queries/MeQuery.graphql.swift @@ -0,0 +1,72 @@ +// @generated +// This file was automatically generated and should not be edited. + +@_exported import ApolloAPI + +public class MeQuery: GraphQLQuery { + public static let operationName: String = "Me" + public static let operationDocument: ApolloAPI.OperationDocument = .init( + definition: .init( + #"query Me { me { __typename trips { __typename ...LaunchListDetail isBooked } } }"#, + fragments: [LaunchListDetail.self] + )) + + public init() {} + + public struct Data: RocketReserverAPI.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { RocketReserverAPI.Objects.Query } + public static var __selections: [ApolloAPI.Selection] { [ + .field("me", Me?.self), + ] } + + public var me: Me? { __data["me"] } + + /// Me + /// + /// Parent Type: `User` + public struct Me: RocketReserverAPI.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { RocketReserverAPI.Objects.User } + public static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("trips", [Trip?].self), + ] } + + public var trips: [Trip?] { __data["trips"] } + + /// Me.Trip + /// + /// Parent Type: `Launch` + public struct Trip: RocketReserverAPI.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { RocketReserverAPI.Objects.Launch } + public static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("isBooked", Bool.self), + .fragment(LaunchListDetail.self), + ] } + + public var isBooked: Bool { __data["isBooked"] } + public var id: RocketReserverAPI.ID { __data["id"] } + public var site: String? { __data["site"] } + public var mission: Mission? { __data["mission"] } + + public struct Fragments: FragmentContainer { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public var launchListDetail: LaunchListDetail { _toFragment() } + } + + public typealias Mission = LaunchListDetail.Mission + } + } + } +} \ No newline at end of file diff --git a/final/RocketReserverAPI/Sources/Operations/Subscriptions/TripsBookedSubscription.graphql.swift b/final/RocketReserverAPI/Sources/Operations/Subscriptions/TripsBookedSubscription.graphql.swift index 3904a38..400c85e 100644 --- a/final/RocketReserverAPI/Sources/Operations/Subscriptions/TripsBookedSubscription.graphql.swift +++ b/final/RocketReserverAPI/Sources/Operations/Subscriptions/TripsBookedSubscription.graphql.swift @@ -16,11 +16,11 @@ public class TripsBookedSubscription: GraphQLSubscription { public let __data: DataDict public init(_dataDict: DataDict) { __data = _dataDict } - public static var __parentType: ApolloAPI.ParentType { RocketReserverAPI.Objects.Subscription } + public static var __parentType: any ApolloAPI.ParentType { RocketReserverAPI.Objects.Subscription } public static var __selections: [ApolloAPI.Selection] { [ .field("tripsBooked", Int?.self), ] } public var tripsBooked: Int? { __data["tripsBooked"] } } -} +} \ No newline at end of file diff --git a/final/RocketReserverAPI/Sources/Schema/CustomScalars/ID.swift b/final/RocketReserverAPI/Sources/Schema/CustomScalars/ID.swift new file mode 100644 index 0000000..18e23c4 --- /dev/null +++ b/final/RocketReserverAPI/Sources/Schema/CustomScalars/ID.swift @@ -0,0 +1,11 @@ +// @generated +// This file was automatically generated and can be edited to +// implement advanced custom scalar functionality. +// +// Any changes to this file will not be overwritten by future +// code generation execution. + +import ApolloAPI + +/// The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID. +public typealias ID = String diff --git a/final/RocketReserverAPI/Sources/Schema/SchemaMetadata.graphql.swift b/final/RocketReserverAPI/Sources/Schema/SchemaMetadata.graphql.swift index 3a0f5a0..c7afe8a 100644 --- a/final/RocketReserverAPI/Sources/Schema/SchemaMetadata.graphql.swift +++ b/final/RocketReserverAPI/Sources/Schema/SchemaMetadata.graphql.swift @@ -3,8 +3,6 @@ import ApolloAPI -public typealias ID = String - public protocol SelectionSet: ApolloAPI.SelectionSet & ApolloAPI.RootSelectionSet where Schema == RocketReserverAPI.SchemaMetadata {} @@ -18,15 +16,15 @@ public protocol MutableInlineFragment: ApolloAPI.MutableSelectionSet & ApolloAPI where Schema == RocketReserverAPI.SchemaMetadata {} public enum SchemaMetadata: ApolloAPI.SchemaMetadata { - public static let configuration: ApolloAPI.SchemaConfiguration.Type = SchemaConfiguration.self + public static let configuration: any ApolloAPI.SchemaConfiguration.Type = SchemaConfiguration.self public static func objectType(forTypename typename: String) -> ApolloAPI.Object? { switch typename { - case "Mutation": return RocketReserverAPI.Objects.Mutation - case "User": return RocketReserverAPI.Objects.User case "Query": return RocketReserverAPI.Objects.Query + case "User": return RocketReserverAPI.Objects.User case "Launch": return RocketReserverAPI.Objects.Launch case "Mission": return RocketReserverAPI.Objects.Mission + case "Mutation": return RocketReserverAPI.Objects.Mutation case "Rocket": return RocketReserverAPI.Objects.Rocket case "TripUpdateResponse": return RocketReserverAPI.Objects.TripUpdateResponse case "Subscription": return RocketReserverAPI.Objects.Subscription diff --git a/final/graphql/LaunchList.graphql b/final/graphql/LaunchList.graphql index 034bfbc..f8064b4 100644 --- a/final/graphql/LaunchList.graphql +++ b/final/graphql/LaunchList.graphql @@ -3,12 +3,16 @@ query LaunchList($cursor: String) { cursor hasMore launches { - id - site - mission { - name - missionPatch(size: SMALL) - } + ... LaunchListDetail } } -} \ No newline at end of file +} + +fragment LaunchListDetail on Launch { + id + site + mission { + name + missionPatch(size: SMALL) + } +} diff --git a/final/graphql/Me.graphql b/final/graphql/Me.graphql new file mode 100644 index 0000000..f27858a --- /dev/null +++ b/final/graphql/Me.graphql @@ -0,0 +1,8 @@ +query Me { + me { + trips { + ... LaunchListDetail + isBooked + } + } +}