Skip to content

Instantly share code, notes, and snippets.

@biloshkurskyi-ss
Created February 7, 2018 11:22
Show Gist options
  • Save biloshkurskyi-ss/f11d4add568a7735f59e45f4df399304 to your computer and use it in GitHub Desktop.
Save biloshkurskyi-ss/f11d4add568a7735f59e45f4df399304 to your computer and use it in GitHub Desktop.
Code example #1
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 }
}
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
}
}
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