[README] FineDust


FineDust

  • アプリケーション
  • では、スモッグとスモッグの情報をリアルタイムで表示できます.
  • github
  • 機能


    ⌝現在位置スモッグ、超スモッグ情報検索

  • 現在位置のスモッグと超スモッグを確認する情報

  • 領域の追加

  • 知りたい領域を検索し、
  • を追加します.

    現在位置の現在位置のスモッグ、オーバースモッグ情報を表示するには、瓅部材を使用します。

  • コンポーネントを使用して、現在の位置のスモッグとスモッグの情報
  • を表示します.

    デザイン


    構成ViewController



    [コントローラを表示](View Controller)の役割


    FineDustViewController

  • この地域のスモッグと超スモッグの情報を表示します.
  • LocationListViewController

  • が増加した地域のスモッグと超スモッグの情報を示した.
  • SearchLocationViewController

  • 地域を検索できます.
  • 「モデルの表示」の役割


    FineDustViewModel

  • はスモッグと超スモッグの情報をもたらします.
  • スモッグ関連データを定義します.
  • FineDustListViewModel

  • 地域のスモッグと超スモッグの情報を追加しました.
  • に追加された地域のスモッグ関連データを定義します.
  • CurrentLocationViewModel

  • この地域の名前を呼ぶ.
  • インプリメンテーション


    ✅ Alamofire


    https://www.zehye.kr/ios/2020/04/01/12iOS_alamofire/
  • SWIFTベースHTTPネットワークライブラリ
  • URLSessionに基づき、URLSessionよりも簡潔で可読性が高い
    func request(_ convertible: URLConvertible, // URL
                 method: HTTPMethod = .get,
                 parameters: Parameters? = nil,
                 encoding: ParameterEncoding = URLEncoding.default,
                 headers: HTTPHeaders? = nil,
                 interceptor: RequestInterceptor? = nil,
                 requestModifier: RequestModifier? = nil) -> DataRequest

    URLセッションの使用

    var request = URLRequest(url: URL(string: url)!)
    request.httpMethod = "GET"
    URLSession.shared.dataTask(with: request) { (data, response, error) in
      if let err = error { // 에러 체크
        print(" ---> error : \(APIError.stationAPIError)")
        onComplete(.failure(err))
      }
      guard let data = data else { // data nil 체크
        onComplete(.failure(APIError.stationAPIError))
        return
      }
      let json = JSON(data)
      guard let station: String = json["response"]["body"]["items"][0]["stationName"].string else {
        onComplete(.failure(APIError.stationAPIError))
        return
      }
      onComplete(.success(station))
    }.resume()

    Alamforeの使用

    AF.request(url, method: .get, encoding: URLEncoding.default)
      .responseJSON{ (response) in
        switch response.result{ // result를 통해 성공 여부를 구분할 수 있음
        case .success(let data):
          let json = JSON(data)
          guard let station: String = json["response"]["body"]["items"][0]["stationName"].string else {
            onComplete(.failure(APIError.stationAPIError))
            return
          }
          onComplete(.success(station))
        case .failure(let error):
          print(" ---> error : \(APIError.stationAPIError)")
          onComplete(.failure(error))
        }
      }

    ✅ SwiftyJSON

  • Alamofireと互換性があり、JSONパーティションを自動的に行うライブラリ.
  • JSONSerializationの使用

    // 받아온 Json 파일
    // {"response":
    //	{"body":
    //		{"items":[
    
    // 받아온 json을 [String: Any]로 변환하겠다.
    guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any] else{
        return
    }
    
    responses = json["response"] as! [String : Any] 
    bodys = responses["body"] as! [String : Any]
    items = bodys["items"] as! [[String : Any]]
    let item: [String : Any] = items[0]
    guard let station: String = item["stationName"] as? String else {
      onComplete(.failure(APIError.stationAPIError))
      return
    }

    SwityJSON使用

    guard let station: String = json["response"]["body"]["items"][0]["stationName"].string else {
      onComplete(.failure(APIError.stationAPIError))
      return
    }

    位置自動検索完了


    ここに配置

    ✅ WidgetKit


    ここに配置

    ✅ RxSwift


    RxSWIFTとは?

  • SWIFTにReactiveXを適用することで、非同期プログラミングのライブラリを直感的に作成できます.
  • Subject

  • Subjectは傍観者であるため、1つ以上の観測可能なものを購読することができ、同時に1つのプロジェクトを通じて新しいプロジェクトを再配置、観察、排除することもできる.
  • で観測できるのはUnicast、SubcetはMulticast
  • Relay

  • Subjectはエラーが発生するとすぐにブレークしますが、Relayはエラーが発生してもブレークしません.
  • onNext()、onError()はaccept()として表すべきである.
  • ✅RxSWIFTを用いて実現したマイクロダスト共通データAPI非同期処理


    1.Alamofireを使用してスモッグ共通データAPIをロードします。

    static func fetchFineDust(stationName: String, onComplete: @escaping (Result<FineDustAPIData, Error>) -> Void){
      let url = fineDustURL(stationName: stationName)
      
      AF.request(url, method: .get, encoding: URLEncoding.default)
        .responseJSON{ (response) in
          switch response.result{
          case let .success(data):
            let json = JSON(data)
            guard let fineDustValue: String = json["response"]["body"]["items"][0]["pm10Value"].string else {
              print("finedust Error")
              onComplete(.failure(APIError.finedustAPIError))
              return
            }
            guard let ultraFineDustValue: String = json["response"]["body"]["items"][0]["pm25Value"].string else {
              print("ultrafinedust Error")
              onComplete(.failure(APIError.finedustAPIError))
              return
            }
            guard let dateTime: String = json["response"]["body"]["items"][0]["dataTime"].string else {
              print("dateTime Error")
              onComplete(.failure(APIError.finedustAPIError))
              return
            }
            let response = fineDustAPIData(dateTime: dateTime, fineDustValue: fineDustValue, ultraFineDustValue: ultraFineDustValue, stationName: stationName)
            onComplete(.success(response))
          case let .failure(error):
            onComplete(.failure(error))
          }
        }
    }

    2.Observableを生成します。

    static func loadFineDust(stationName: String) -> Observable<FineDustAPIData>{
      return Observable.create{ emitter in
        self.fetchFineDust(stationName: stationName){ result in
          switch result {
          case let .success(finedust):
            emitter.onNext(finedust)
            emitter.onCompleted()
          case let .failure(error):
            emitter.onError(error)
          }
        }
        return Disposables.create()
      }
    }

    3.PublishRelayを作成した後、Observerableを購読して結果値を中継に渡します。

  • エラーの発生による割り込みを防止するため、PublishRelayが生成される.また,以前のデータは不要であるため,BehaviorRelayではなくPublishRelayを用いた.
  • StrogReference Cyclesを阻止するために弱selfを用いた.
  • 現在位置のスモッグデータを取得するには、現在位置の緯度を取得する必要があります->緯度に基づいてTM座標値を取得する->TM値、現在位置に近い測定局を探す->この測定局のスモッグ値を取得するには4段階が必要なので、Observableに戻るFlatMapを使用して処理します.
  • lazy var observable = PublishRelay<FineDustAPIData>()
    
    func loadFineDust(latitude: Double, longtitude: Double, mode: FineDustVCMode){
      _ = APIService.loadTM(latitude: latitude, longtitude: longtitude)
        .flatMap{ tm in APIService.loadStation(tmX: tm.tmX, tmY: tm.tmY)}
        .flatMap{ station in APIService.loadFineDust(stationName: station)}
        .take(1)
        .subscribe(onNext:{ [weak self] response in
          self?.observable.accept(response)
        })
    }

    4.FineDustViewControllerで配布Relayを購読し、スモッグデータを受信して画面に表示する。

  • ビューを装飾する必要があるので、observe(on:MainScheduler.instance)でMainThreadに変更します.
  • fineDustViewModel.observable
      .observe(on: MainScheduler.instance)
      .subscribe(onNext:{ [weak self] in
        self?.configureView($0)
        self?.setProgressView($0)
      })
      .disposed(by: disposeBag)