From 43b1861347f313092211cb08377d686fb0dfc200 Mon Sep 17 00:00:00 2001 From: Grigory Date: Thu, 8 Jun 2017 14:18:52 +0300 Subject: [PATCH 1/3] formTableView --- LeadKitAdditions.podspec | 4 +- .../Protocols/CellFieldJumpingProtocol.swift | 38 +++++ .../Protocols/CellFieldMaskProtocol.swift | 14 ++ .../CellFieldValidationProtocol.swift | 5 + .../Protocols/CellFieldsToolBarProtocol.swift | 16 ++ .../Protocols/FormCellViewModelProtocol.swift | 15 ++ .../Services/CellFieldsJumpingService.swift | 154 ++++++++++++++++++ .../Sources/Services/MaskFieldTextProxy.swift | 53 ++++++ .../ValidationService/ValidationError.swift | 15 ++ .../ValidationService/ValidationItem.swift | 98 +++++++++++ .../ValidationService/ValidationService.swift | 104 ++++++++++++ .../Views/CellTextField/CellTextField.swift | 44 +++++ .../CellTextFieldViewModel.swift | 30 ++++ 13 files changed, 589 insertions(+), 1 deletion(-) create mode 100644 LeadKitAdditions/Sources/Protocols/CellFieldJumpingProtocol.swift create mode 100644 LeadKitAdditions/Sources/Protocols/CellFieldMaskProtocol.swift create mode 100644 LeadKitAdditions/Sources/Protocols/CellFieldValidationProtocol.swift create mode 100644 LeadKitAdditions/Sources/Protocols/CellFieldsToolBarProtocol.swift create mode 100644 LeadKitAdditions/Sources/Protocols/FormCellViewModelProtocol.swift create mode 100644 LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift create mode 100644 LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift create mode 100644 LeadKitAdditions/Sources/Services/ValidationService/ValidationError.swift create mode 100644 LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift create mode 100644 LeadKitAdditions/Sources/Services/ValidationService/ValidationService.swift create mode 100644 LeadKitAdditions/Sources/Views/CellTextField/CellTextField.swift create mode 100644 LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift diff --git a/LeadKitAdditions.podspec b/LeadKitAdditions.podspec index e3efa75..8a8625d 100644 --- a/LeadKitAdditions.podspec +++ b/LeadKitAdditions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKitAdditions" - s.version = "0.0.16" + s.version = "0.0.17" s.summary = "iOS framework with a bunch of tools for rapid development" s.homepage = "https://github.com/TouchInstinct/LeadKitAdditions" s.license = "Apache License, Version 2.0" @@ -19,6 +19,8 @@ Pod::Spec.new do |s| ss.dependency "LeadKit", '0.5.1' ss.dependency "KeychainAccess", '3.0.2' ss.dependency "IDZSwiftCommonCrypto", '0.9.1' + ss.dependency "InputMask", '2.2.5' + ss.dependency "SwiftValidator", '4.0.0' end s.subspec 'Core-iOS-Extension' do |ss| diff --git a/LeadKitAdditions/Sources/Protocols/CellFieldJumpingProtocol.swift b/LeadKitAdditions/Sources/Protocols/CellFieldJumpingProtocol.swift new file mode 100644 index 0000000..176bb81 --- /dev/null +++ b/LeadKitAdditions/Sources/Protocols/CellFieldJumpingProtocol.swift @@ -0,0 +1,38 @@ +import RxSwift +import RxCocoa +import UIKit + +typealias UIItemSettingsBlock = (UIItem) -> Void where UIItem: UIView + +protocol CellFieldJumpingProtocol: FormCellViewModelProtocol { + + var toolBar: UIToolbar? { get set } + + var shouldGoForward: PublishSubject { get } + + var shouldBecomeFirstResponder: PublishSubject { get } + var shouldResignFirstResponder: PublishSubject { get } + + var returnButtonType: UIReturnKeyType { get set } + +} + +extension CellFieldJumpingProtocol { + + func bind(for textField: UITextField, to disposeBag: DisposeBag) { + shouldResignFirstResponder.asObservable() + .observeOn(MainScheduler.instance) + .subscribe(onNext: { [weak textField] _ in + textField?.resignFirstResponder() + }) + .addDisposableTo(disposeBag) + + shouldBecomeFirstResponder.asObservable() + .observeOn(MainScheduler.instance) + .subscribe(onNext: { [weak textField] _ in + textField?.becomeFirstResponder() + }) + .addDisposableTo(disposeBag) + } + +} diff --git a/LeadKitAdditions/Sources/Protocols/CellFieldMaskProtocol.swift b/LeadKitAdditions/Sources/Protocols/CellFieldMaskProtocol.swift new file mode 100644 index 0000000..493fb62 --- /dev/null +++ b/LeadKitAdditions/Sources/Protocols/CellFieldMaskProtocol.swift @@ -0,0 +1,14 @@ +protocol CellFieldMaskProtocol { + + var haveMask: Bool { get } + var maskFieldTextProxy: MaskFieldTextProxy? { get set } + +} + +extension CellFieldMaskProtocol { + + var haveMask: Bool { + return maskFieldTextProxy != nil + } + +} diff --git a/LeadKitAdditions/Sources/Protocols/CellFieldValidationProtocol.swift b/LeadKitAdditions/Sources/Protocols/CellFieldValidationProtocol.swift new file mode 100644 index 0000000..b294adb --- /dev/null +++ b/LeadKitAdditions/Sources/Protocols/CellFieldValidationProtocol.swift @@ -0,0 +1,5 @@ +protocol CellFieldValidationProtocol { + + var validationItem: ValidationItem? { get set } + +} diff --git a/LeadKitAdditions/Sources/Protocols/CellFieldsToolBarProtocol.swift b/LeadKitAdditions/Sources/Protocols/CellFieldsToolBarProtocol.swift new file mode 100644 index 0000000..c2bf138 --- /dev/null +++ b/LeadKitAdditions/Sources/Protocols/CellFieldsToolBarProtocol.swift @@ -0,0 +1,16 @@ +import UIKit +import RxSwift + +protocol CellFieldsToolBarProtocol: class { + + var needArrows: Bool { get set } + + var canGoForward: Bool { get set } + var canGoBackward: Bool { get set } + + var shouldGoForward: PublishSubject { get } + var shouldGoBackward: PublishSubject { get } + + var shouldEndEditing: PublishSubject { get } + +} diff --git a/LeadKitAdditions/Sources/Protocols/FormCellViewModelProtocol.swift b/LeadKitAdditions/Sources/Protocols/FormCellViewModelProtocol.swift new file mode 100644 index 0000000..84a85c9 --- /dev/null +++ b/LeadKitAdditions/Sources/Protocols/FormCellViewModelProtocol.swift @@ -0,0 +1,15 @@ +import RxCocoa +import RxSwift + +protocol FormCellViewModelProtocol: class { + var isActive: Bool { get set } +} + +extension FormCellViewModelProtocol { + + func activate(_ isActive: Bool) -> Self { + self.isActive = isActive + return self + } + +} diff --git a/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift b/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift new file mode 100644 index 0000000..daac2c7 --- /dev/null +++ b/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift @@ -0,0 +1,154 @@ +import RxSwift +import UIKit + +enum CellFieldsToolBarType { + case none + case `default` +} + +struct CellFieldsJumpingServiceConfig { + + var toolBarType: CellFieldsToolBarType = .default + var toolBarNeedArrows = true + + init() {} + + init(toolBarType: CellFieldsToolBarType) { + self.toolBarType = toolBarType + } + + static var `default`: CellFieldsJumpingServiceConfig { + return CellFieldsJumpingServiceConfig() + } + +} + +class CellFieldsJumpingService { + + private var disposeBag = DisposeBag() + + // MARK: - Private properties + + private var cellFields: [CellFieldJumpingProtocol] = [] + + // MARK: - Public propertries + + var config: CellFieldsJumpingServiceConfig = .default { + didSet { + configure() + } + } + + let didDone = PublishSubject() + + // MARK: - Initialization + + init() {} + + // MARK: - Public + + func removeAll() { + cellFields.removeAll() + disposeBag = DisposeBag() + } + + func add(fieled: CellFieldJumpingProtocol, shouldConfigure: Bool = true) { + add(fieleds: [fieled], shouldConfigure: shouldConfigure) + } + + func add(fieleds: [CellFieldJumpingProtocol], shouldConfigure: Bool = true) { + cellFields += fieleds + + if shouldConfigure { + configure() + } + } + + func configure() { + disposeBag = DisposeBag() + + let cellFields = self.cellFields + + cellFields + .filter { $0.isActive } + .enumerated() + .forEach { offset, field in + field.toolBar = toolBar(for: field, with: offset) + field.returnButtonType = .next + + field.shouldGoForward.asObservable() + .subscribe(onNext: { + if let nextActive = cellFields.nextActive(from: offset) { + nextActive.shouldBecomeFirstResponder.onNext() + } else { + self.didDone.onNext() + } + }) + .addDisposableTo(disposeBag) + } + + cellFields.lastActive?.returnButtonType = .done + } + + private func toolBar(for field: CellFieldJumpingProtocol, with index: Int) -> UIToolbar { + let toolBar = CellTextFieldToolBar() + toolBar.canGoForward = cellFields.nextActive(from: index) != nil + toolBar.canGoBackward = cellFields.previousActive(from: index) != nil + + toolBar.needArrows = config.toolBarNeedArrows + + toolBar.shouldGoForward.asObservable() + .subscribe(onNext: { [weak self] in + if let nextActive = self?.cellFields.nextActive(from: index) { + nextActive.shouldBecomeFirstResponder.onNext() + } else { + self?.didDone.onNext() + } + }) + .addDisposableTo(disposeBag) + + toolBar.shouldGoBackward.asObservable() + .subscribe(onNext: { [weak self] in + if let previousActive = self?.cellFields.previousActive(from: index) { + previousActive.shouldBecomeFirstResponder.onNext() + } + }) + .addDisposableTo(disposeBag) + + toolBar.shouldEndEditing.asObservable() + .subscribe(onNext: { + field.shouldResignFirstResponder.onNext() + }) + .addDisposableTo(disposeBag) + + return toolBar + } + +} + +extension Array where Element == CellFieldJumpingProtocol { + + var firstActive: CellFieldJumpingProtocol? { + return first { $0.isActive } + } + + var lastActive: CellFieldJumpingProtocol? { + return reversed().first { $0.isActive } + } + + func nextActive(from index: Int) -> CellFieldJumpingProtocol? { + for (currentIndex, item) in enumerated() where currentIndex > index && item.isActive { + return item + } + return nil + } + + func previousActive(from index: Int) -> CellFieldJumpingProtocol? { + let reversedIndex = count - index - 1 + for (currentIndex, item) in reversed().enumerated() where currentIndex > reversedIndex && item.isActive { + return item + } + return nil + } + +} diff --git a/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift b/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift new file mode 100644 index 0000000..67012c8 --- /dev/null +++ b/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift @@ -0,0 +1,53 @@ +import InputMask +import RxCocoa +import RxSwift + +class MaskFieldTextProxy: NSObject { + + private var disposeBag = DisposeBag() + + let text = Variable("") + let isComplete = Variable(false) + + private(set) var field: UITextField? + + private let maskedDelegate: PolyMaskTextFieldDelegate + + init(primaryFormat: String, affineFormats: [String] = []) { + maskedDelegate = PolyMaskTextFieldDelegate(primaryFormat: primaryFormat, affineFormats: affineFormats) + + super.init() + + maskedDelegate.listener = self + } + + func configure(with field: UITextField) { + self.field = field + field.delegate = maskedDelegate + } + + private func bindData() { + disposeBag = DisposeBag() + + text.asDriver() + .distinctUntilChanged() + .drive(onNext: { [weak self] value in + guard let textField = self?.field else { + return + } + + self?.maskedDelegate.put(text: value, into: textField) + }) + .addDisposableTo(disposeBag) + } + +} + +extension MaskFieldTextProxy: MaskedTextFieldDelegateListener { + + func textField(_ textField: UITextField, didFillMandatoryCharacters complete: Bool, didExtractValue value: String) { + text.value = value + isComplete.value = complete + } + +} diff --git a/LeadKitAdditions/Sources/Services/ValidationService/ValidationError.swift b/LeadKitAdditions/Sources/Services/ValidationService/ValidationError.swift new file mode 100644 index 0000000..69c1ec6 --- /dev/null +++ b/LeadKitAdditions/Sources/Services/ValidationService/ValidationError.swift @@ -0,0 +1,15 @@ +import SwiftValidator + +struct ValidationError: Error { + + let failedRule: Rule + let errorMessage: String? + let errorHint: String? + + init(failedRule: Rule, errorMessage: String?, errorHint: String? = nil) { + self.failedRule = failedRule + self.errorMessage = errorMessage + self.errorHint = errorHint + } + +} diff --git a/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift b/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift new file mode 100644 index 0000000..d14ee84 --- /dev/null +++ b/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift @@ -0,0 +1,98 @@ +import SwiftValidator +import RxSwift +import RxCocoa + +enum ValidationItemState { + case initial + case correction(ValidationError) + case error(ValidationError) + case valid +} + +extension ValidationItemState { + + var isInitial: Bool { + switch self { + case .initial: + return true + default: + return false + } + } + + var isValid: Bool { + switch self { + case .valid: + return true + default: + return false + } + } + +} + +class ValidationItem { + + private let disposeBag = DisposeBag() + + private let validationStateHolder: Variable = Variable(.initial) + var validationState: ValidationItemState { + return validationStateHolder.value + } + var validationStateObservable: Observable { + return validationStateHolder.asObservable() + } + + private(set) var rules: [Rule] = [] + private var text: String? + + init(textObservable: Observable, rules: [Rule]) { + self.rules = rules + bindValue(with: textObservable) + } + + private func bindValue(with textObservable: Observable) { + textObservable + .do(onNext: { [weak self] value in + self?.text = value + }) + .filter { [weak self] _ in !(self?.validationState.isInitial ?? true)} + .subscribe(onNext: { [weak self] value in + self?.validate(text: value) + }) + .addDisposableTo(disposeBag) + } + + @discardableResult + func manualValidate() -> Bool { + return validate(text: text, isManual: true) + } + + @discardableResult + private func validate(text: String?, isManual: Bool = false) -> Bool { + let error = rules.filter{ + return !$0.validate(text ?? "") + } + .map{ rule -> ValidationError in + return ValidationError(failedRule: rule, errorMessage: rule.errorMessage()) + } + .first + + if let validationError = error { + switch validationStateHolder.value { + case .error where !isManual, + .correction where !isManual, + .valid where !isManual: + + validationStateHolder.value = .correction(validationError) + default: + validationStateHolder.value = .error(validationError) + } + } else { + validationStateHolder.value = .valid + } + + return validationStateHolder.value.isValid + } + +} diff --git a/LeadKitAdditions/Sources/Services/ValidationService/ValidationService.swift b/LeadKitAdditions/Sources/Services/ValidationService/ValidationService.swift new file mode 100644 index 0000000..5e2366a --- /dev/null +++ b/LeadKitAdditions/Sources/Services/ValidationService/ValidationService.swift @@ -0,0 +1,104 @@ +import SwiftValidator +import RxCocoa +import RxSwift + +private enum ValidationServiceStateReactType { + case none + case all + case each +} + +enum ValidationServiceState { + case initial + case valid + case invalid +} + +extension ValidationServiceState { + + var isValid: Bool { + return self == .valid + } + +} + +class ValidationService { + + private var disposeBag = DisposeBag() + + private(set) var validationItems: [ValidationItem] = [] + + private let stateHolder: Variable = Variable(.initial) + var state: ValidationServiceState { + return stateHolder.value + } + var stateObservable: Observable { + return stateHolder.asObservable() + } + + private var validationStateReactType: ValidationServiceStateReactType = .none + + func register(item: ValidationItem) { + register(items: [item]) + } + + func register(items: [ValidationItem]) { + validationItems += items + bindItems() + } + + func unregisterAll() { + validationItems.removeAll() + bindItems() + } + + func unregister(item: ValidationItem) { + unregister(items: [item]) + } + + func unregister(items: [ValidationItem]) { + items.forEach { item in + if let removeIndex = validationItems.index(where: { $0 === item }) { + validationItems.remove(at: removeIndex) + } + } + + bindItems() + } + + @discardableResult + func validate() -> Bool { + validationStateReactType = .all + let isValid = validationItems.map { $0.manualValidate()}.reduce(true) { $0 && $1 } + validationStateReactType = .each + + return isValid + } + + private func bindItems() { + disposeBag = DisposeBag() + + let allValidationStateObservables = validationItems.map { $0.validationStateObservable } + + let zipStates = Observable + .zip(allValidationStateObservables) { $0 } + .filter { [weak self] _ in self?.validationStateReactType == .all } + + let combineLatestStates = Observable + .combineLatest(allValidationStateObservables) { $0 } + .filter { [weak self] _ in self?.validationStateReactType == .each } + + let stateObservables = [zipStates, combineLatestStates] + + stateObservables.forEach { observable in + observable + .map { states -> Bool in + return states.map { $0.isValid }.reduce(true) { $0 && $1 } + } + .map { $0 ? ValidationServiceState.valid : .invalid } + .bind(to: stateHolder) + .addDisposableTo(disposeBag) + } + } + +} diff --git a/LeadKitAdditions/Sources/Views/CellTextField/CellTextField.swift b/LeadKitAdditions/Sources/Views/CellTextField/CellTextField.swift new file mode 100644 index 0000000..4ccf4ac --- /dev/null +++ b/LeadKitAdditions/Sources/Views/CellTextField/CellTextField.swift @@ -0,0 +1,44 @@ +import UIKit +import RxCocoa +import RxSwift + +class CellTextField: UITextField { + + private var disposeBag = DisposeBag() + + var viewModel: CellTextFieldViewModel? { + didSet { + configure() + } + } + + // MARK: - Init + + private func configure() { + disposeBag = DisposeBag() + + guard let viewModel = viewModel else { + return + } + + inputAccessoryView = viewModel.toolBar + returnKeyType = viewModel.returnButtonType + + text = viewModel.text.value + placeholder = viewModel.placeholder + viewModel.textFieldSettingsBlock?(self) + + viewModel.bind(for: self, to: disposeBag) + + rx.text.asDriver() + .drive(viewModel.text) + .addDisposableTo(disposeBag) + + rx.controlEvent(.editingDidEndOnExit).asObservable() + .subscribe(onNext: { + viewModel.shouldGoForward.onNext() + }) + .addDisposableTo(disposeBag) + } + +} diff --git a/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift b/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift new file mode 100644 index 0000000..f8088ec --- /dev/null +++ b/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift @@ -0,0 +1,30 @@ +import UIKit +import RxSwift + +class CellTextFieldViewModel: CellFieldJumpingProtocol { + + let text: Variable + let placeholder: String + + let textFieldSettingsBlock: UIItemSettingsBlock? + + // MARK: - CellFieldJumpingProtocol + + var toolBar: UIToolbar? + + let shouldGoForward = PublishSubject() + + let shouldBecomeFirstResponder = PublishSubject() + let shouldResignFirstResponder = PublishSubject() + + var returnButtonType: UIReturnKeyType = .default + + var isActive: Bool = true + + init(initialText: String = "", placeholder: String = "", textFieldSettingsBlock: UIItemSettingsBlock? = nil) { + text = Variable(initialText) + self.placeholder = placeholder + self.textFieldSettingsBlock = textFieldSettingsBlock + } + +} From 36200eaeb9c84d96caee65c724551725007f7b14 Mon Sep 17 00:00:00 2001 From: Grigory Date: Thu, 8 Jun 2017 16:15:36 +0300 Subject: [PATCH 2/3] build fix --- .../Sources/Protocols/CellFieldJumpingProtocol.swift | 2 +- .../Sources/Protocols/CellFieldMaskProtocol.swift | 2 +- .../Sources/Services/CellFieldsJumpingService.swift | 8 ++++---- .../Sources/Services/MaskFieldTextProxy.swift | 10 +++++----- .../Services/ValidationService/ValidationItem.swift | 4 ++-- .../Views/CellTextField/CellTextFieldViewModel.swift | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/LeadKitAdditions/Sources/Protocols/CellFieldJumpingProtocol.swift b/LeadKitAdditions/Sources/Protocols/CellFieldJumpingProtocol.swift index 176bb81..91e375f 100644 --- a/LeadKitAdditions/Sources/Protocols/CellFieldJumpingProtocol.swift +++ b/LeadKitAdditions/Sources/Protocols/CellFieldJumpingProtocol.swift @@ -34,5 +34,5 @@ extension CellFieldJumpingProtocol { }) .addDisposableTo(disposeBag) } - + } diff --git a/LeadKitAdditions/Sources/Protocols/CellFieldMaskProtocol.swift b/LeadKitAdditions/Sources/Protocols/CellFieldMaskProtocol.swift index 493fb62..e3e9df3 100644 --- a/LeadKitAdditions/Sources/Protocols/CellFieldMaskProtocol.swift +++ b/LeadKitAdditions/Sources/Protocols/CellFieldMaskProtocol.swift @@ -10,5 +10,5 @@ extension CellFieldMaskProtocol { var haveMask: Bool { return maskFieldTextProxy != nil } - + } diff --git a/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift b/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift index daac2c7..31a8cac 100644 --- a/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift +++ b/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift @@ -78,7 +78,7 @@ class CellFieldsJumpingService { field.shouldGoForward.asObservable() .subscribe(onNext: { - if let nextActive = cellFields.nextActive(from: offset) { + if let nextActive = cellFields.nextActive(from: offset) { nextActive.shouldBecomeFirstResponder.onNext() } else { self.didDone.onNext() @@ -99,7 +99,7 @@ class CellFieldsJumpingService { toolBar.shouldGoForward.asObservable() .subscribe(onNext: { [weak self] in - if let nextActive = self?.cellFields.nextActive(from: index) { + if let nextActive = self?.cellFields.nextActive(from: index) { nextActive.shouldBecomeFirstResponder.onNext() } else { self?.didDone.onNext() @@ -109,7 +109,7 @@ class CellFieldsJumpingService { toolBar.shouldGoBackward.asObservable() .subscribe(onNext: { [weak self] in - if let previousActive = self?.cellFields.previousActive(from: index) { + if let previousActive = self?.cellFields.previousActive(from: index) { previousActive.shouldBecomeFirstResponder.onNext() } }) @@ -123,7 +123,7 @@ class CellFieldsJumpingService { return toolBar } - + } extension Array where Element == CellFieldJumpingProtocol { diff --git a/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift b/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift index 67012c8..1a67202 100644 --- a/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift +++ b/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift @@ -11,19 +11,19 @@ class MaskFieldTextProxy: NSObject { private(set) var field: UITextField? - private let maskedDelegate: PolyMaskTextFieldDelegate + private let maskedProxy: PolyMaskTextFieldDelegate init(primaryFormat: String, affineFormats: [String] = []) { - maskedDelegate = PolyMaskTextFieldDelegate(primaryFormat: primaryFormat, affineFormats: affineFormats) + maskedProxy = PolyMaskTextFieldDelegate(primaryFormat: primaryFormat, affineFormats: affineFormats) super.init() - maskedDelegate.listener = self + maskedProxy.listener = self } func configure(with field: UITextField) { self.field = field - field.delegate = maskedDelegate + field.delegate = maskedProxy } private func bindData() { @@ -36,7 +36,7 @@ class MaskFieldTextProxy: NSObject { return } - self?.maskedDelegate.put(text: value, into: textField) + self?.maskedProxy.put(text: value, into: textField) }) .addDisposableTo(disposeBag) } diff --git a/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift b/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift index d14ee84..1f3574f 100644 --- a/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift +++ b/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift @@ -70,10 +70,10 @@ class ValidationItem { @discardableResult private func validate(text: String?, isManual: Bool = false) -> Bool { - let error = rules.filter{ + let error = rules.filter { return !$0.validate(text ?? "") } - .map{ rule -> ValidationError in + .map { rule -> ValidationError in return ValidationError(failedRule: rule, errorMessage: rule.errorMessage()) } .first diff --git a/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift b/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift index f8088ec..5f819db 100644 --- a/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift +++ b/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift @@ -26,5 +26,5 @@ class CellTextFieldViewModel: CellFieldJumpingProtocol { self.placeholder = placeholder self.textFieldSettingsBlock = textFieldSettingsBlock } - + } From c79c740d795a458df748a9ec79ee48ddfe8ddbfd Mon Sep 17 00:00:00 2001 From: Grigory Date: Tue, 20 Jun 2017 17:32:18 +0300 Subject: [PATCH 3/3] pull request fix --- .../Protocols/CellFieldJumpingProtocol.swift | 2 +- .../Services/CellFieldsJumpingService.swift | 42 ++++++------------- .../Sources/Services/MaskFieldTextProxy.swift | 12 ++++-- .../ValidationService/ValidationItem.swift | 20 ++++----- .../ValidationService/ValidationService.swift | 4 +- .../CellTextFieldViewModel.swift | 4 +- 6 files changed, 36 insertions(+), 48 deletions(-) diff --git a/LeadKitAdditions/Sources/Protocols/CellFieldJumpingProtocol.swift b/LeadKitAdditions/Sources/Protocols/CellFieldJumpingProtocol.swift index 91e375f..39029f7 100644 --- a/LeadKitAdditions/Sources/Protocols/CellFieldJumpingProtocol.swift +++ b/LeadKitAdditions/Sources/Protocols/CellFieldJumpingProtocol.swift @@ -2,7 +2,7 @@ import RxSwift import RxCocoa import UIKit -typealias UIItemSettingsBlock = (UIItem) -> Void where UIItem: UIView +typealias ItemSettingsBlock = (UIItem) -> Void where UIItem: UIView protocol CellFieldJumpingProtocol: FormCellViewModelProtocol { diff --git a/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift b/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift index 31a8cac..692ff28 100644 --- a/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift +++ b/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift @@ -1,22 +1,12 @@ import RxSwift import UIKit -enum CellFieldsToolBarType { - case none - case `default` -} - struct CellFieldsJumpingServiceConfig { - var toolBarType: CellFieldsToolBarType = .default var toolBarNeedArrows = true init() {} - init(toolBarType: CellFieldsToolBarType) { - self.toolBarType = toolBarType - } - static var `default`: CellFieldsJumpingServiceConfig { return CellFieldsJumpingServiceConfig() } @@ -77,12 +67,8 @@ class CellFieldsJumpingService { field.returnButtonType = .next field.shouldGoForward.asObservable() - .subscribe(onNext: { - if let nextActive = cellFields.nextActive(from: offset) { - nextActive.shouldBecomeFirstResponder.onNext() - } else { - self.didDone.onNext() - } + .subscribe(onNext: { [weak self] in + self?.shouldGoForwardAction(from: offset) }) .addDisposableTo(disposeBag) } @@ -99,11 +85,7 @@ class CellFieldsJumpingService { toolBar.shouldGoForward.asObservable() .subscribe(onNext: { [weak self] in - if let nextActive = self?.cellFields.nextActive(from: index) { - nextActive.shouldBecomeFirstResponder.onNext() - } else { - self?.didDone.onNext() - } + self?.shouldGoForwardAction(from: index) }) .addDisposableTo(disposeBag) @@ -124,6 +106,14 @@ class CellFieldsJumpingService { return toolBar } + private func shouldGoForwardAction(from index: Int) { + if let nextActive = cellFields.nextActive(from: index) { + nextActive.shouldBecomeFirstResponder.onNext() + } else { + didDone.onNext() + } + } + } extension Array where Element == CellFieldJumpingProtocol { @@ -137,18 +127,12 @@ extension Array where Element == CellFieldJumpingProtocol { } func nextActive(from index: Int) -> CellFieldJumpingProtocol? { - for (currentIndex, item) in enumerated() where currentIndex > index && item.isActive { - return item - } - return nil + return enumerated().first { $0 > index && $1.isActive }?.element } func previousActive(from index: Int) -> CellFieldJumpingProtocol? { let reversedIndex = count - index - 1 - for (currentIndex, item) in reversed().enumerated() where currentIndex > reversedIndex && item.isActive { - return item - } - return nil + return reversed().enumerated().first { $0 > reversedIndex && $1.isActive }?.element } } diff --git a/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift b/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift index 1a67202..0ef05cf 100644 --- a/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift +++ b/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift @@ -7,7 +7,13 @@ class MaskFieldTextProxy: NSObject { private var disposeBag = DisposeBag() let text = Variable("") - let isComplete = Variable(false) + fileprivate let isCompleteHolder = Variable(false) + var isComplete: Bool { + return isCompleteHolder.value + } + var isCompleteObservable: Observable { + return isCompleteHolder.asObservable() + } private(set) var field: UITextField? @@ -36,7 +42,7 @@ class MaskFieldTextProxy: NSObject { return } - self?.maskedProxy.put(text: value, into: textField) + self?.maskedDelegate.put(text: value, into: textField) }) .addDisposableTo(disposeBag) } @@ -47,7 +53,7 @@ extension MaskFieldTextProxy: MaskedTextFieldDelegateListener { func textField(_ textField: UITextField, didFillMandatoryCharacters complete: Bool, didExtractValue value: String) { text.value = value - isComplete.value = complete + isCompleteHolder.value = complete } } diff --git a/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift b/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift index 1f3574f..902ac93 100644 --- a/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift +++ b/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift @@ -35,7 +35,7 @@ class ValidationItem { private let disposeBag = DisposeBag() - private let validationStateHolder: Variable = Variable(.initial) + private let validationStateHolder = Variable(.initial) var validationState: ValidationItemState { return validationStateHolder.value } @@ -43,19 +43,17 @@ class ValidationItem { return validationStateHolder.asObservable() } - private(set) var rules: [Rule] = [] - private var text: String? + let text = Variable(nil) - init(textObservable: Observable, rules: [Rule]) { + private(set) var rules: [Rule] = [] + + init(rules: [Rule]) { self.rules = rules - bindValue(with: textObservable) + bindText() } - private func bindValue(with textObservable: Observable) { - textObservable - .do(onNext: { [weak self] value in - self?.text = value - }) + private func bindText() { + text.asObservable() .filter { [weak self] _ in !(self?.validationState.isInitial ?? true)} .subscribe(onNext: { [weak self] value in self?.validate(text: value) @@ -65,7 +63,7 @@ class ValidationItem { @discardableResult func manualValidate() -> Bool { - return validate(text: text, isManual: true) + return validate(text: text.value, isManual: true) } @discardableResult diff --git a/LeadKitAdditions/Sources/Services/ValidationService/ValidationService.swift b/LeadKitAdditions/Sources/Services/ValidationService/ValidationService.swift index 5e2366a..37e5fbe 100644 --- a/LeadKitAdditions/Sources/Services/ValidationService/ValidationService.swift +++ b/LeadKitAdditions/Sources/Services/ValidationService/ValidationService.swift @@ -28,7 +28,7 @@ class ValidationService { private(set) var validationItems: [ValidationItem] = [] - private let stateHolder: Variable = Variable(.initial) + private let stateHolder = Variable(.initial) var state: ValidationServiceState { return stateHolder.value } @@ -69,7 +69,7 @@ class ValidationService { @discardableResult func validate() -> Bool { validationStateReactType = .all - let isValid = validationItems.map { $0.manualValidate()}.reduce(true) { $0 && $1 } + let isValid = validationItems.map { $0.manualValidate() }.reduce(true) { $0 && $1 } validationStateReactType = .each return isValid diff --git a/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift b/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift index 5f819db..c154ede 100644 --- a/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift +++ b/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift @@ -6,7 +6,7 @@ class CellTextFieldViewModel: CellFieldJumpingProtocol { let text: Variable let placeholder: String - let textFieldSettingsBlock: UIItemSettingsBlock? + let textFieldSettingsBlock: ItemSettingsBlock? // MARK: - CellFieldJumpingProtocol @@ -21,7 +21,7 @@ class CellTextFieldViewModel: CellFieldJumpingProtocol { var isActive: Bool = true - init(initialText: String = "", placeholder: String = "", textFieldSettingsBlock: UIItemSettingsBlock? = nil) { + init(initialText: String = "", placeholder: String = "", textFieldSettingsBlock: ItemSettingsBlock? = nil) { text = Variable(initialText) self.placeholder = placeholder self.textFieldSettingsBlock = textFieldSettingsBlock