Created
February 7, 2018 11:22
-
-
Save biloshkurskyi-ss/f11d4add568a7735f59e45f4df399304 to your computer and use it in GitHub Desktop.
Code example #1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import RxSwift | |
protocol EditTimeEntryType { | |
var projectIds: Variable<[String]?> { get } | |
var projectsNames: Observable<String?> { get } | |
var isValid: Observable<Bool> { get } | |
var realmService: RealmProjectsServiceProtocol { get } | |
var interval: Variable<DateInterval> { get } | |
var type: EditTimeEntryViewModel.ScreenType { get } | |
var dateFromPicker: Variable<Date> { get } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
import RxSwift | |
import RxDataSources | |
class EditTimeEntryVC: UIViewController, BindableType { | |
// MARK: - Injections | |
let disposeBag = DisposeBag() | |
// MARK: - Outlets | |
@IBOutlet weak var cancelButton: UIBarButtonItem! { | |
didSet { | |
cancelButton.title = Localized(string: Constants.UI.Buttons.Titles.cancel).capitalizedFirst() | |
} | |
} | |
@IBOutlet weak var doneButton: UIBarButtonItem! { | |
didSet { | |
doneButton.title = Localized(string: Constants.UI.Buttons.Titles.done).capitalizedFirst() | |
} | |
} | |
@IBOutlet weak var tableView: UITableView! | |
// MARK: - Instance Properties | |
var viewModel: EditTimeEntryViewModel! | |
var datePickerIndexPath: NSIndexPath? | |
fileprivate let dataSource = RxTableViewSectionedAnimatedDataSource<AddTimeEntryListSection>() | |
// MARK: - View LifeCycle | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
configureDataSource() | |
} | |
// MARK: - BindableType | |
func bindViewModel() { | |
self.title = viewModel.type.title.capitalizedFirst() | |
cancelButton.rx.action = viewModel.onCancel | |
doneButton.rx.action = viewModel.onSave | |
viewModel.projectsNames | |
.skip(1) | |
.subscribe { [unowned self] (event) in | |
self.tableView.beginUpdates() | |
self.tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .automatic) | |
self.tableView.endUpdates() | |
} | |
.disposed(by: disposeBag) | |
viewModel.isValid | |
.bind(to: doneButton.rx.isEnabled ) | |
.disposed(by: disposeBag) | |
viewModel.sectionsItems | |
.bind(to: tableView.rx.items(dataSource: dataSource)) | |
.disposed(by: self.disposeBag) | |
viewModel.indexesForReload.asObservable() | |
.subscribe { [unowned self] (event) in | |
guard let element = event.element, let indexes = element else { return } | |
self.tableView.beginUpdates() | |
self.tableView.reloadRows(at: indexes, with: .automatic) | |
self.tableView.endUpdates() | |
} | |
.disposed(by: disposeBag) | |
tableView.rx | |
.itemSelected | |
.subscribe(onNext: { [unowned self] indexPath in | |
self.viewModel.selectedCell(at: indexPath) | |
if let selectedRowIndexPath = self.tableView.indexPathForSelectedRow { | |
self.tableView.deselectRow(at: selectedRowIndexPath, animated: true) | |
} | |
}) | |
.disposed(by: disposeBag) | |
tableView.rx.setDelegate(self) | |
.disposed(by: disposeBag) | |
} | |
//MARK: - Private | |
fileprivate func configureDataSource() { | |
dataSource.titleForHeaderInSection = { dataSource, index in | |
dataSource.sectionModels[index].header | |
} | |
dataSource.configureCell = { [weak self] dataSource, tableView, indexPath, item in | |
let cell = tableView.dequeueReusableCell(withIdentifier: item.reuseIdentifier, for: indexPath) | |
if item.cellType.isPickerCell { | |
cell.configureCell(for: showPickerForType, interval: item.data as! DateInterval) | |
cell.delegate = self | |
} else { | |
cell.textLabel?.text = item.title | |
} | |
if let detailText = item.detailText { | |
cell.detailTextLabel?.text = detailText | |
} | |
cell.setNeedsUpdateConstraints() | |
return cell | |
} | |
dataSource.canEditRowAtIndexPath = { _ in false } | |
dataSource.animationConfiguration = AnimationConfiguration(insertAnimation: .top, reloadAnimation: .automatic, deleteAnimation: .fade) | |
} | |
} | |
//MARK: - UITableViewDelegate | |
extension EditTimeEntryVC: UITableViewDelegate { | |
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { | |
return self.viewModel.cellsInfoArray.value[indexPath.row].cellType.cellHeight | |
} | |
} | |
//MARK: - DatePickerDlegate | |
extension EditTimeEntryVC: DatePickerDlegate { | |
func selected(_ date: Date) { | |
viewModel.dateFromPicker.value = date | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import RxSwift | |
import Action | |
class EditTimeEntryViewModel: EditTimeEntryType { | |
// MARK: - Injections | |
var realmService: RealmProjectsServiceProtocol = RealmProjectsService.shared | |
let coordinator: SceneCoordinatorType | |
let disposeBag = DisposeBag() | |
// MARK: - Instance Properties | |
var dateFormatter = DateFormatter() { | |
didSet{ | |
dateFormatter.dateStyle = .short | |
dateFormatter.timeStyle = .short | |
} | |
} | |
var type: ScreenType = .create | |
let isValid: Observable<Bool> | |
let projectIds: Variable<[String]?> = Variable(nil) | |
let interval: Variable<DateInterval> = Variable(DateInterval(start: Date(), duration: 0)) | |
let projectsNames: Observable<String?> { | |
return projectIds.asObservable() | |
.map { [unowned self] ids in | |
var projectNames: String? | |
if let projectsIds = ids { | |
let names = self.realmService.projectsNames(projectsIds) | |
projectNames = names?.joined(separator: ", ") | |
} | |
self.updateProjectCellsInfo(projectNames) | |
return projectNames | |
} | |
} | |
let dateFromPicker = Variable(Date()) | |
let cellsInfoArray: Variable<[TimeEntryCellInfo]> | |
let sectionsItems: Observable<[AddTimeEntryListSection]> | |
// MARK: - Instance Methods | |
lazy var onCancel: CocoaAction = { (this: EditTimeEntryViewModel) in | |
return CocoaAction { [unowned this] _ in | |
AnalyticsService.shared.endTime(event: .timeEntryManualCreation, parameters: [.selectedButton(.cancel)]) | |
return this.coordinator.pop() | |
} | |
} (self) | |
lazy var onSave: CocoaAction = { [unowned self] _ in | |
return Action { _ in | |
for id in self.projectIds.value! { | |
do { | |
let project = try self.realmService.project(by: id) | |
let time = Time(dateInterval: self.interval.value) | |
self.realmService.add(time: time, to: project) | |
} catch { | |
print("error: \(error)") | |
} | |
} | |
AnalyticsService.shared.endTime(event: .timeEntryManualCreation, parameters: [.selectedButton(.done), .addTimeEntry(Int(self.interval.value.duration))]) | |
return self.coordinator.remove(type: .dismiss) | |
} | |
}() | |
func selectedCell(at indexPath: IndexPath) { | |
/* --process cellsInfoArray */ | |
} | |
// MARK: - Class Constructors | |
init(coordinator: SceneCoordinatorType, projectId: String? = nil) { | |
self.coordinator = coordinator | |
AnalyticsService.shared.track(event: .timeEntryManualCreation) | |
if let projectId = projectId { | |
projectIds.value = [projectId] | |
} | |
let projectValidation: Observable<Bool> = projectIds.asObservable() | |
.map { (ids) -> Bool in | |
if let ids = ids, ids.count > 0 { | |
return true | |
} | |
return false | |
} | |
.shareReplay(1) | |
let intervalValidation: Observable<Bool> = interval.asObservable() | |
.map { (dateInterval) -> Bool in | |
if dateInterval.duration > 59 { | |
return true | |
} | |
return false | |
} | |
.shareReplay(1) | |
isValid = Observable.combineLatest(projectValidation, intervalValidation) | |
.map { $0 && $1 } | |
cellsInfoArray = Variable(EditTimeEntryViewModel.cells(interval: interval.value)) | |
sectionsItems = cellsInfoArray.asObservable() | |
.map { cells in | |
return [AddTimeEntryListSection(header: "", items: cells)] | |
} | |
dateFromPicker.asObservable() | |
.subscribe { [unowned self] (date) in | |
guard let newDate = date.element else { return } | |
var updatedType: TimeEntryCellType? | |
for (index, element) in self.cellsInfoArray.value.enumerated() { | |
if element.cellType == .datePicker { | |
updatedType = self.cellsInfoArray.value[index - 1].cellType | |
break | |
} | |
} | |
} | |
.disposed(by: disposeBag) | |
} | |
// MARK: - Private Methods | |
fileprivate func showProjectSelection() { | |
let canselAction = CocoaAction { | |
return self.coordinator.remove(type: .pop) | |
} | |
let selectedAction: Action<[String], Void> = Action { [unowned self] identifiers in | |
if identifiers.first == SelectedProjectViewModel.AllProjectInfo.uid { | |
self.projectIds.value = nil | |
} else { | |
self.projectIds.value = identifiers | |
} | |
return self.coordinator.remove(type: .pop) | |
} | |
let selectProjectsViewModel = SelectedProjectViewModel(selectedAction: selectedAction, canselAction: canselAction, allowAllSelection: false, selectedProject: self.projectIds.value) | |
self.coordinator.transition(to: Scene.selectProjects(selectProjectsViewModel), type: .push) | |
} | |
fileprivate func updateProjectCellsInfo(_ names: String?) { | |
for object in cellsInfoArray.value { | |
if object.cellType == .projects { | |
object.update(data: names) | |
break | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment