- 블로그에 정리했던 글
URLSession : https://velog.io/@heyksw/iOS-URLSession
Alamofire : https://velog.io/@heyksw/iOS-Alamofire
URLSession 토이 플젝 : https://velog.io/@heyksw/아이폰-앱-2-현재-날씨는
- URLSession
- iOS 앱에서 서버와 통신하기 위해 Apple 이 제공하는 네트워킹 API
- 로우 레벨의 코드를 작성할 수 있고, 가장 기본적인 방법
- Alamofire 의 기반이 되는 API
- 특정 URL을 사용해서 데이터를 다운로드, 업로드하기 위한 API
- Alamofire
- 네트워킹 오픈소스 라이브러리
- URLSession 에 기반한 라이브러리
- 코드의 간소화 및 가독성 측면에서 보다 성능이 개선되었다
- 유지보수와 유닛 테스트가 힘들다는 단점이 있다
- Moya
- 네트워킹 오픈소스 프레임워크
- 추상화 된 정도 : URLSession < Alamofire < Moya
- Network Layer 를 템플릿화 해서 재사용성을 높인다
- 열거형 enum 을 사용해서 안전하고 정돈된 방식으로 캡슐화한다
- 추상화란
- 객체 지향 프로그래밍의 특징(추상화, 캡슐화, 은닉화, 상속성, 다형성) 중 하나
- 객체들의 공통적인 부분을 뽑아내서 따로 구현해놓은 것
- 공통적인 부분들만 추려놓았기 때문에 구체적인 정보를 담아두지 못했고, 추상적인 정보가 모였기 때문에 '추상화'라고 부른다
- 날씨 데이터를 가져와 사용하는 코드를 3가지 방법으로 작성해보려 한다.
1. URLSession
2. Alamofire
3. Moya
날씨 데이터 API : https://openweathermap.org/api
-> 도시 이름을 입력하면, 현재 그 도시의 날씨를 출력해주는 앱
-> PostMan 으로 확인한 json 데이터의 모습
- URLSession LifeCycle
- ViewController.swift
// API에서 현재 날씨 가져오기 func getCurrentWeatherByURLSession(cityName: String) { // 날씨 데이서 API url 연결 guard let url = URL(string: url_seoul) else { return } // 1. Session Configuration 설정 + 생성 let session = URLSession(configuration: .default) // 2,3. 사용할 Task 결정하고 Complete Handler 작성 session.dataTask(with: url) { [weak self] // response : http 헤더 및 상태 코드 메타 데이터 // error : 요청 실패 에러 객체. 성공하면 nil. data, response, error in // http status 가 200번대면 성공 let successRange = (200..<300) guard let data = data, error == nil else { return } let decoder = JSONDecoder() // response 를 HTTPURLResponse 형태로 다운 캐스팅 if let response = response as? HTTPURLResponse, successRange.contains(response.statusCode) { guard let weatherInformation = try? decoder.decode(WeatherInformation.self, from: data) else { return } // UI 작업은 메인 쓰레드에서 작업 DispatchQueue.main.async { self?.weatherStackView.isHidden = false self?.configureView(weatherInformation: weatherInformation) } } else { guard let errorMessage = try? decoder.decode(ErrorMessage.self, from: data) else { return } DispatchQueue.main.async { self?.showAlert(message: errorMessage.message) } } }.resume() // 4. 해당 Task 를 실행 }
- ViewController.swift
import Alamofire ... func getCurrentWeahterByAlamofire(cityName: String) { guard let url = URL(string: url_seoul) else { return } AF.request(url, method: .get, parameters: [:]) .responseData(completionHandler: { response in let decoder = JSONDecoder() switch response.result { case let .success(data): do { let weatherInformation = try decoder.decode(WeatherInformation.self, from: data) DispatchQueue.main.async { self.weatherStackView.isHidden = false self.configureView(weatherInformation: weatherInformation) } } catch { debugPrint(error) } case let .failure(error): debugPrint(error) } }) }
- URLSession 에서 해줘야 했던 HTTPURLResponse 타입 캐스팅 과정이 생략 되었다
- 코드 메타 데이터로 성공여부를 파악하지 않아도 된다
- Moya 사용 플로우
- MoyaProvider : API 의 request 를 처리해주는 관리자
- TargetType: MoyaProvider 에서 필요한 규격을 정의하는 프로토콜.
6개의 프로퍼티를 기입해줘야 한다.
- WeatherAPI.swift - 규격 세팅
import Foundation import Moya enum WeatherAPI { case getWeather // case setWeather } // TargetType : MoyaProvider에 필요한 규격을 정의하는데 사용되는 프로토콜 extension WeatherAPI: TargetType { // 서버의 base URL, end point 도메인 var baseURL: URL { return URL(string: "https://api.openweathermap.org/data/2.5/weather?q=seoul&appid=1290787ef5eb584060b11b43e201a9fc")! } // 도메인 뒤에 추가 될 path (/users, /documents, ...) var path: String { switch self { case .getWeather: return "" } } // HTTP 메소드 (GET, POST, ...) var method: Moya.Method { switch self { case .getWeather: return .get } //case .setWeather: // return .post } // 테스트용 Mock Data var sampleData: Data { return Data() } // request에 사용될 파라미터 // .requestPlain : 파라미터 없을 때 // .requestParameters(parameter: ,encoding: ) var task: Task { switch self { case .getWeather: return .requestPlain } } // HTTP header 에 대한 배경지식 필요 var headers: [String : String]? { return ["Content-type": "application/json"] } }
- ViewController.swift
import Moya ... func getCurrentWeatherByMoya(cityName: String) { let moyaProvider = MoyaProvider<WeatherAPI>() moyaProvider.request(.getWeather) { (result) in switch result { case let .success(response): guard let result = try? response.map(WeatherInformation.self) else { return } DispatchQueue.main.async { self.weatherStackView.isHidden = false self.configureView(weatherInformation: result) } case let .failure(error): print(error.localizedDescription) } } }
- url 주소값과 request 들을 외부에서 정의한다.
- 데이터를 사용하는 곳에서 좀 더 request / response 에만 집중한 코드를 작성할 수 있다.
- 한 API 내에서 코드를 재사용하기가 좋다.
- Alamofire : 일일히 request 하고 메서드를 작성
- Moya : TargetType 에서 HTTP 메서드를 여러 개 작성해서 재활용 할 수 있다 (열거형)
안녕하세요! 포스팅 잘 보고있습니다 :)
혹시 Alamofire가 어떤 면에서 유지보수랑 단위테스트가 어려운 것인지 구체적인 이유를 여쭤봐도 될까요? 저도 둘다 써봤는데도 확 다가오지를 않아서 여쭤봅니당