iosswiftrx-swiftrx-cocoa

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


Solution

  • 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!