Easy to Use, Read, Extensible, Flexible Validation Checker
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("vbmania@me.com")
.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("vbmania@me.com")
.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("vbmania@me.com")
},
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.