Swift Github API 연결해보기
2022. 5. 19. 23:30ㆍSwift
반응형
Swift Github API 연결해보기 그리고 Rx를 곁들인..
Repository.swfit 파일 생성
- JSON 파일을 가지고 오면 원하는 형태로 디코딩 하기 위해 구조체를 하나 만들어 줍니다.
import Foundation
struct Repository: Decodable {
let id: Int
let name: String
let description: String
let stargazersCount: Int
let language: String
enum CodingKeys: String, CodingKey {
case id, name, description, language
case stargazersCount = "stargazers_count"
}
}
미리 정의해 놓은 customCell
import UIKit
import SnapKit
class RepositoryListCell: UITableViewCell {
var repository: Repository?
let nameLabel = UILabel()
let descriptionLabel = UILabel()
let startImageView = UIImageView()
let starLabel = UILabel()
let languageLabel = UILabel()
override func layoutSubviews() {
super.layoutSubviews()
[
nameLabel, descriptionLabel,
startImageView, starLabel, languageLabel
].forEach {
contentView.addSubview($0)
}
guard let repository = repository else { return }
nameLabel.text = repository.name
nameLabel.font = .systemFont(ofSize: 15, weight: .bold)
descriptionLabel.text = repository.description
descriptionLabel.font = .systemFont(ofSize: 15)
descriptionLabel.numberOfLines = 2
startImageView.image = UIImage(systemName: "star")
starLabel.text = "\(repository.stargazersCount)"
starLabel.font = .systemFont(ofSize: 16)
starLabel.textColor = .gray
languageLabel.text = repository.language
languageLabel.font = .systemFont(ofSize: 16)
languageLabel.textColor = .gray
nameLabel.snp.makeConstraints {
$0.top.leading.trailing.equalToSuperview().inset(10)
}
descriptionLabel.snp.makeConstraints {
$0.top.equalTo(nameLabel.snp.bottom).offset(3)
$0.leading.trailing.equalTo(nameLabel)
}
startImageView.snp.makeConstraints {
$0.top.equalTo(descriptionLabel.snp.bottom).offset(8)
$0.leading.trailing.equalTo(descriptionLabel)
$0.width.height.equalTo(20)
$0.bottom.equalToSuperview().inset(18)
}
starLabel.snp.makeConstraints {
$0.centerY.equalTo(startImageView)
$0.leading.equalTo(startImageView.snp.trailing).offset(5)
}
languageLabel.snp.makeConstraints {
$0.centerY.equalTo(starLabel)
$0.leading.equalTo(starLabel.snp.trailing).offset(12)
}
}
}
ViewController
import RxSwift
import RxCocoa
import UIKit
class RepositoryListViewController: UITableViewController {
private let organization = "Apple"
private let repositories = BehaviorSubject<[Repository]>(value: [])
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
title = organization + "Repositories"
self.refreshControl = UIRefreshControl() // 당겨서 새로고침
let refreshControl = self.refreshControl!
refreshControl.backgroundColor = .white
refreshControl.tintColor = .darkGray
refreshControl.attributedTitle = NSAttributedString(string: "당겨서 새로고침")
refreshControl.addTarget(self, action: #selector(refresh), for: .valueChanged)
tableView.register(RepositoryListCell.self, forCellReuseIdentifier: "RepositoryListCell")
tableView.rowHeight = 140
}
@objc func refresh() {
DispatchQueue.global(qos: .background).async { [weak self] in
guard let self = self else { return }
self.fetchRepositories(of: self.organization)
}
}
func fetchRepositories(of organization: String) {
Observable.from([organization]) // from은 array만 받을수 있다.
.map { organization -> URL in
return URL(string: "https://api.github.com/orgs/\(organization)/repos")!
}
.map { url -> URLRequest in
var request = URLRequest(url: url)
request.httpMethod = "GET"
return request
}
.flatMap { request -> Observable<(response: HTTPURLResponse, data: Data)> in
return URLSession.shared.rx.response(request: request)
}
.filter { responds, _ in // 정상적인 응답과 비정상적인 응답을 걸러냄
return 200..<300 ~= responds.statusCode
}
.map { _, data -> [[String: Any]] in
guard let json = try? JSONSerialization.jsonObject(with: data, options: []),
let result = json as? [[String: Any]] else {
return []
}
return result
}
.filter { result in
return result.count > 0
}
.map { objects in
return objects.compactMap { dic -> Repository? in
guard let id = dic["id"] as? Int,
let name = dic["name"] as? String,
let description = dic["description"] as? String,
let stargazersCount = dic["stargazers_count"] as? Int,
let language = dic["language"] as? String else {
return nil
}
return Repository(id: id, name: name, description: description, stargazersCount: stargazersCount, language: language)
}
}
.subscribe(onNext: { [weak self] newRepositories in // filter를 통해서 걸러냈음으로 나머지 이벤트들은 사용하지 않을거임.
self?.repositories.onNext(newRepositories)
DispatchQueue.main.async {
self?.tableView.reloadData()
self?.refreshControl?.endRefreshing()
}
})
.disposed(by: disposeBag)
}
}
// UITableView Delegate DataSource
extension RepositoryListViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
do {
return try repositories.value().count
} catch {
return 0
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "RepositoryListCell", for: indexPath) as? RepositoryListCell else { return UITableViewCell() }
var currentRepo: Repository? {
do {
return try repositories.value()[indexPath.row]
} catch {
return nil
}
}
cell.repository = currentRepo
return cell
}
}
하 어렵네
반응형
'Swift' 카테고리의 다른 글
Swift MVVM 알아보기 (0) | 2022.05.29 |
---|---|
Swift UITapGestureRecognizer (0) | 2022.05.21 |
Swift 인스타그램 앱 클론 코딩(6) (0) | 2022.05.08 |
Swift 인스타그램 앱 클론 코딩(5) (0) | 2022.05.07 |
Swift 인스타그램 앱 클론 코딩(4) (0) | 2022.05.05 |