RxValidator
Easy to Use, Read, Extensible, Flexible Validation Checker.
It can use without Rx.
Requirements
RxValidator
is written in Swift 4. Compatible with iOS 8.0+
Installation
CocoaPods
RxValidator is available through CocoaPods. To install
it, simply add the following line to your Podfile:
pod 'RxValidator'
At a Glance
You just use like this:
//without Rx
Validate.to(TargetValue)
.validate(condition)
...
.validate(condition)
.asObservable() or .check()
//with Rx
observable
.validate(condition)
...
.validate(condition)
.subscribe(...)
Usage
String
Validate.to("word is not empty")
.validate(StringShouldNotBeEmpty())
.check()
// result -> RxValidatorResult.valid
//multiple condition
Validate.to("[email protected]")
.validate(StringShouldNotBeEmpty())
.validate(StringIsNotOverflowThen(maxLength: 50))
.validate(StringShouldBeMatch("[a-z]+@[a-z]+\\.[a-z]+"))
.check()
// result -> RxValidatorResult.valid
Date
let targetDate: Date //2018-05-05
let sameTargetDate: Date
let afterTargetDate: Date
let beforeTargetDate: Date
Validate.to(Date())
.validate(.shouldEqualTo(date: sameTargetDate)) //(1)
.validate(.shouldAfterOrSameThen(date: sameTargetDate)) //(2)
.validate(.shouldBeforeOrSameThen(date: sameTargetDate)) //(3)
.validate(.shouldBeforeOrSameThen(date: afterTargetDate)) //(4)
.validate(.shouldBeforeThen(date: afterTargetDate)) //(5)
.validate(.shouldAfterOrSameThen(date: beforeTargetDate)) //(6)
.validate(.shouldAfterThen(date: beforeTargetDate)) //(7)
.check()
// check() result
// valid result -> RxValidatorResult.valid
// (1) not valid -> RxValidatorResult.notEqualDate
// (2) not valid -> RxValidatorResult.notAfterDate
// (3) not valid -> RxValidatorResult.notBeforeDate
// (4) not valid -> RxValidatorResult.notBeforeDate
// (5) not valid -> RxValidatorResult.notBeforeDate
// (6) not valid -> RxValidatorResult.notAfterDate
// (7) not valid -> RxValidatorResult.notAfterDate
Int
Validate.to(2)
.validate(NumberShouldBeEven())
.check()
//.valid
Validate.to(1)
.validate(NumberShouldBeEven())
.check()
//.notEvenNumber
Working with RxSwift
String
Validate.to("word is not empty")
.validate(StringShouldNotBeEmpty())
.asObservable()
.subscribe(onNext: { value in
print(value)
//print("word is not empty")
})
.disposed(by: disposeBag)
Validate.to("word is not empty")
.validate(StringShouldNotBeEmpty())
.asObservable()
.map { $0 + "!!" }
.bind(to: anotherObservableBinder)
.disposed(by: disposeBag)
//Multiple condition
Validate.to("[email protected]")
.validate(StringShouldNotBeEmpty()) //(1)
.validate(StringIsNotOverflowThen(maxLength: 50)) //(2)
.validate(StringShouldBeMatch("[a-z]+@[a-z]+\\.[a-z]+")) //(3)
.asObservable()
.subscribe(onNext: { value in
print(value)
//print("[email protected]")
},
onError: { error in
let validError = RxValidatorResult.determine(error: error)
// (1) validError -> RxValidatorResult.stringIsEmpty
// (2) validError -> RxValidatorResult.stringIsOverflow
// (3) validError -> RxValidatorResult.stringIsNotMatch
})
.disposed(by: disposeBag)
Int
Validate.to(2)
.validate(NumberShouldBeEven())
.asObservable()
.subscribe(onNext: { value in
print(value)
//print(2)
})
.disposed(by: disposeBag)
Validate.to(1)
.validate(NumberShouldBeEven())
.asObservable()
.subscribe(onNext: { value in
print(value)
//print(1)
},
onError: { error in
let validError = RxValidatorResult.determine(error: error)
//validError -> RxValidatorResult.notEvenNumber
})
.disposed(by: disposeBag)
Date
let targetDate: Date //2018-05-05
let sameTargetDate: Date
let afterTargetDate: Date
let beforeTargetDate: Date
Validate.to(Date())
.validate(.shouldEqualTo(date: sameTargetDate)) //(1)
.validate(.shouldAfterOrSameThen(date: sameTargetDate)) //(2)
.validate(.shouldBeforeOrSameThen(date: sameTargetDate)) //(3)
.validate(.shouldBeforeOrSameThen(date: afterTargetDate)) //(4)
.validate(.shouldBeforeThen(date: afterTargetDate)) //(5)
.validate(.shouldAfterOrSameThen(date: beforeTargetDate)) //(6)
.validate(.shouldAfterThen(date: beforeTargetDate)) //(7)
.asObservable()
.subscribe(onNext: { value in
print(value) //print("2018-05-05")
}, onError: { error in
let validError = RxValidatorResult.determine(error: error)
// (1) validError -> RxValidatorResult.notEqualDate
// (2) validError -> RxValidatorResult.notAfterDate
// (3) validError -> RxValidatorResult.notBeforeDate
// (4) validError -> RxValidatorResult.notBeforeDate
// (5) validError -> RxValidatorResult.notBeforeDate
// (6) validError -> RxValidatorResult.notAfterDate
// (7) validError -> RxValidatorResult.notAfterDate
})
.disposed(by: disposeBag)
Chaining from Observable
textField.rx.text
.filterNil()
.validate(StringIsAlwaysPass())
.subscribe(onNext: { (text) in
print(text)
})
.disposed(by: disposeBag)
let text = PublishSubject<String>()
text
.validate(StringIsAlwaysPass())
.subscribe(onNext: { (text) in
print(text)
})
.disposed(by: disposeBag)
Instant Condition Validation
Validate.to("swift")
.validate({ $0 == "objc" })
.check()
Validate.to(7)
.validate({ $0 > 10 })
.check()
Validate.to(Date())
.validate({ !$0.isToday })
.check()
Validate.to("swift")
.validate({ $0 == "objc" }, message: "This is not swift")
.check()
Validate.to(7)
.validate({ $0 > 10 }, message: "Number is too small.")
.check()
Validate.to(Date())
.validate({ !$0.isToday }, message: "It is today!!")
.check()
ResultType
enum RxValidatorResult
case notValid
case notValidWithMessage(message: String)
case notValidWithCode(code: Int)
case undefinedError
case stringIsOverflow
case stringIsEmpty
case stringIsNotMatch
case notEvenNumber
case invalidateDateTerm
case notBeforeDate
case notAfterDate
case notEqualDate
Working with ReactorKit (http://reactorkit.io)
func mutate(action: Action) -> Observable<Mutation> {
....
case let .changeTitle(title):
return Validate.to(title)
.validate(StringIsNotOverflowThen(maxLength: TITLE_MAX_LENGTH))
.asObservable()
.flatMap { Observable<Mutation>.just(.updateTitle(title: $0)) }
.catchError({ (error) -> Observable<Mutation> in
let validError = ValidationTargetErrorType.determine(error: error)
return Observable<Mutation>.just(.setTitleValidateError(validError, title))
})
....
Supported Validation Rules
//String
StringShouldNotBeEmpty()
StringIsNotOverflowThen(maxLength: Int)
StringShouldBeMatch("regex string")
//Int
NumberShouldBeEven()
//Date
DateValidatorType.shouldEqualTo(Date)
DateValidatorType.shouldBeforeThen(Date)
DateValidatorType.shouldBeforeOrSameThen(Date)
DateValidatorType.shouldAfterThen(Date)
DateValidatorType.shouldAfterOrSameThen(Date)
DateValidatorType.shouldBeCloseDates(date: Date, termOfDays: Int)
Make custom ValidationRule like this:
//String Type
class MyCustomStringValidationRule: StringValidatorType {
func validate(_ value: String) throws {
if {notValidCondition} {
throw RxValidatorResult.notValidate(code: 999) //'code' must be defined your self.
}
}
}
//Int Type
class MyCustomIntValidationRule: IntValidatorType {
func validate(_ value: Int) throws {
if {notValidCondition} {
throw RxValidatorResult.notValidate(code: 999) //'code' must be defined your self.
}
}
}
I want to be...
- More Built-in Validation Rules. (Help me. Welcome to PR.)
- Support More Type (Array, Float, Double, etc)
- More Flexible Code via Generics.