Sebastian Guerrero
Sebastian Guerrero

Reputation: 453

Change format of a textfield on the fly using RxSwift

I want to give my textfield a Currency format so when I type it looks like:

$0.00
$0.09
$0.98
$9.87
$98.76

Upvotes: 2

Views: 1430

Answers (1)

Sebastian Guerrero
Sebastian Guerrero

Reputation: 453

So what I did was:

An extension of ObservableType with 2 functions, one for transform into the desired format and other to read the value:

extension ObservableType where E == String {

    func currencyFormattedToValue() -> Observable<E> {
        return asObservable().flatMap { currencyString -> Observable<E> in
          let formatter = Constants.moneyFormatter
          let value = Double(truncating: formatter.number(from: currencyString) ?? 0.00).rounded(toPlaces: 2)
          return Observable.just("\(value)")
        }
    }

    func valueToCurrencyFormatted() -> Observable<E> {
        return asObservable().flatMap { valueString -> Observable<E> in
          let formatter = Constants.moneyFormatter
          var amountWithPrefix = valueString
          var number: NSNumber!

          let regex = try! NSRegularExpression(pattern: "[^0-9]", options: .caseInsensitive)
          amountWithPrefix = regex.stringByReplacingMatches(in: amountWithPrefix, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, valueString.count), withTemplate: "")

          let double = (amountWithPrefix as NSString).doubleValue
          number = NSNumber(value: (double / 100))

          guard number != 0 as NSNumber else {
            return Observable.just("$0.00")
          }
          return Observable.just(formatter.string(from: number)!)
      }
  }
}

And then I used them like so:

textField.rx.text
    .orEmpty
    .changed
    .valueToCurrencyFormatted()
    .bind(to: self.textField.rx.text)
    .disposed(by: disposeBag)

and to read the value and bind it to a subject:

textField.rx.text
  .orEmpty
  .currencyFormattedToValue()
  .bind(to: viewModel.amountValue)
  .disposed(by: disposeBag)

My Formatter:

class Constants {
  static var moneyFormatter:NumberFormatter {
    let formatter = NumberFormatter()
    formatter.numberStyle = .currencyAccounting
    formatter.currencySymbol = "$"
    formatter.maximumFractionDigits = 2
    formatter.minimumFractionDigits = 2

    return formatter
  }
}

I'm open tu suggestions, thanks!

Upvotes: 9

Related Questions