💡 Network을 이용해, Github 프로필 가져오기!
들어가기에 앞서,,
SearchViewController에 Component 연결까지도 되어있다!
일단 ViewController에서 해줘야 할 일은
- setupUI : UI 세팅
- userProfile 데이터 확인
- Binding : User가 업데이트 되면, UI까지 업데이트 되게!
- searchControl 세팅
- network 세팅
이다. 천천히 해보자!
1️⃣ setupUI
private func setupUI() {
// thumbnail 이미지 Radius 설정
thumbnail.layer.cornerRadius = 80
}
2️⃣ userProfile 데이터 Binding
var subscriptions = Set<AnyCancellable>()
@Published private(set) var user: UserProfile?
// ...
private func bind() {
$user
.receive(on: RunLoop.main)
.sink { [unowned self] result in
self.update(result)
}
.store(in: &subscriptions)
}
private func update(_ user : UserProfile?) {
}
3️⃣ searchControl
private func embedSearchControl() {
self.navigationItem.title = "Search"
let searchController = UISearchController(searchResultsController: nil)
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.placeholder = "yexjin"
searchController.searchResultsUpdater = self
searchController.searchBar.delegate = self
self.navigationItem.searchController = searchController
}
extension UserProfileViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let keyword = searchController.searchBar.text
print("search: \(keyword)") // for comfirmation
}
}
extension UserProfileViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
print("button clicked: \(searchBar.text)") // for comfirmation
}
}
여기까지, 전체코드와 실행 화면 및 결과는..!
// SearchViewController.swift
import UIKit
import Combine
class UserProfileViewController: UIViewController {
// setupUI
// userProfile
// bind
// searchControl
// network
var subscriptions = Set<AnyCancellable>()
@Published private(set) var user: UserProfile?
@IBOutlet weak var thumbnail: UIImageView!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var loginLabel: UILabel!
@IBOutlet weak var followersLabel: UILabel!
@IBOutlet weak var followingLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
embedSearchControl()
bind()
}
private func setupUI() {
thumbnail.layer.cornerRadius = 80
}
private func embedSearchControl() {
self.navigationItem.title = "Search"
let searchController = UISearchController(searchResultsController: nil)
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.placeholder = "yexjin"
searchController.searchResultsUpdater = self
searchController.searchBar.delegate = self
self.navigationItem.searchController = searchController
}
private func bind() {
$user
.receive(on: RunLoop.main)
.sink { [unowned self] result in
self.update(result)
}
.store(in: &subscriptions)
}
private func update(_ user : UserProfile?) {
}
}
extension UserProfileViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let keyword = searchController.searchBar.text
print("search: \(keyword)")
}
}
extension UserProfileViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
print("button clicked: \(searchBar.text)")
}
}
🚫 여기서 다시 해야할 거 짚고 넘어가기
- 이제 Bind을 통해 UI 업데이트
- search control을 통해, network 연결
4️⃣ Update UI! + Network
private func update(_ user : UserProfile?) {
guard let user = user else {
// user가 없을 경우,
self.nameLabel.text = "n/a"
self.loginLabel.text = "n/a"
self.followersLabel.text = ""
self.followingLabel.text = ""
self.thumbnail.image = nil
return
}
self.nameLabel.text = user.name
self.loginLabel.text = user.login
self.followersLabel.text = "follower: \(user.followers)"
self.followingLabel.text = "following: \(user.following)"
self.thumbnail.image = nil // 이건 나중에 세팅!
}
extension UserProfileViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
print("button clicked: \(searchBar.text)")
guard let keyword = searchBar.text, !keyword.isEmpty else { return }
let base = "https://api.github.com/"
let path = "users/\(keyword)"
let params: [String:String] = [:]
let header: [String:String] = ["Content-Type":"application/json"]
var urlComponents = URLComponents(string: base+path)!
let queryItems = params.map {(key: String, value: String) in
return URLQueryItem(name: key, value: value)
}
urlComponents.queryItems = queryItems
// base부터 header까지 URLRequest로 만들기
var request = URLRequest(url: urlComponents.url!)
header.forEach{ (key: String, value: String) in
request.addValue(value, forHTTPHeaderField: key)
}
// For Network
// URLSession을 이용해서 data task 만들기 (Combine이용)
URLSession.shared
.dataTaskPublisher(for: request)
.tryMap{ result -> Data in
guard let response = result.response as? HTTPURLResponse,
(200..<300).contains(response.statusCode) else {
let response = result.response as? HTTPURLResponse
let statusCode = response?.statusCode ?? -1
throw NetworkError.responseError(statusCode: statusCode)
}
return result.data
}
// 받은 데이터를 가지고 JSONDecoder을 이용하여 UserProfile로 Decoding
.decode(type: UserProfile.self, decoder: JSONDecoder())
.receive(on: RunLoop.main)
.sink { completion in
print("completion: \(completion)")
switch completion {
case .failure(let error):
self.user = nil
case .finished: break
}
} receiveValue: { user in
// ViewController에 있는 User로 세팅!
self.user = user
}.store(in: &subscriptions)
}
}
5️⃣ 이미지 가져오기
- 오픈소스 이용해보기!
import Kingfisher
let url = URL(string: "https://example.com/image.png")
imageView.kf.setImage(with: url)
self.thumbnail.kf.setImage(with: user.avatarUrl)
를 추가해주면,,
이제 구현 끝!
자세한 코드는 https://github.com/yexjin/iOS_Study의 GithubUserProfile폴더에서!
📌 정리,
- 구현목록
- setupUI : UI 세팅
- userProfile 데이터 확인
- searchControl 세팅
- Binding : User가 업데이트 되면, UI까지 업데이트 되게!
- network 세팅
- urlComponent, urlRequest 만들기
- urlSession을 이용해, Data task 만들기
- 오픈소스를 이용한 Image 가져오기 (Pakage 설치)
Reference
패스트캠퍼스 온라인 강의
https://yexjinitlog.tistory.com/121?category=1012361
'iOS > Toy project' 카테고리의 다른 글
[iOS : Toy Project] Todo List 만들기 (1) (0) | 2022.08.15 |
---|---|
[iOS : Toy Project] Github Profile (2) : Refactoring (0) | 2022.07.17 |
[iOS : Toy Project] Apple Framework List (4) : Combine (0) | 2022.07.08 |
[iOS : Toy Project] Head Space Focus (2) : Navigation (0) | 2022.06.30 |
[iOS : Toy Project] Apple Framework List (3) : Modal (0) | 2022.06.29 |