Skip to content

Instantly share code, notes, and snippets.

@jbnunn
Last active January 15, 2024 02:02
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jbnunn/dab656b53f6e4ee2f53730f0b8daee64 to your computer and use it in GitHub Desktop.
Save jbnunn/dab656b53f6e4ee2f53730f0b8daee64 to your computer and use it in GitHub Desktop.
SwiftUI + PhoneNumberKit implementation
import SwiftUI
import UIKit
import PhoneNumberKit
struct PhoneNumberTextFieldView: UIViewRepresentable {
@Binding var phoneNumber: String
private let textField = PhoneNumberTextField()
func makeUIView(context: Context) -> PhoneNumberTextField {
textField.withExamplePlaceholder = true
//textField.font = UIFont(name: GlobalConstant.paragraphFont.rawValue, size: 17)
textField.withFlag = true
textField.withPrefix = true
// textField.placeholder = "Enter phone number"
textField.becomeFirstResponder()
return textField
}
func getCurrentText() {
self.phoneNumber = textField.text!
}
func updateUIView(_ view: PhoneNumberTextField, context: Context) {
}
}
struct SignUpSignInView: View {
@State private var phoneNumber = String()
@State private var validationError = false
@State private var errorDesc = Text("")
@State private var phoneField: PhoneNumberTextFieldView?
let phoneNumberKit = PhoneNumberKit()
var body: some View {
ZStack {
VStack(alignment: .leading) {
Text("Sign in with your phone number")
self.phoneField
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 60)
.keyboardType(.phonePad)
Button(action: {
do {
self.phoneField?.getCurrentText()
print("phone is: \(self.phoneNumber)")
let validatedPhoneNumber = try self.phoneNumberKit.parse(self.phoneNumber)
print("Validated Number: \(validatedPhoneNumber)")
// Integrate with your login/registration system here...
}
catch {
self.validationError = true
self.errorDesc = Text("Please enter a valid phone number")
}
}) {
Text("Sign in")
}
.padding(15)
Spacer()
}
.padding()
.onAppear {
self.phoneField = PhoneNumberTextFieldView(phoneNumber: self.$phoneNumber)
}
.alert(isPresented: self.$validationError) {
Alert(title: Text(""), message: self.errorDesc, dismissButton: .default(Text("OK")))
}
}
}
}
@dustinsenos
Copy link

Thanks for the example!

.keyboardType(.numberPad) could also be .keyboardType(.phonePad).

@akacaptain
Copy link

Awesome. Thanks!

@mroushdy
Copy link

were you guys able to show the country code picked?

@akacaptain
Copy link

I actually wasn't able to get J. Nunn's implementation to work. The button closure always executed before self.phoneNumber was updated in the updateUIView() function. I had to implement a separate function to explicitly update self.phoneNumber before using it in the parent SwiftUI view. And yes, the phone number field correctly displays country codes, country flags, etc.

You can see my gist here https://gist.github.com/akacaptain/58cc0f8073f436dabc869ed2aa6f3830

@Fivenn
Copy link

Fivenn commented Jun 17, 2020

Thanks for the example, you saved me a lot of time!

@saroar
Copy link

saroar commented Jul 11, 2020

always get Error validating phone number but my phone number is valid same way use with swift UIkit

@CodyBontecou
Copy link

The code here doesn't properly bind and to my state. When I tried to print my phoneNumber, it would only show +1.

I actually wasn't able to get J. Nunn's implementation to work. The button closure always executed before self.phoneNumber was updated in the updateUIView() function. I had to implement a separate function to explicitly update self.phoneNumber before using it in the parent SwiftUI view. And yes, the phone number field correctly displays country codes, country flags, etc.

You can see my gist here https://gist.github.com/akacaptain/58cc0f8073f436dabc869ed2aa6f3830

This ended up doing the trick, thank you.

@Mr-Perfection
Copy link

Thank you so much! This worked!!! <3

I actually wasn't able to get J. Nunn's implementation to work. The button closure always executed before self.phoneNumber was updated in the updateUIView() function. I had to implement a separate function to explicitly update self.phoneNumber before using it in the parent SwiftUI view. And yes, the phone number field correctly displays country codes, country flags, etc.

You can see my gist here https://gist.github.com/akacaptain/58cc0f8073f436dabc869ed2aa6f3830

@jbnunn
Copy link
Author

jbnunn commented Jul 15, 2020

Thank you @akacaptain, I updated the Gist here with your corrections.

@Mr-Perfection
Copy link

Mr-Perfection commented Jul 16, 2020

I used this implementation in .sheet(..) which is a modal. if your physical app is crashing for some reason in .sheet(), make sure to remove textField.becomeFirstResponder(). its swiftUI bug apparently when I did some research.

@CodyBontecou
Copy link

I have a login view and a signup view. The login view has a button to signup, the signup view has a button to go back to the login view.

If I go from login -> signup, this field works.

But if I go from login -> signup -> login -> signup, the text field no longer recognizes my input and believes it's always empty. Any ideas as to why this is happening?

@roip890
Copy link

roip890 commented Aug 14, 2020

Hi, I want to suggest an improvement that more compatible to SwiftUI:

import SwiftUI
import UIKit
import PhoneNumberKit

struct PhoneNumberField: UIViewRepresentable {
    
    @Binding var phoneNumber: String
    private let textField = PhoneNumberTextField()
 
    func makeUIView(context: Context) -> PhoneNumberTextField {
        textField.withExamplePlaceholder = true
        textField.withFlag = true
        textField.withPrefix = true
        textField.placeholder = "Phone"
        textField.becomeFirstResponder()
        textField.addTarget(context.coordinator, action: #selector(Coordinator.onTextUpdate), for: .editingChanged)
        return textField
    }
    
    func updateUIView(_ view: PhoneNumberTextField, context: Context) {

    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, UITextFieldDelegate {

        var control: PhoneNumberField

        init(_ control: PhoneNumberField) {
            self.control = control
        }

        @objc func onTextUpdate(textField: UITextField) {
            self.control.phoneNumber = textField.text!
        }

    }

}

@fogonthedowns
Copy link

Hi, I want to suggest an improvement that more compatible to SwiftUI:

import SwiftUI
import UIKit
import PhoneNumberKit

struct PhoneNumberField: UIViewRepresentable {
    
    @Binding var phoneNumber: String
    private let textField = PhoneNumberTextField()
 
    func makeUIView(context: Context) -> PhoneNumberTextField {
        textField.withExamplePlaceholder = true
        textField.withFlag = true
        textField.withPrefix = true
        textField.placeholder = "Phone"
        textField.becomeFirstResponder()
        textField.addTarget(context.coordinator, action: #selector(Coordinator.onTextUpdate), for: .editingChanged)
        return textField
    }
    
    func updateUIView(_ view: PhoneNumberTextField, context: Context) {

    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, UITextFieldDelegate {

        var control: PhoneNumberField

        init(_ control: PhoneNumberField) {
            self.control = control
        }

        @objc func onTextUpdate(textField: UITextField) {
            self.control.phoneNumber = textField.text!
        }

    }

}

Wow! This is exactly what I spent hours researching - nice idea adding a separate Class

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment