날씨 api는 open api인 openweathermap의 one-call-api를 이용하여 필요 정보를 한번에 받아왔습니다.
Moya 공식문서를 바탕으로 글을 작성하였습니다.
Moya GitHub 바로가기
URLSession과 Alamofire를 한번 더 감싼 통신 라이브러리입니다!
Alamofire를 사용할땐 Network Layer를 만들어 사용하곤 하는데, Moya를 사용하면 한번 더 감싸지는 것이 없기 때문에 Network Layer를 도와줘서 Alamofire를 캡슐화 하는 효과가 있어요!
URL들을 담고있는 구조체를 만들어 url들을 관리해줍니다.
struct APIService {
static let baseURL = "https://api.openweathermap.org/data/2.5"
}
import Foundation
/// The protocol used to define the specifications necessary for a `MoyaProvider`.
public protocol TargetType {
/// The target's base `URL`.
var baseURL: URL { get }
/// The path to be appended to `baseURL` to form the full `URL`.
var path: String { get }
/// The HTTP method used in the request.
var method: Moya.Method { get }
/// Provides stub data for use in testing.
var sampleData: Data { get }
/// The type of HTTP task to be performed.
var task: Task { get }
/// The type of validation to perform on the request. Default is `.none`.
var validationType: ValidationType { get }
/// The headers to be used in the request.
var headers: [String: String]? { get }
}
public extension TargetType {
/// The type of validation to perform on the request. Default is `.none`.
var validationType: ValidationType {
return .none
}
}
위와 같은 TargetType 을 상속받아 사용합니다!
baseURL
통신할 api들의 baseURL의 주소를 담아줍니다.
path
baseURL을 제외한 full URL을 작성해줍니다.
method
통신에서 사용할 HTTP method를 채택해줍니다.
sampleData
task
HTTP method들이 수행할 일들을 작성해줍니다. (파라미터라든지..모..)
validationType
headers
header 들을 붙여줍니다.
Moya는 enum으로 관리하기 때문에 end point 관리가 용이합니다!
import Foundation
import Moya
enum WeatherAPI {
case getWeathers(lat: Double, lon: Double, exclude: String)
}
extension WeatherAPI: TargetType {
var baseURL: URL {
guard let url = URL(string: APIService.baseURL) else {
fatalError("baseURL 가져오기 실패")
}
return url
}
var path: String {
switch self {
case .getWeathers(_, _, _):
return "/onecall"
}
}
var method: Moya.Method {
switch self {
case .getWeathers:
return .get
}
}
var sampleData: Data {
return Data()
}
var task: Task {
switch self {
case let .getWeathers(lat, lon, exclude):
return .requestParameters(
parameters: [ "lat": lat, "lon": lon, "exclude": exclude, "units": "metric", "appid": "43e744bd747e3acafd7cbe50e304701d", "lang": "kr"], encoding: URLEncoding.queryString)
}
}
var headers: [ String: String]? {
return nil
}
}
실질적으로 통신을 하고 처리를 해주는 부분입니다.
싱글톤패턴을 이용하여 WeatherService를 작성했어요!
싱글톤패턴이란 전역 변수를 사용하지 않고 객체를 하나만 생성 하도록 하며, 생성된 객체를 어디에서든지 참조할 수 있도록 하는 패턴입니다. 참고
싱글톤 패턴은 객체가 프로그램 내부에서 단 1개만 생성됨 을 보장하며 멀티 스레드에서 이 객체를 공유하며 동시에 접근하는 경우에 발생하는 동시성 문제도 해결해주는 디자인 패턴이라고 합니다.
여러 부분에서 WeatherService를 공유하여 쓰기 때문에 싱글톤패턴을 적용하여 작성해보았습니다.
import Foundation
import Moya
class WeatherService {
static let shared = WeatherService()
private lazy var service = MoyaProvider<WeatherAPI>(plugins: [MoyaLoggingPlugin()])
private var getWeather: GenericModel?
private var searchWeather: MainWeatherModel?
public func requestGetWeather(lat: Double,
lon: Double,
location: String,
completion: @escaping (MainWeatherModel?) -> Void) {
service.request(WeatherAPI.getWeathers(lat: lat, lon: lon, exclude: "minutely")) { [weak self] result in
switch result {
case .success(let response):
do {
let response = try JSONDecoder().decode(GenericModel.self, from: response.data)
self?.getWeather = response
completion(self?.convertMainWeatherModel(response: response, location: location))
} catch let err {
debugPrint(err)
}
case .failure(let error):
print(error.localizedDescription)
}
}
}
}