AWSAppSyncClientの処理をRxでラップする


## やったこと

AWSAppSyncClientで頻出のfetch, mutate, subscribe処理をRxでラップして、
`Single`や`Observable`などのオペレータで扱えるようにしました。

AWSAppSyncClientでは内部でApolloClientをラップしているので、
AWSサービスなどを使わずにApolloをGraphQLしてRxで扱うケースと同じようなコードになるかなと思います。

## コード

```Swift:AWSAppSyncClient+Rx.swift
import Foundation
import AWSAppSync
import RxSwift
import SwiftyBeaver

let log = SwiftyBeaver.self

enum AppSyncError: Error {
    case gqlErrors([GraphQLError])
    case noData
    case unknowned
}

extension AWSAppSyncClient: ReactiveCompatible {}

extension Reactive where Base: AWSAppSyncClient {
    
    public func fetch<Query: GraphQLQuery>(
        query: Query,
        cachePolicy: CachePolicy = .returnCacheDataElseFetch,
        queue: DispatchQueue = DispatchQueue.main
    ) -> Single<Query.Data> {
        return Single.create { [weak base] observer in
            log.debug("query did called - query: \(query)")
            let cancellable = base?.fetch(query: query, cachePolicy: cachePolicy, queue: queue) {(result, error) in
                if let error = error {
                    log.error("client connection error - error: \(error)")
                    observer(.error(error))
                }
                switch result {
                case .none:
                    log.error("fetched result is nil")
                    observer(.error(AppSyncError.noData))
                case .some(let result):
                    if let errors = result.errors {
                        log.error("GraphQL fetch error - errors: \(errors.map { $0.description } )")
                        observer(.error(AppSyncError.gqlErrors(errors)))
                    } else if let data = result.data {
                        observer(.success(data))
                    } else {
                        log.error("Unknowned error. result and error, both values ​​are nil")
                        observer(.error(AppSyncError.unknowned))
                    }
                }
            }
            return Disposables.create {
                cancellable?.cancel()
            }
        }
    }

    public func perform<Mutation: GraphQLMutation>(
        mutation: Mutation,
        queue: DispatchQueue = .main
    ) -> Single<Mutation.Data> {
        return Single.create { [weak base] observer in
            log.debug("mutation did called - mutation: \(mutation)")
            let cancellable = base?.perform(mutation: mutation, queue: queue, optimisticUpdate: nil, conflictResolutionBlock: nil, resultHandler: { (result, error) in
                if let error = error {
                    log.error("client connection error - error: \(error)")
                    observer(.error(error))
                }
                switch result {
                case .none:
                    assertionFailure("Unexpected error - in case result is nil, should return error before evaluating result value")
                    observer(.error(AppSyncError.resultIsNil)) // 一応エラーを返しておく
                case .some(let result):
                    if let errors = result.errors {
                        log.error("GraphQL perform error - errors: \(errors.map { $0.description } )")
                        observer(.error(AppSyncError.gqlErrors(errors)))
                    } else if let data = result.data {
                        observer(.success(data))
                    } else {
                        log.error("perform unknowned error. result and error, both values ​​are nil")
                        observer(.error(AppSyncError.unknowned))
                    }
                }
            })
            return Disposables.create {
                cancellable?.cancel()
            }
        }
    }

    public func subscribe<Subscription: GraphQLSubscription>(
      subscription: Subscription,
      queue: DispatchQueue = .main
    ) -> Observable<Subscription.Data> {
        return Observable.create { [weak base] observer in
            log.debug("subscription did called - subscription: \(subscription)")
            var cancellable: Cancellable?
            do {
                cancellable = try base?.subscribe(subscription: subscription, queue: queue, statusChangeHandler: nil, resultHandler: { (result, _, error) in
                    if let error = error {
                        log.error("client connection error - error: \(error)")
                        observer.onError(error)
                    }
                    switch result {
                    case .none:
                        assertionFailure("Unexpected error - in case result is nil, should return error before evaluating result value")
                        observer.onError(AppSyncError.resultIsNil) // 一応エラーを返しておく
                    case .some(let result):
                        if let data = result.data {
                            log.debug("subscribed data: - \(data)")
                            observer.onNext(data)
                        } else if let errors = result.errors {
                            log.error("GraphQL subscribe error - errors: \(errors.map { $0.description } )")
                            observer.onError(AppSyncError.gqlErrors(errors))
                        }
                    }
                })
            } catch {
                log.error("subscribe configurations error")
                observer.onError(AppSyncError.unknowned) // base?.subscribe内でthrowされるがどんなerrorが投げられているのかは不明
                cancellable?.cancel()
            }
            return Disposables.create {
                cancellable?.cancel()
            }
        }
    }
}

```