Last active
May 15, 2018 12:37
-
-
Save sahara-ooga/a0de1555c0e3b1e35c30a72edd913f88 to your computer and use it in GitHub Desktop.
Playground codes which search github repository using RxSwift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: [Previous](@previous) | |
import UIKit | |
import RxSwift | |
import RxCocoa | |
import PlaygroundSupport | |
// Definition | |
struct GithubRepository { | |
let name: String | |
let starCount: Int | |
init(from dictionary: [String: Any]) { | |
name = dictionary["full_name"] as! String | |
starCount = dictionary["stargazers_count"] as! Int | |
} | |
} | |
struct SearchResult { | |
let repos: [GithubRepository] | |
let totalCount: Int | |
init?(from response: Any) { | |
guard let response = response as? [String: Any], | |
let reposDictionaries = response["items"] as? [[String: Any]], | |
let count = response["total_count"] as? Int else { return nil } | |
repos = reposDictionaries.compactMap({ GithubRepository(from: $0) }) | |
totalCount = count | |
} | |
} | |
func searchRepos(keyword: String) -> Observable<SearchResult?> { | |
let endPoint = "https://api.github.com" | |
let path = "/search/repositories" | |
let query = "?q=\(keyword)" | |
let url = URL(string: endPoint + path + query)! | |
let request = URLRequest(url: url) | |
return URLSession.shared.rx | |
.json(request: request) | |
.map { SearchResult(from: $0) } | |
} | |
class SearchViewController: UIViewController { | |
let searchField = UITextField() | |
let totalCountLabel = UILabel() | |
let reposLabel = UILabel() | |
let disposeBag = DisposeBag() //Disposableを保持しておいて、deinitされるタイミングで購読の破棄を一気に行う | |
//ViewControllerが破棄されるタイミングで購読もすべて破棄される | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
view.backgroundColor = .gray | |
setupSubviews() | |
bind() | |
} | |
func setupSubviews() { | |
searchField.frame = CGRect(x: 10, y: 10, width: 300, height: 20) | |
totalCountLabel.frame = CGRect(x: 10, y: 40, width: 300, height: 20) | |
reposLabel.frame = CGRect(x: 10, y: 60, width: 300, height: 500) | |
searchField.borderStyle = .roundedRect | |
searchField.keyboardType = .alphabet | |
reposLabel.numberOfLines = 0 | |
[searchField, totalCountLabel, reposLabel] | |
.forEach(view.addSubview(_:)) | |
} | |
} | |
extension SearchViewController { | |
func bind() { | |
let result: Observable<SearchResult?> = searchField.rx.text | |
.orEmpty//Optional<String> -> String or "" | |
.asObservable()// テキストフィールドの値が更新されるたびに、最新の値を排出する | |
.skip(1)//最初に排出する値は無視する | |
.debounce(0.3, scheduler: MainScheduler.instance)//直前の値から0.3秒以内に排出された次の値は無視 | |
.distinctUntilChanged()//直前の値と同じなら無視する | |
.flatMapLatest{ searchRepos(keyword: $0)//新しい値だけの結果にする | |
.observeOn(MainScheduler.instance)//以降の処理はメインスレッドで行う指定 | |
.catchErrorJustReturn(nil)//もしもエラーが排出された場合はnilを返す | |
//エラーが排出されてもObservableシーケンスに値が流れ続けるようになる | |
}.share(replay: 1)//connectable Observableへの変換 | |
// SearchResultからStringへの変換 | |
let foundRepos: Observable<String> = result.map { | |
let repos = $0?.repos ?? [GithubRepository]() | |
return repos.reduce("") { | |
$0 + "\($1.name)(\($1.starCount))\n" | |
} | |
} | |
// SearchResultからStringへの変換 | |
let foundCount: Observable<String> = result.map { | |
let count = $0?.totalCount ?? 0 | |
return "TotalCount: \(count)" | |
} | |
//UIパーツへの束縛処理 | |
foundRepos | |
.bind(to: reposLabel.rx.text)//レポジトリの名前とスター数の文字列をreposLabel.rx.textにバインドする | |
.disposed(by: disposeBag) | |
foundCount | |
.startWith("Input Repository Name")//検索結果から生成する文字列が渡ってくるまで表示する値を指定 | |
.bind(to: totalCountLabel.rx.text)//検索結果数をtotalCountLabel.rx.textにバインドする | |
.disposed(by: disposeBag) | |
} | |
} | |
//PlaygroundPage.current.liveView = SearchViewController() | |
let parent = UIViewController() | |
let vc = SearchViewController() | |
vc.view.layoutIfNeeded() | |
parent.view.translatesAutoresizingMaskIntoConstraints = false | |
vc.view.translatesAutoresizingMaskIntoConstraints = false | |
parent.addChildViewController(vc) | |
parent.view.addSubview(vc.view) | |
NSLayoutConstraint.activate([ | |
vc.view.leadingAnchor.constraint(equalTo: parent.view.leadingAnchor), | |
vc.view.trailingAnchor.constraint(equalTo: parent.view.trailingAnchor), | |
vc.view.topAnchor.constraint(equalTo: parent.view.topAnchor), | |
vc.view.bottomAnchor.constraint(equalTo: parent.view.bottomAnchor) | |
] | |
) | |
let traits = UITraitCollection( | |
traitsFrom: [ | |
UITraitCollection(verticalSizeClass: .compact), | |
UITraitCollection(horizontalSizeClass: .compact), | |
UITraitCollection(preferredContentSizeCategory: .extraExtraExtraLarge) | |
] | |
) | |
parent.setOverrideTraitCollection(traits, forChildViewController: vc) | |
parent.preferredContentSize = parent.view.frame.size | |
PlaygroundPage.current.liveView = parent | |
//: [Next](@next) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment