Skip to content

Instantly share code, notes, and snippets.

@abdulowork
Last active March 15, 2017 13:18
Show Gist options
  • Save abdulowork/fc7b339eb0c11755fef4632e10325132 to your computer and use it in GitHub Desktop.
Save abdulowork/fc7b339eb0c11755fef4632e10325132 to your computer and use it in GitHub Desktop.

Вот как выглядит SOA и MVPVM в моем последнем приложении (это экран который отображает магазины на google картах):

StoresTarget (Транспортный слой построен на Moya):

extension TargetType {
  
  var baseURL: URL { return URL(string: "https://my-api-base-url")! }
  
  var parameterEncoding: ParameterEncoding { return JSONEncoding() }
  
  var task: Task { return .request }
  
}

enum StoresTarget {
  case getAllStores
}

extension StoresTarget: TargetType {
  
  var path: String { return "/mySecretPath" }
  
  var method: Moya.Method { return .get }
  
  var parameters: [String : Any]? { return nil }
  
}

StoresService:

class BasicService<Target: TargetType> {
  
  let localAuthenticationService = LocalAuthenticationService()
  var provider: RxMoyaProvider<Target> {
    return
      RxMoyaProvider<Target>(
        endpointClosure: { [unowned self] target in
          let defaultEndpoint = MoyaProvider.defaultEndpointMapping(for: target)
          if let sessionIDCookie = self.localAuthenticationService.sessionIDCookie {
            return defaultEndpoint.adding(httpHeaderFields: [
              "Cookie" : sessionIDCookie
              ])
          return defaultEndpoint
        }
    )
  }
  
  let disposeBag = DisposeBag()
  
  private typealias Seconds = Double
  let defaultRequestTimeout = RxTimeInterval(Seconds(20))
  
}

class StoresService: BasicService<StoresTarget> {
  
  let dao = try! ArrayStoresResponseDAO()
  
  func getAllStores(_ strategy: RequestStrategy) -> Observable<StoresResponse> {
    switch strategy {
    case .api:
      return getAllStoresFromDAO().concat(getAllStoresFromAPI())
    case .cached:
      return getAllStoresFromDAO()
    }
  }
  
  private func getAllStoresFromAPI() -> Observable<StoresResponse> {
    return
      provider
        .request(.getAllStores)
        .observeOn(utilityScheduler)
        .timeout(defaultRequestTimeout, scheduler: utilityScheduler)
        .mapResponse(StoresResponse.self)
        //Сохранение в json файл
        .do(
          onNext: { [unowned self] response in
            try self.dao.persist(entity: response)
        })
        .observeOn(MainScheduler.instance)
  }
  
  private func getAllStoresFromDAO() -> Observable<StoresResponse> {
    return
      Observable
        .just()
        .observeOn(utilityScheduler)
        .map { [unowned self] in
          guard let response = self.dao.getAll().last else { throw NSError(domain: "DAO is empty?", code: 404, userInfo: nil) }
          return response
        }
        .observeOn(MainScheduler.instance)
  }
}

MapPresentationModel:

class MapPresentationModel: BasicPresentationModel {
  
  private let service = StoresService()
  
  func getAllStores() -> Observable<[MapStoreViewModel]> {
    networkIsActive.on(.next(true))
    return
      service
        .getAllStores(.api)
        .observeOn(utilityScheduler)
        .map { response in
          response.storeList.map{ MapStoreViewModel($0) }
        }
        .observeOn(MainScheduler.instance)
        .do(
          onDisposed: { [unowned self] in self.networkIsActive.on(.next(false))}
    )
  }
}

MapViewController:

class MapViewController: UIViewController, RouterType {
  
  let disposeBag = DisposeBag()
  
  let presentationModel = MapPresentationModel()
  
  var mapView: GMSMapView!
  
  var router: AnyRouter! {
    return MapRouter(viewController: self)
  }
  
  var mapStoreViewModels: [MapStoreViewModel] = []
  
  override func viewDidLoad() {
    super.viewDidLoad()
    configureHUD()
    configureMap()
  }
  
  func configureMap() {
    let moscowCoordinates = CLLocationCoordinate2D(latitude: 55.7558, longitude: 37.6173)
    mapView = GMSMapView.map(
      withFrame: view.bounds,
      camera: GMSCameraPosition.camera(withTarget: moscowCoordinates, zoom: 12)
    )
    mapView.delegate = self
    mapView.isMyLocationEnabled = true
    mapView.settings.myLocationButton = true
    view.addSubview(mapView)
    
    presentationModel
      .getAllStores()
      .subscribe(
        onNext: { [unowned self] viewModels in
          for viewModel in viewModels {
            let marker = StoreMarker(position: store.coordinate.toCLLocation().coordinate)
            marker.title = viewModel.title
            marker.store = viewModel.store
            marker.map = self.mapView
          }
          self.mapStoreViewModels = viewModels
      })
      .addDisposableTo(disposeBag)
  }
  
  func configureHUD() {
    PKHUD.sharedHUD.contentView = PKHUDSystemActivityIndicatorView()
    
    presentationModel.networkIsActive
      .subscribe(onNext: { active in
        switch active {
        case true:
          PKHUD.sharedHUD.show()
        case false:
          PKHUD.sharedHUD.hide()
        }
      }).addDisposableTo(disposeBag)
  }
  
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if
      let destination = segue.destination as? RouterType,
      let mapStoreViewModel = sender as? MapStoreViewModel {
      router
        .pass(mapStoreViewModel.store, to: destination.router)
    }
  }
  
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment