졸업 프로젝트를 진행하면서 사용자의 위도, 경도를 서버에 넘겨주고 서버에서 보내는 온도, 날씨 응답을 받아와야 하는 일이 생겼다.
서버와 통신할 때 Alamofire을 많이 이용한다 하여 나도 이 라이브러리를 사용하기로 했다.
Alamofire
을 이용하기 위해서는 먼저 프로젝트에 라이브러리를 추가해 주어야 하는데, 나는 해당 블로그의 글을 참고하였다. 검색하면 수많은 글들이 나오니 참고하면서 하면 된다.
struct APIConstants {
static let baseURL = "http://54.180.220.34:8080"
// 회원가입 url
static let userSignUpURL = baseURL + "/users/new-user"
// 로그인 url
static let userSignInURL = baseURL + "/users/login"
// 장르 저장 url
static let genreURL = baseURL + "/create/genre"
// 날씨 불러오기 url
static let weatherURL = baseURL + "/read/weather"
}
통신 URL 작성하기
enum NetworkResult<T> {
case success(T)
case requestErr(T)
case pathErr
case serverErr
case networkFail
}
서버 통신에 대한 결과를 담는 enum 만들기
struct GenericResponse<T: Codable>: Codable {
var message: String
var data: T?
enum CodingKeys: String, CodingKey {
// json은 key, data 값을 갖고 있는데 json의 key값을 swift 타입으로 디코딩할 때 이름이 같아야 해서
// CodingKeys를 통해 data 변수를 key랑 struct를 이어주는 역할
case message = "message"
case data = "data"
}
init(from decoder: Decoder) throws {
// 데이터로 들어오는 값이 없을 수도 있을 수도 있기 때문에 먼저 처리
// 데이터가 없을 때 nil로 처리
let values = try decoder.container(keyedBy: CodingKeys.self)
message = (try? values.decode(String.self, forKey: .message)) ?? ""
data = (try? values.decode(T.self, forKey: .data)) ?? nil
}
}
struct WeatherData: Codable {
let temp: Int
let icon, main: String
}
import Foundation
import Alamofire
struct WeatherService {
static let shared = WeatherService()
func getWeather(lat: Double, lon: Double, completion: @escaping (NetworkResult<Any>) -> (Void)) {
let url = APIConstants.weatherURL // 날씨 불러오기 url
let header: HTTPHeaders = [ "Content-Type" : "application/json" ]
let params: Parameters = [
"lat": lat,
"lon": lon
]
let dataRequest = AF.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: header)
dataRequest.responseData { (response) in
switch response.result {
case .success:
guard let statusCode = response.response?.statusCode else { return }
guard let data = response.value else { return }
print(data)
completion(judgeWeatherData(status: statusCode, data: data))
case .failure(let err):
print(err)
completion(.networkFail)
}
}
}
// statusCode와 decode 결과에 따라 NetworkResult 반환
private func judgeWeatherData(status: Int, data: Data) -> NetworkResult<Any> {
// 통신을 통해 전달받은 데이터를 decode
let decoder = JSONDecoder()
guard let decodedData = try? decoder.decode(
WeatherData.self, from: data) else {
return .pathErr
}
// statusCode를 통해 통신 결과를 알 수 있음
switch status {
case 200:
print(decodedData)
return .success(decodedData)
case 400..<500:
return .requestErr
case 500:
return .serverErr
default:
return .networkFail
}
}
}
WeatherService.shared.getWeather(lat: LocationService.shared.latitude ?? 0, lon: LocationService.shared.longitude ?? 0) { response in
switch response {
case .success(let data):
let formatter = DateFormatter()
formatter.dateFormat = "yyyy년 MM월 dd일 (E)"
let current_date_string = formatter.string(from: Date())
if let data = data as? WeatherData {
self.dateLabel.text = "\(current_date_string) \(data.temp)º \(data.main)"
}
case .pathErr:
print("결과 :: Path Err")
case .requestErr:
print("결과 :: Request Err")
case .serverErr:
print("결과 :: Server Err")
case .networkFail:
print("결과 :: Network Fail")
}
}
반환값인 data를 print 해 보면 아래와 같이 출력되고, 만들어 둔 label에도 잘 적용되는 모습을 확인할 수 있었다.
WeatherData(temp: 13, icon: "04n", main: "Clouds")