2

I have a simple TextField for telephone input, and want to format it each time it being changed.

I'm using PhoneNumberKit and it works fine, but i do not understand how to call formatting func after the value in textField have changed.

Telephone Formatting function.

import SwiftUI
import PhoneNumberKit
import Combine

func formatTelephone(telephone : String) -> String
    {

        do {
            let phoneNumber =  PartialFormatter().formatPartial(telephone)

            print(phoneNumber)
            return phoneNumber
        }
        catch {
            print("Generic parser error")

        }
    }

It does something like this:

formatTelephone("79152140700") -> "7 (915) 214 08-00" 
formatTelephone("791521") -> "7 (915) 21" 

and i have a TextField like that

TextField("915 214 07 00" , text: $telephoneManager.telephone)

After each input of a digit the whole textfield label needs to be formatted by a func and show user's input in a better way.

Nikita K.
  • 33
  • 5
  • You should listen for text changes and call your formatTelephone method after each change. You can see how to do that here: https://stackoverflow.com/questions/28394933/how-do-i-check-when-a-uitextfield-changes – Razvan S. Oct 28 '19 at 10:13
  • if you want to support more than one country, implementing it yourself is a nightmare. There's a cool library I used -> https://github.com/marmelroy/PhoneNumberKit – Tal Cohen Oct 28 '19 at 11:49

1 Answers1

2

In order to get what you want you need to use Combine (the implementation of the two methods in the view model is up to you because they strictly depend on your needs):

import SwiftUI
import Combine

class ContentViewModel: ObservableObject {
    @Published var phoneNumber = ""
    private var toCancel: AnyCancellable!

    init() {
        toCancel = $phoneNumber
            .filter { !self.isFormatValid($0) }
            .map { self.convert($0) }
            .receive(on: RunLoop.main)
            .assign(to: \.phoneNumber, on: self)
    }

    private func isFormatValid(_ str: String) -> Bool {
        //here you must check if _str_ is already in the right format
        true //just to make this example compile, but clearly this must be replaced with your code
    }

    private func convert(_ str: String) -> String {
        //convert your string to be in the right format
        "string in the correct format" //just to make this example compile, but clearly this must be replaced with your code
    }

    deinit {
        toCancel.cancel()
    }
}

struct ContentView: View {
    @ObservedObject var contentViewModel = ContentViewModel()

    var body: some View {
        TextField("Your phone number...", text: $contentViewModel.phoneNumber)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Steps are (look at the view model init):

  1. phoneNumber is a @Published property, so "it's backed" by a publisher you can subscribe to. So, let's subscribe to that publisher.
  2. You must check if the string in your textfield is already in the right format. This is paramount: if you miss this step you'll end up with an infinite loop. This is done through the .filter operator. The event is forwarded down only if the .filter operator returns true (in this case it returns true if the string format is invalid).
  3. With the .map operator you can change the string (in the wrong format) and return the string in the right format.
  4. .receive lets you forward the event on the thread you are interested in. In this case the main thread (since you want to update a textfield).
  5. .assign just assigns the new value to the textfield.
superpuccio
  • 11,674
  • 8
  • 65
  • 93
  • Thank you very much. It works good! But now i have a problem, that the text cursor (caret) is not staying at the end of the word after formatting. Example: the input is in textfield "1234" and when i enter "5", the string "12345" triggers the format function and it become something like that "1234-5", so the string become longer, but the cursor (caret) is staying between "-" and "5" like that "1234-|5", so the next char will be spelled not in the end of the number. Maybe you had experienced that, and i will be glad if your help again. – Nikita K. Oct 29 '19 at 10:51
  • @NikitaK. did you find a solution? – G B Jun 11 '20 at 12:15