
Standard Mission
- ‘공공데이터포털’의 API를 활용해 서버 데이터 가져와보기
- 반드시 JSON 데이터를 데이터 모델을 걸쳐 디코딩하여 Swift에서 사용할 수 있게 변환해야 합니다!
- Google, Kakao 등의 소셜 로그인 구현해보기
- API명세서나 여러 자료를 통해 차근차근 진행해보아요!
- 한 가지 힌트 :
소셜 로그인을 구현하기 위해선 해당 기업에서 제공하는 라이브러리(SDK)를 import하셔야 합니다. (CocoaPods에 설치 후 import)
// TourDataModel.swift
struct TourData: Codable {
var comMsgHeader: TourComHeader
var msgHeader: TourMsgHeader
var msgBody: [TourBody]
}
struct TourComHeader: Codable {
var successYN: String
var returnCode: String
var returnMessage: String
}
struct TourMsgHeader: Codable {
var pageNo: Int
var totalCount: Int
var totalPage: Int
var numOfRows: Int
}
struct TourBody: Codable {
var tourSeq: String
var id: String
var name: String
var recommend: String
var expression: String
var dCode: String?
var tGubun: String?
var pGubun: String?
var lGubun: String?
var cGubun: String?
var sGubun: String?
var addr1: String
var addr2: String?
var zipcode: String?
var nAddr1: String?
var nAddr2: String?
var telCode: String
var telKuk: String
var telNo: String
var hitCnt: String
var dTag: String?
var dTel: String?
var idxImgPath: String?
var idxImgName: String?
var dCodeNm: String?
var contents2: String?
var imgIdx: String?
var keyword: String?
var useYn: String
var dLang: String
var cid: String?
}
이 API는 Response로 오는 데이터들이 엄청 많은데, 여기서는 name, recommend(추천 여부), addr1, addr2(기본주소, 상세주소), idxImgPath(썸네일물리적저장경로)만 사용했다.
나는 Alamofire를 사용해서 서버 통신을 구현했다.
extension ViewController {
func getTourData() {
let url = "http://apis.data.go.kr/6300000/tourDataService/tourDataListJson?serviceKey=key&pageNo=1&numOfRows=30"
AF.request(url, method: .get)
.validate()
.responseDecodable(of: TourData.self) { response in
switch response.result {
case .success(let response):
self.tourData = response
self.tableView.reloadData()
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
원래 request parameter를 따로 변수에 넣고 요청을 보내려고 했는데 디코딩 실패 에러가 떴다. 그래서 url에 파라미터를 통째로 넣었더니 이건 정상적으로 응답이 온다. 왜지…..
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "mainCell") as? MainTableViewCell else {
return UITableViewCell()
}
guard let tourBody = tourData?.msgBody else {
return UITableViewCell()
}
let data = tourBody[indexPath.row]
cell.titleLabel.text = data.name
cell.recommendationLabel.isHidden = data.recommend == "Y" ? false : true
cell.keywordLabel.text = "\(data.addr1) \(data.addr2 ?? "")"
if let imagePath = data.idxImgPath {
if let url = URL(string: "http://www.daejeon.go.kr/" + imagePath) {
cell.tourImageView.load(url: url)
}
return cell
}
extension UIImageView {
func load(url: URL) {
DispatchQueue.global().async { [weak self] in
if let data = try? Data(contentsOf: url) {
if let image = UIImage(data: data) {
DispatchQueue.main.async {
self?.image = image
}
}
}
}
}
}
처음에는 cellForRowAt 메소드에서 그냥 URL로 이미지를 가져오도록 했다. 그래서 스크롤을 할 때마다 이미지가 다시 로드되는 문제가 있었다.
검색을 해보니 캐시를 사용해서 캐시에 데이터가 없는 경우에만 서버와 통신하도록 하면 해결이 되는 것 같았다.
[iOS - swift] TableView 캐시를 이용한 효율적인 이미지 로딩 방법 (async, cache)
위 블로그 내용은 너무 복잡해서………ㅎㅎ 더 찾아보다 Kingfisher라는 라이브러리를 발견했다.
Kingfisher는 이미지 다운로드뿐만 아니라 캐싱까지 지원해주기 때문에 간단하게 이미지 로딩을 구현할 수 있었다.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// ...
if let imagePath = data.idxImgPath {
// Kingfisher 라이브러리 사용
KF.url(URL(string: "http://www.daejeon.go.kr/" + imagePath)).set(to: cell.tourImageView)
}
return cell
}
이제 이미지 로드가 한 번만 이뤄진다!
문자열 URL 변환부터 image view 이미지 설정까지 코드 한 줄로 끝내니까 정말 너무 편하다…!!!!
위에 캐시 사용법이랑 Kingfisher에 있는 다른 기능들도 나중에 더 알아봐야겠다.