[iOS/Swift] ํ ์ด๋ธ๋ทฐ ์ ์ API ์ฐ๊ฒฐํ๊ธฐ (GETํต์ , Alamofire์ด์ฉ)
์๋ฒ ๊ฐ๋ฐ์๋ถ์ด ๋ง๋ค์ด์ฃผ์ ํธ์ํฐ ํธ์๋ฆฌ์คํธ API๋ฅผ ์ด์ฉํด tableview ์ ๊ณผ ๋ฐ์ดํฐ๋ค์ ์ฐ๊ฒฐ์์ผ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค!
Request header์ Response Body๋ฅผ ํ์ธํด Response๋ฅผ ์ด๋ป๊ฒ ๋ฐ์ ์ ์๋์ง ํ์ธํฉ๋๋ค!
- API ๋ช ์ธ์ ํ์ธํ๊ธฐ
Postman์ ํตํด ํ์ธํด๋ณด๋, ํธ์ ๋ฆฌ์คํธ๊ฐ JSON ํํ๋ก ์ ์ ๋ฌ๋๋ ๊ฒ์ ํ์ธ ๊ฐ๋ฅํฉ๋๋ค!
1. API ๋ช ์ธ์์ ๋ง๊ฒ, response๋ฅผ ๋ฐ์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค.
[Model/Twitt.swift]
import Foundation
struct TwittResponse : Codable {
let isLike : Bool
let isRetwit : Bool
let twitId : String
let content : String
let likeCount : Int
let writer : YJWriter
enum CodingKeys : String, CodingKey {
case twitId = "_id"
case content
case writer
case likeCount
case isLike
case isRetwit
}
}
2. Router ๊ฐ์ฒด์์ ๊ฒฝ๋ก์ HTTP Method ์ค์ ์ ํด์ค๋๋ค.
import Foundation
import Alamofire
import UIKit
enum YJUserRouter {
case getUser
case getList
case postLike(postId : String)
}
extension Router : BaseRouter {
var method: HTTPMethod {
switch self{
case .getUser, .getList:
return .get
case .postLike:
return .post
}
}
var parameters: RequestParams {
switch self {
case .getList, .getUser, .postLike:
return .requestPlain
}
}
var header: HeaderType {
return .auth
}
var path : String {
switch self {
case .getUser:
return "/user"
case .getList:
return "/twit"
case .postLike(let postId):
return "/like/\(postId)"
}
}
}
3. Serviceํ์ผ์ ๋ง๋ค์ด์ค๋๋ค.
์๋น์ค ํ์ผ์์์ , API ๋ช ์ธ์์ ๋ง๊ฒ ๋ด์ฉ๋ค์ ์ค๋นํฉ๋๋ค. ์๋ฒ์ request๋ฅผ ๋ณด๋ด๊ณ , response๋ฅผ ๋ฐ์ต๋๋ค.
import UIKit
import Alamofire
class TwittService : BaseService {
static let shared = TwittService()
private override init() { }
func getList(completion : @escaping (NetworkResult<Any>) -> (Void)) {
AFmanager.request(Router.getList)
.validate(statusCode: 200...500)
.responseData { response in
switch response.result {
case .success:
guard let statusCode = response.response?.statusCode
else {return}
guard let data = response.data else {return}
let networkResult = self.judgeStatus(by: statusCode, data,[TwittResponse].self)
completion(networkResult)
case .failure(let err):
print(err.localizedDescription)
}
}
}
}
request์ ๋งค๊ฐ๋ณ์๋ก Router.getList๊ฐ ๋ค์ด๊ฐ์์ต๋๋ค.
Router์์๋ url, ํต์ ํ API์ฃผ์, HTTP Method, ์์ฒญ๋ฐฉ์, ์ธ์ฝ๋ฉ ๋ฐฉ์, ์์ฒญ ํค๋์ ์ ๋ณด๊ฐ ๋ค์ด๊ฐ๊ฒ ๋ฉ๋๋ค.
[BaseService.swift]
func judgeStatus<T: Codable>(by statusCode: Int, _ data: Data, _ type: T.Type) -> NetworkResult<Any> {
let decoder = JSONDecoder()
guard let decodedData = try? decoder.decode(GeneralResponse<T>.self, from: data)
else { return .pathErr }
print(decodedData)
switch statusCode {
case 200:
return .success(decodedData.data ?? "None-Data")
case 201..<300:
return .success(decodedData.data as Any)
case 400..<500:
return .requestErr(decodedData.status)
case 500:
return .serverErr
default:
return .networkFail
}
}
networkResult๋ ์์ ๋ง๋ค์ด์ค [TwittResponse] ๋ฐฐ์ด์ ํํ๋ก ๋ฐ์ดํฐ๋ฅผ decodeํด์ค๋๋ค.
๋ฐฐ์ด์์ indexPath.row์ cell์ ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฐ๊ฒฐ์์ผ์ค๋๋ค.
extension YujinStoryboard2ViewController : UITableViewDataSource{
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if twittDataList[indexPath.row].isRetwit == false {
guard let cell = tableView.dequeueReusableCell(withIdentifier: TwitterMyTwittTableViewCell.identifier) as? TwitterMyTwittTableViewCell else {return UITableViewCell() }
cell.setData(twittDataList[indexPath.row])
cell.postId = twittDataList[indexPath.row].twitId
return cell
} else {
guard let cell = tableView.dequeueReusableCell(withIdentifier: TwitterRetwittTableViewCell.identifier) as? TwitterRetwittTableViewCell else {return UITableViewCell() }
cell.setData(twittDataList[indexPath.row])
cell.postId = twittDataList[indexPath.row].twitId
return cell
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return twittDataList.count
}
}
4. ViewController ์์ฑํ๊ธฐ
[ViewController.swift]
( 1 ) ViewController์์ ํธ์Response๋ฅผ ๋ด์ ์ ์๋ ๋ฐฐ์ด์ ๋ง๋ญ๋๋ค.
โญ๏ธ ( 2 ) ํ ์ด๋ธ ๋ทฐ๋ ํ๋ฉด์ด ๋ค์ appearํ ๋๋ง๋ค, ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ผํฉ๋๋ค! ์ฒ์ ํ๋ฉด์ด ์์ฑ๋ ๋, ๋ฐ์ดํฐ๋ฅผ ํธ์ถํ๊ฒ ๋๋ฉด, ํ ์ด๋ธ ๋ทฐ์ ๋ฐ์ดํฐ๊ฐ ๋ฐ๋์ง ์์ต๋๋ค.
๋ฐ๋ผ์ viewWillAppear๋ถ๋ถ์ ํธ์๋ฆฌ์คํธ ์์ฒญ์ ๋ณด๋ด๋ ์๋น์ค ๋ฉ์๋๋ฅผ ์์ฑํด์ฃผ์ด์ผํฉ๋๋ค!
Service๊ฐ์ฒด์์ ๋ง๋ค์ด๋์ ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ ธ์ค๋ response์ result์ success๊ฐ ๋์์ค๋ฉด, ๋ฐฐ์ด๊ฐ์ฒด์ response๋ก ๋์์จ ๋ฐ์ดํฐ๋ฅผ ์ง์ด๋ฃ์ด ์ค๋๋ค.
( 3 ) ํ ์ด๋ธ ๋ทฐ์ ์ ์ ๋ฑ๋กํด์ค๋๋ค~ ํด๋น ์ ์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด์ค๋๋ค.
indexPath.row๋ก ๋ฐฐ์ด์index๋ฅผ ๋ฃ์ด์ค๋๋ค!
( 4 ) reloadData() ํธ์ถ
reloadData()๋ ํ ์ด๋ธ๋ทฐ์ ์ธ์คํด์ค ๋ฉ์๋์ ๋๋ค. ํ ์ด๋ธ๋ทฐ ์ธ์คํด์ค์์ ์ ๊ทผํด์ ์ฌ์ฉํฉ๋๋ค.
reloadData() ๋ฉ์๋๋ ํ ์ด๋ธ ๋ทฐ์ ํ์ฌ ๋ณด์ด๋ ์ ์ฒด ์ด(row), ์น์ (section) ์ ๋ฐ์ดํธ๋ฅผ ํ ๋ ์ฌ์ฉํฉ๋๋ค.
reloadData() ๋ฉ์๋๋ ํน์ ์ด, ์น์ ์ ๋ถ๋ถ์ ์ ๋ฐ์ดํธ๊ฐ ์๋, ํ ์ด๋ธ ๋ทฐ์ ๋ณด์ด๋ ์์ญ ์ ์ฒด๋ฅผ ์ ๋ฐ์ดํธ ํด์ค ๋ ํจ์จ์ ์ ๋๋ค.
// MARK: - TableView
// (1)
var twittDataList : [TwittResponse] = []
// (2)
override func viewWillAppear(_ animated: Bool) {
getTwittList()
}
// (3)
func getTwittList() {
TwittService.shared.getList {
result in
switch result {
case .success(let data):
guard let twittList = data as? [TwittResponse] else {return}
self.twittDataList = twittList
// (4)
self.mainTableView.reloadData()
case .requestErr:
print("requestErr")
case .pathErr:
print("pathErr")
case .serverErr:
print("serverErr")
case .networkFail:
print("networkFail")
}
}
}