Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Assignment] 4주차 과제 #16

Merged
merged 14 commits into from
Nov 22, 2023
Merged

[Assignment] 4주차 과제 #16

merged 14 commits into from
Nov 22, 2023

Conversation

HELLOHIDI
Copy link
Collaborator

🔥 Pull requests

🌴 작업한 브랜치

✅ 작업한 내용

  • 4주차 과제

❗️PR Point

  • 서버통신 과정에서 의존성 주입을 신경쓰면서 개발했습니다.

  • repository 패턴 적용
    데이터를 받아오고 가져오는 과정을 비즈니스 로직과 분리하기 위해서 Repository 패턴을 사용했습니다.
    => repository 패턴의 가장 큰 역할 외부 서비스에 들어오는 값을 내부 입맛에 맡게 잘 가공해주는 역할입니다.
    지금은 서버통신을 통해서만 데이터를 받아오지만, Realm, UserDefault 등 다양한 서비스에서 가져오는 데이터는 그 형식도 각기 다를것이다. 그런 데이터를 UseCase에서 통일된 데이터로 받아올 수 있게 repository 패턴을 적용했습니다.

public class DefaultWeatherRepository: WeatherRepository {
    
    typealias Error = URLSessionNetworkServiceError
    public let urlSessionService: URLSessionNetworkService
    private let disposeBag = DisposeBag()
    
    public init(urlSessionService: URLSessionNetworkService) {
        self.urlSessionService = urlSessionService
    }
    
    public func getCityWeatherData(city: String) -> Observable<CurrentWeatherModel> {
        return urlSessionService.request(target: WeatherAPI.getCurrentWeatherData(city: city))
            .map({ result in
            switch result {
            case .success(let data):
                guard let dto = self.decode(data: data, to: CurrentWeatherEntity.self) else { throw Error.responseDecodingError }
                return dto.toDomain()
            case .failure(let error):
                throw error
            }
        })
    }
    
    public func getHourlyWeatherData(city: String) -> Observable<[HourlyWeatherModel]> {
        return urlSessionService.request(target: WeatherAPI.getHourlyWeatherData(city: city)).map({ result in
            switch result {
            case .success(let data):
                guard let dto = self.decode(data: data, to: HourlyWeatherEntity.self) else { throw Error.responseDecodingError }
                return dto.toDomain()
            case .failure(let error):
                throw error
            }
        })
    }
  • WeatherNetworkService로 분리
    서버통신을 담당하는 부분으로 분리했습니다! + 커스텀했습니다!

  • Moya처럼 URLRequest하는 부분을 커스텀했습니다. 확실히 서버통신하는 부분과 requset 객체를 생성하는 부분을 분리할 수 있어서 좋았습니다.

import Foundation

public enum HTTPMethod {
    static let get = "GET"
    static let post = "POST"
    static let patch = "PATCH"
    static let delete = "DELETE"
}

public protocol URLSessionTargetType {
    var baseURL: String { get }
    var path: String { get }
    var method: String { get }
    var headers: [String: String]? { get }
    var body: Data? { get }
}

extension URLRequest {
    init(target: URLSessionTargetType) {
        let url = URL(string: target.baseURL + target.path)
        self.init(url: url!)
        httpMethod = target.method
        
        target.headers?.forEach { (key, value) in
            addValue(value, forHTTPHeaderField: key)
        }
        
        if let body = target.body {
            httpBody = body
        }
    }

//
//  WeatherAPI.swift
//  Networks
//
//  Created by 류희재 on 2023/11/17.
//  Copyright © 2023 hellohidi. All rights reserved.
//

import Foundation
import Core

public enum WeatherAPI {
    case getCurrentWeatherData(city: String)
    case getHourlyWeatherData(city: String)
}

extension WeatherAPI: BaseAPI {    
    public static var apiType: APIType = .weather
    
    public var path: String {
        switch self {
        case .getCurrentWeatherData(let city):
            return "/weather?q=\(city)&appid=\(Config.apiKey)"
        case .getHourlyWeatherData(let city):
            return "/forecast?q=\(city)&appid=\(Config.apiKey)"
        }
    }
    
    public var method: String {
        switch self {
        case .getCurrentWeatherData:
            return HTTPMethod.get
        case .getHourlyWeatherData:
            return HTTPMethod.get
        }
    }
    
    public var body: Data? {
        switch self {
        case .getCurrentWeatherData:
            return nil
        case .getHourlyWeatherData:
            return nil
        }
    }
}

  1. ViewController -> ViewModel를 통해서 UseCase에서 서버통신을 해야 되는것을 인지
  2. UseCase에서 repository 메소드 호출
public func getCurrentWeatherData() {
        let currentCityWeatherList = City.cityList.map { city in
            return repository.getCityWeatherData(city: city)
        }
  1. 서버통신을 해준다
  2. 서버통신으로 받아온 값을 toDomain()을 통해서 repository Model에 맞는 값으로 바꿨다.
import Foundation

import Core
import Networks
import Domain

extension CurrentWeatherEntity {
    public func toDomain() -> CurrentWeatherModel {
        return CurrentWeatherModel(
            time: WeatherUtil.makeTimeZoneToTime(timeZone: timezone),
            place: name,
            weather: weather[0].main,
            temparature: main.temp,
            maxTemparature: main.tempMax,
            minTemparature: main.tempMin
        )
    }
}

📸 스크린샷

Simulator Screen Recording - iPhone 14 Pro - 2023-11-17 at 21 59 43

✏️ 배운점 & 참고 레퍼런스

closed #15

@bangMinjI98
Copy link

코드가 스윗하네요
역시 젠틀스윗오비 류희재
이번주도 질문 받아줘서 고맙습니다>_<

@HELLOHIDI HELLOHIDI merged commit fde14b2 into main Nov 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Assignment] 4주차 과제
2 participants