[iOS] Apollo를 이용해 Graph QL 서버와 연동해보기 - 2
안녕하세요~!🙋🙋🙋 5anniversary 입니다~!~!~!~!
이번 포스팅에서는 저번 apollo 활용 첫번째 포스팅에 이어서 서버 데이터를 받아오는 시간을 가져볼거에요~!~!
우선 GraphQL이 어떻게 사용되는지 간단하게 알아볼까요???
GraphQL은 거의 공통적으로 query를 클라이언트 레벨에서 request 해주면 server에서 해당 query를 승인 해준뒤 알맞는 데이터를 보내주는 구조에요!! 이런 구조를 글로만 설명하면 이해가 좀... 힘들겠죠??? 보러가시죠~!
사이트에 들어가시죠!!

사이트에 들어가면 이런 화면이 나올거에요!!

오른쪽의 Docs를 누르면 이 서버에서 제공하는 GraphQL의 query리스트가 있구요!!

schema를 누르면 이 서버에서 제공하는 GraphQL의 shema를 보여줍니다.
여기서 데이터가 어떤 방식으로 전달되는지 볼게요!!
왼쪽 창에
query {
launches{
cursor
hasMore
}
}
와 같은 쿼리를 넣어주세요!!

그럼 이렇게 데이터가 나오게 돼요!!
여기서 원하는 데이터들을 알맞게 받아올수있는데요!!
docs를 보고 원하는 데이터들을 쿼리 리스트에 넣어주세요!!
query {
launches{
cursor
hasMore
launches{
id
}
}
}
이런 쿼리를 넣어주면 이렇게 나온답니다~!

이제 GraphQL이 어떤 방식으로 구현되는지 알아봤으니 구현을 해볼까여~??

empty 파일으로 LaunchList.graphql 라는 이름의 파일을 만들어 볼게요~! 여기서도 조건이 있는데요
1. Target이 설정되어있으면 안되구요
2. schema.json 파일과 같은 레벨에 위치해 있어야합니다.
그리고 이제 알맞는 쿼리를 넣어줄까요??
query LaunchList($cursor:String) {
launches(after:$cursor) {
hasMore
cursor
launches {
id
site
mission {
name
missionPatch(size: SMALL)
}
}
}
}
여기서는 cursor를 페이징처리를 위해 받아서 넘겨주는 방식으로 처리합니다.
그리고~!
네트워크 관련 코드를 작성해야겠죠????
import Apollo
class Network {
static let shared = Network()
private(set) lazy var apollo: ApolloClient = {
let httpNetworkTransport =
HTTPNetworkTransport(url: URL(string: "https://apollo-fullstack-tutorial.herokuapp.com/")!)
httpNetworkTransport.delegate = self
return ApolloClient(networkTransport: httpNetworkTransport)
}()
}
extension Network: HTTPNetworkTransportPreflightDelegate {
func networkTransport(_ networkTransport: HTTPNetworkTransport,
shouldSend request: URLRequest) -> Bool {
return true
}
func networkTransport(_ networkTransport: HTTPNetworkTransport,
willSend request: inout URLRequest) {
}
}
를 만들고 작성해주세요~!
그 다음 service코드를 작성할건데요
import Apollo
public final class LaunchListQuery: GraphQLQuery {
/// The raw GraphQL definition of this operation.
public let operationDefinition: String =
"""
query LaunchList($cursor: String) {
launches(after: $cursor) {
__typename
hasMore
cursor
launches {
__typename
id
site
mission {
__typename
name
missionPatch(size: SMALL)
}
}
}
}
"""
public let operationName: String = "LaunchList"
public var cursor: String?
public init(cursor: String? = nil) {
self.cursor = cursor
}
public var variables: GraphQLMap? {
return ["cursor": cursor]
}
public struct Data: GraphQLSelectionSet {
public static let possibleTypes: [String] = ["Query"]
public static let selections: [GraphQLSelection] = [
GraphQLField("launches", arguments: ["after": GraphQLVariable("cursor")], type: .nonNull(.object(Launch.selections))),
]
public private(set) var resultMap: ResultMap
public init(unsafeResultMap: ResultMap) {
self.resultMap = unsafeResultMap
}
public init(launches: Launch) {
self.init(unsafeResultMap: ["__typename": "Query", "launches": launches.resultMap])
}
public var launches: Launch {
get {
return Launch(unsafeResultMap: resultMap["launches"]! as! ResultMap)
}
set {
resultMap.updateValue(newValue.resultMap, forKey: "launches")
}
}
public struct Launch: GraphQLSelectionSet {
public static let possibleTypes: [String] = ["LaunchConnection"]
public static let selections: [GraphQLSelection] = [
GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
GraphQLField("hasMore", type: .nonNull(.scalar(Bool.self))),
GraphQLField("cursor", type: .nonNull(.scalar(String.self))),
GraphQLField("launches", type: .nonNull(.list(.object(Launch.selections)))),
]
public private(set) var resultMap: ResultMap
public init(unsafeResultMap: ResultMap) {
self.resultMap = unsafeResultMap
}
public init(hasMore: Bool, cursor: String, launches: [Launch?]) {
self.init(unsafeResultMap: ["__typename": "LaunchConnection", "hasMore": hasMore, "cursor": cursor, "launches": launches.map { (value: Launch?) -> ResultMap? in value.flatMap { (value: Launch) -> ResultMap in value.resultMap } }])
}
public var __typename: String {
get {
return resultMap["__typename"]! as! String
}
set {
resultMap.updateValue(newValue, forKey: "__typename")
}
}
public var hasMore: Bool {
get {
return resultMap["hasMore"]! as! Bool
}
set {
resultMap.updateValue(newValue, forKey: "hasMore")
}
}
public var cursor: String {
get {
return resultMap["cursor"]! as! String
}
set {
resultMap.updateValue(newValue, forKey: "cursor")
}
}
public var launches: [Launch?] {
get {
return (resultMap["launches"] as! [ResultMap?]).map { (value: ResultMap?) -> Launch? in value.flatMap { (value: ResultMap) -> Launch in Launch(unsafeResultMap: value) } }
}
set {
resultMap.updateValue(newValue.map { (value: Launch?) -> ResultMap? in value.flatMap { (value: Launch) -> ResultMap in value.resultMap } }, forKey: "launches")
}
}
public struct Launch: GraphQLSelectionSet {
public static let possibleTypes: [String] = ["Launch"]
public static let selections: [GraphQLSelection] = [
GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
GraphQLField("id", type: .nonNull(.scalar(GraphQLID.self))),
GraphQLField("site", type: .scalar(String.self)),
GraphQLField("mission", type: .object(Mission.selections)),
]
public private(set) var resultMap: ResultMap
public init(unsafeResultMap: ResultMap) {
self.resultMap = unsafeResultMap
}
public init(id: GraphQLID, site: String? = nil, mission: Mission? = nil) {
self.init(unsafeResultMap: ["__typename": "Launch", "id": id, "site": site, "mission": mission.flatMap { (value: Mission) -> ResultMap in value.resultMap }])
}
public var __typename: String {
get {
return resultMap["__typename"]! as! String
}
set {
resultMap.updateValue(newValue, forKey: "__typename")
}
}
public var id: GraphQLID {
get {
return resultMap["id"]! as! GraphQLID
}
set {
resultMap.updateValue(newValue, forKey: "id")
}
}
public var site: String? {
get {
return resultMap["site"] as? String
}
set {
resultMap.updateValue(newValue, forKey: "site")
}
}
public var mission: Mission? {
get {
return (resultMap["mission"] as? ResultMap).flatMap { Mission(unsafeResultMap: $0) }
}
set {
resultMap.updateValue(newValue?.resultMap, forKey: "mission")
}
}
public struct Mission: GraphQLSelectionSet {
public static let possibleTypes: [String] = ["Mission"]
public static let selections: [GraphQLSelection] = [
GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
GraphQLField("name", type: .scalar(String.self)),
GraphQLField("missionPatch", arguments: ["size": "SMALL"], type: .scalar(String.self)),
]
public private(set) var resultMap: ResultMap
public init(unsafeResultMap: ResultMap) {
self.resultMap = unsafeResultMap
}
public init(name: String? = nil, missionPatch: String? = nil) {
self.init(unsafeResultMap: ["__typename": "Mission", "name": name, "missionPatch": missionPatch])
}
public var __typename: String {
get {
return resultMap["__typename"]! as! String
}
set {
resultMap.updateValue(newValue, forKey: "__typename")
}
}
public var name: String? {
get {
return resultMap["name"] as? String
}
set {
resultMap.updateValue(newValue, forKey: "name")
}
}
public var missionPatch: String? {
get {
return resultMap["missionPatch"] as? String
}
set {
resultMap.updateValue(newValue, forKey: "missionPatch")
}
}
}
}
}
}
}
예시로 나와있는 코드는 이렇게 되는데요 우선 이번 시간에는 실행 해보고~~
다음 포스팅에서 자세히 알아보도록 할게요~~~
그리고 이 서버 코드를 실행시키는 코드도 있어야겠죠??
예시에서는 어플을 실행시키자마자 서버 연동이 되는지 확인 하기 위해서 AppDelegate에 넣어줬는데요
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
Network.shared.apollo.fetch(query: LaunchListQuery()) { result in
switch result {
case .success(let graphQLResult):
print(graphQLResult)
case .failure(let error):
print("Failure! Error: \(error)")
}
}
return true
}
이렇게 작성한 뒤 빌드를 해볼까요???

자 이렇게 서버에서 받아오는 데이터를 볼수가 있죠???🎉 🎉 🎉 🎉
이제 다음 포스팅으로 넘어가서 하나씩 이 코드가 어떻게 작동하는지 알아보도록 하죠!!!
수고하셨습니다.