You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

302 lines
11 KiB

//
// ExistingUserKycPresenter.swift
// GME Remit
//
// Created by Shiran on 8/18/20.
//Copyright © 2020 Gobal Money Express Co. Ltd. All rights reserved.
//
import RxSwift
import RxCocoa
import CoreLocation
class ExistingUserKycPresenter {
// MARK: Properties
enum Step: Int {
case innerStep1
case innerStep2
}
var interactor: ExistingUserKycInteractorInput?
var wireframe: ExistingUserKycWireframeInput?
struct Input {
let viewWillAppear: Driver<Bool>
let sendLocation: Driver<CLLocation>
let sourceOfFund: Driver<KeyValue?>
let occupation: Driver<KeyValue?>
let monthlyIncome: Driver<String>
let businessType: Driver<KeyValue?>
let visaStatus: Driver<KeyValue?>
let idType: Driver<KeyValue?>
let purpose: Driver<KeyValue?>
let employerName: Driver<String>
let mobile: Driver<String>
let email: Driver<String>
let address: Driver<String>
let isValidPersonalInfo: Driver<Bool>
let personalInfoSave: Driver<Void>
let editingPersonalInfo: Driver<Void>
let frontIDImage: Driver<String?>
let backIDImage: Driver<String?>
let sideIDImage: Driver<String?>
let isValidPictures: Driver<Bool>
let editingPictureInfo: Driver<Void>
let submit: Driver<Void>
}
struct Output {
let isError: Driver<Error>
let isProgress: Driver<Bool>
let gpsAddress: Driver<String>
let model: Driver<KYCInfoModel>
let idTypes: Driver<[KeyValue]?>
let purposes: Driver<[KeyValue]?>
let busniessType: Driver<[KeyValue]?>
let sourceOfFund: Driver<[KeyValue]?>
let occupation: Driver<[KeyValue]?>
// let monthlyIncome: Driver<[KeyValue]?>
let visaStatus: Driver<[KeyValue]?>
let isSetPersonalInfoModel: Driver<Bool>
let isSetPictureInfoModel: Driver<Bool>
let storePersonalSuccess: Driver<Bool>
let storedSuccess: Driver<String>
}
private let disposeBag = DisposeBag()
private let progressLinker = PublishSubject<Bool>()
private let errorLinker = PublishSubject<Error>()
private let gpsAddress = PublishSubject<String>()
private let model = PublishSubject<KYCInfoModel>()
private var storedPersonalInfo = PublishSubject<PersonalInfoModel?>()
private var storedPictureInformation = PublishSubject<PictureInfoModel?>()
private var storedPersonalSuccess = PublishSubject<String?>()
private let storedSuccess = PublishSubject<String>()
func transform(input: Input) -> Output {
let newPicturesInfo = Driver.combineLatest(
input.isValidPictures,
input.frontIDImage,
input.backIDImage,
input.sideIDImage
).map { (isValid, frontIdImage, backIdImage, sideIdImage) -> PictureInfoModel? in
return isValid ? PictureInfoModel(
idFront: frontIdImage,
idBack: backIdImage,
idSide: sideIdImage,
additionalId: nil,
facePicture: nil,
additionalIdBack: nil
) : nil
}
let newPersonalInfo = Driver.combineLatest(
input.isValidPersonalInfo,
input.idType,
input.visaStatus,
input.occupation,
input.purpose,
Driver.combineLatest(
input.employerName,
input.sourceOfFund,
input.monthlyIncome,
input.mobile,
input.businessType,
input.email,
input.address,
model.map{$0.personalInformation?.nativeCountry}.asDriverOnErrorJustComplete()
).map({ (employerName, sourceOfFund, monthlyIncome, mobile, businessType, email, address, nativeCountry) -> PersonalInfoModel in
return PersonalInfoModel(
email: email,
address: address,
visaStatus: nil,
sourceOfFund: sourceOfFund?.id,
monthlyIncome: monthlyIncome,
mobile: mobile,
idType: nil,
additionalIdType: nil,
businessType: businessType?.id,
nativeCountry: nativeCountry,
employeerName: employerName,
occupation: nil,
purpose: nil,
isBackIDRequired: nil
)
})
).map { (isValid, idType, visaStatus, occupation, purpose, info) -> PersonalInfoModel? in
if isValid{
var personalInfo = info
personalInfo.idType = idType?.id
personalInfo.visaStatus = visaStatus?.id
personalInfo.occupation = occupation?.id
personalInfo.purpose = purpose?.id
return personalInfo
}
return nil
}
let isSetPersonalInformation = Driver
.combineLatest(input.isValidPersonalInfo, storedPersonalInfo.asDriverOnErrorJustComplete())
.map { $0 && $1 != nil }
let isSetPictureInformation = Driver
.combineLatest(input.isValidPictures, storedPictureInformation.asDriverOnErrorJustComplete())
.map {$0 && $1 != nil}
let storePersonalSuccess = Driver
.combineLatest(input.isValidPersonalInfo, storedPersonalSuccess.asDriverOnErrorJustComplete())
.map {$0 && $1 != nil}
input.viewWillAppear.drive(onNext: { [weak self] (value) in
self?.progressLinker.onNext(value)
self?.interactor?.fetchInformation()
}).disposed(by: disposeBag)
input.sendLocation.drive(onNext: { [weak self] in
self?.progressLinker.onNext(true)
let geocoder = CLGeocoder()
let locale = Locale(identifier: "en-US")
geocoder.reverseGeocodeLocation($0, preferredLocale: locale) {(placemarks, error) in
if let error = error {
self?.progressLinker.onNext(false)
self?.errorLinker.onNext(error)
}
if let address: [CLPlacemark] = placemarks {
let name = address.last?.name ?? ""
let locality = address.last?.locality ?? ""
self?.progressLinker.onNext(false)
let mergedAddress = "\(name) \(locality)"//.extract(regex: "[A-Z0-9a-z\\s]")
self?.gpsAddress.onNext(mergedAddress)
}
}
}).disposed(by: disposeBag)
input.editingPersonalInfo
.withLatestFrom(newPersonalInfo) { $1 }
.withLatestFrom(storedPersonalInfo.asDriverOnErrorJustComplete()) {($0, $1)}
.drive(onNext: {[weak self] in
guard let new = $0, let stored = $1 else { return }
if new != stored {
self?.storedPersonalInfo.onNext(nil)
}
})
.disposed(by: disposeBag)
input.editingPictureInfo
.withLatestFrom(newPicturesInfo) { $1 }
.withLatestFrom(storedPictureInformation.asDriverOnErrorJustComplete()) {($0, $1)}
.drive(onNext: {[weak self] in
guard let new = $0, let stored = $1 else { return }
if new != stored {
self?.storedPictureInformation.onNext(nil)
}
})
.disposed(by: disposeBag)
input.personalInfoSave
.withLatestFrom(newPersonalInfo) { $1 }
.drive(onNext: {[weak self] in
let saveModel = KYCSaveInformation(
type: Step.innerStep1.rawValue,
personalInformation: $0,
pictures: nil
)
self?.progressLinker.onNext(true)
self?.interactor?.saveInformation(with: saveModel)
}).disposed(by: disposeBag)
input.submit
.withLatestFrom(newPersonalInfo) { $1 }
.withLatestFrom(newPicturesInfo) { ($0, $1) }
.drive(onNext: {[weak self] in
let saveModel = KYCSaveInformation(
type: Step.innerStep2.rawValue,
personalInformation: $0,
pictures: $1
)
self?.progressLinker.onNext(true)
self?.interactor?.saveInformation(with: saveModel)
}).disposed(by: disposeBag)
return Output(
isError: errorLinker.asDriverOnErrorJustComplete(),
isProgress: progressLinker.asDriverOnErrorJustComplete(),
gpsAddress: gpsAddress.asDriverOnErrorJustComplete(),
model: model.asDriverOnErrorJustComplete(),
idTypes: model.map {$0.idType}.asDriverOnErrorJustComplete(),
purposes: model.map {$0.purpose}.asDriverOnErrorJustComplete(),
busniessType: model.map {$0.businessType}.asDriverOnErrorJustComplete(),
sourceOfFund: model.map {$0.sourceOfFund}.asDriverOnErrorJustComplete(),
occupation: model.map {$0.occupation}.asDriverOnErrorJustComplete(),
// monthlyIncome: model.map {$0.monthlyIncome}.asDriverOnErrorJustComplete(),
visaStatus: model.map {$0.visaStatus}.asDriverOnErrorJustComplete(),
isSetPersonalInfoModel: isSetPersonalInformation,
isSetPictureInfoModel: isSetPictureInformation,
storePersonalSuccess: storePersonalSuccess,
storedSuccess: storedSuccess.asDriverOnErrorJustComplete()
)
}
}
// MARK: ExistingUserKyc interactor output interface
extension ExistingUserKycPresenter: ExistingUserKycInteractorOutput {
func setModel(with model: KYCInfoModel) {
progressLinker.onNext(false)
self.model.onNext(model)
guard let personalInformation = model.personalInformation, !personalInformation.isHasEmptyValue() else {
storedPersonalInfo.onNext(nil)
return
}
storedPersonalInfo.onNext(model.personalInformation)
guard let pictures = model.pictures, !pictures.isHasEmptyValue() else {
storedPictureInformation.onNext(nil)
return
}
storedPictureInformation.onNext(pictures)
}
func setSaveResult(with storedModel: KYCSaveInformation?, message: String) {
progressLinker.onNext(false)
guard let storedModel = storedModel, let type = Step(rawValue: storedModel.type) else { return }
switch type {
case .innerStep1:
storedPersonalInfo.onNext(storedModel.personalInformation)
storedPersonalSuccess.onNext(message)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.storedPersonalSuccess.onNext(nil)
}
case .innerStep2:
storedPictureInformation.onNext(storedModel.pictures)
storedSuccess.onNext(message)
}
}
func setError(with error: Error) {
progressLinker.onNext(false)
errorLinker.onNext(error)
}
}