npm install -g apollo-codegen
apollo-codegen download-schema https://api.github.com/graphql --output schema.json --header "Authorization: Bearer <token>"
APOLLO_FRAMEWORK_PATH="$(eval find $FRAMEWORK_SEARCH_PATHS -name "Apollo.framework" -maxdepth 1)" if [ -z "$APOLLO_FRAMEWORK_PATH" ]; then echo "error: Couldn't find Apollo.framework in FRAMEWORK_SEARCH_PATHS; make sure to add the framework to your project." exit 1 fi cd "${SRCROOT}/Source" $APOLLO_FRAMEWORK_PATH/check-and-run-apollo-codegen.sh generate $(find . -name '*.graphql') --schema schema.json --output GraphQL/API.swift
query SearchRepos($searchText: String!) { search(first: 20, query: $searchText, type: REPOSITORY) { nodes { ... on Repository { ...RepositoryDetail } } } } fragment RepositoryDetail on Repository { id nameWithOwner viewerHasStarred stargazers { totalCount } }
let apollo: ApolloClient = { let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = ["Authorization": "bearer <your token>"] let url = URL(string: "https://api.github.com/graphql")! return ApolloClient(networkTransport: HTTPNetworkTransport(url: url, configuration: configuration)) }()
import Apollo class ReposListViewModel { private var currentSearchCancellable: Cancellable? }
func search(for text: String, completion: @escaping ([RepositoryDetail]) -> Void) { currentSearchCancellable?.cancel() let query = SearchReposQuery(searchText: text) currentSearchCancellable = apollo.fetch(query: query, cachePolicy: .returnCacheDataAndFetch, queue: .main, resultHandler: { (result, error) in if let result = result, let data = result.data { let repositoryDetails = (data.search.nodes ?? [SearchReposQuery.Data.Search.Node?]()).map{$0?.asRepository}.filter{$0 != nil}.map{($0?.fragments.repositoryDetail)!} completion(repositoryDetails) } else { print(error as Any) completion([RepositoryDetail]()) } }) }
let viewModel = ReposListViewModel() var repositoryDetails = [RepositoryDetail]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return repositoryDetails.count }
class ReposListCell: UITableViewCell { @IBOutlet weak var repoName: UILabel! @IBOutlet weak var starCount: UILabel! @IBOutlet weak var starButton: UIButton! var repositoryDetail: RepositoryDetail! { didSet { repoName.text = repositoryDetail.nameWithOwner starCount.text = "\(repositoryDetail.stargazers.totalCount)" if repositoryDetail.viewerHasStarred { starButton.setImage( imageLiteral(resourceName: "ic_full_star"), for: .normal) } else { starButton.setImage( imageLiteral(resourceName: "ic_empty_star"), for: .normal) } } } }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "ReposListCell") as! ReposListCell cell.repositoryDetail = repositoryDetails[indexPath.row] return cell }
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { viewModel.search(for: searchBar.text ?? "") { [unowned self] repositoryDetails in self.repositoryDetails = repositoryDetails self.tableView.reloadData() } }
mutation AddStar( $repositoryId: ID!) { addStar(input: {clientMutationId: “<your ID>”, starrableId: $repositoryId}) { starrable { ... on Repository { ...RepositoryDetail } } } } mutation RemoveStar($repositoryId: ID!) { removeStar(input: {clientMutationId: “<your ID>”, starrableId: $repositoryId}) { starrable { ... on Repository { ...RepositoryDetail } } } }
func addStar(for repositoryID: String, completion: @escaping (RepositoryDetail?) -> Void ) { currentAddStarCancellable?.cancel() let mutation = AddStarMutation(repositoryId: repositoryID) currentAddStarCancellable = apollo.perform(mutation: mutation, queue: .main, resultHandler: { (result, error) in if let result = result, let data = result.data { let repositoryDetails = data.addStar?.starrable.asRepository?.fragments.repositoryDetail completion(repositoryDetails) } else { print(error as Any) completion(nil) } }) } func removeStar(for repositoryID: String, completion: @escaping (RepositoryDetail?) -> Void ) { currentRemoveStarCancellable?.cancel() let mutation = RemoveStarMutation(repositoryId: repositoryID) currentAddStarCancellable = apollo.perform(mutation: mutation, queue: .main, resultHandler: { (result, error) in if let result = result, let data = result.data { let repositoryDetails = data.removeStar?.starrable.asRepository?.fragments.repositoryDetail completion(repositoryDetails) } else { print(error as Any) completion(nil) } }) }
protocol ReposListCellDelegate: class { func starTapped(for cell: ReposListCell) }
weak var delegate: ReposListCellDelegate? override func awakeFromNib() { super.awakeFromNib() starButton.addTarget(self, action: #selector(starTapped), for: .touchUpInside) } @objc func starTapped() { delegate?.starTapped(for: self) }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "ReposListCell") as! ReposListCell cell.repositoryDetail = repositoryDetails[indexPath.row] cell.delegate = self return cell }
func updateTableView(for newDetail: RepositoryDetail?) { if let repositoryDetail = newDetail { for (index, detail) in repositoryDetails.enumerated() { if detail.id == repositoryDetail.id { self.repositoryDetails[index] = repositoryDetail for visibleCell in tableView.visibleCells { if (visibleCell as! ReposListCell).repositoryDetail.id == repositoryDetail.id { (visibleCell as! ReposListCell).repositoryDetail = repositoryDetail } } } } } }
extension ReposListViewController: ReposListCellDelegate { func starTapped(for cell: ReposListCell) { if cell.repositoryDetail.viewerHasStarred { viewModel.removeStar(for: cell.repositoryDetail.id) { [unowned self] repositoryDetail in self.updateTableView(for: repositoryDetail) } } else { viewModel.addStar(for: cell.repositoryDetail.id) { [unowned self] repositoryDetail in self.updateTableView(for: repositoryDetail) } } } }
Source: https://habr.com/ru/post/352850/