[날씨 기본앱] #5 날씨 받아오기 - Moya, Singleton

kimdocs...📄·2021년 9월 7일
0

CloneCoding💛

목록 보기
5/6
post-thumbnail

날씨 api는 open api인 openweathermap의 one-call-api를 이용하여 필요 정보를 한번에 받아왔습니다.

Moya가 모야?

Moya 공식문서를 바탕으로 글을 작성하였습니다.
Moya GitHub 바로가기

URLSession과 Alamofire를 한번 더 감싼 통신 라이브러리입니다!

Alamofire를 사용할땐 Network Layer를 만들어 사용하곤 하는데, Moya를 사용하면 한번 더 감싸지는 것이 없기 때문에 Network Layer를 도와줘서 Alamofire를 캡슐화 하는 효과가 있어요!

1️⃣ APIService

URL들을 담고있는 구조체를 만들어 url들을 관리해줍니다.

struct APIService {
    static let baseURL = "https://api.openweathermap.org/data/2.5"
}

2️⃣ WeatherAPI

TargetType 알아보기

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 들을 붙여줍니다.

WeatherAPI 작성해보기

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
    }
}

3️⃣ WeatherService

실질적으로 통신을 하고 처리를 해주는 부분입니다.
싱글톤패턴을 이용하여 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)
            }
        }
    }
}

📂 GitHub
https://github.com/ezidayzi/Moother_KimYoonSeo

profile
👩‍🌾 GitHub: ezidayzi / 📂 Contact: ezidayzi@gmail.com

0개의 댓글