mobile-ios/CovidSafe/PhoneNumberViewController.swift

243 lines
10 KiB
Swift
Raw Normal View History

2020-05-08 07:49:14 +00:00
// Copyright © 2020 Australian Government All rights reserved.
import SafariServices
import UIKit
2020-06-19 07:43:33 +00:00
import FlagKit
2020-05-08 07:49:14 +00:00
2020-06-19 07:43:33 +00:00
class PhoneNumberViewController: UIViewController, UITextFieldDelegate, RegistrationHandler, CountrySelectionDelegate {
2020-05-08 07:49:14 +00:00
@IBOutlet weak var phoneNumberField: UITextField!
2020-06-19 07:43:33 +00:00
@IBOutlet weak var countryCodeField: UITextField!
2020-05-08 07:49:14 +00:00
@IBOutlet weak var getOTPButton: UIButton!
@IBOutlet weak var titleLabel: UILabel!
2020-06-19 07:43:33 +00:00
@IBOutlet weak var phoneExample: UILabel!
@IBOutlet weak var phoneError: UILabel!
@IBOutlet weak var phoneLabel: UILabel!
var countryFlagContainerView: UIView!
var flagImageView: UIImageView!
2020-05-08 07:49:14 +00:00
let PHONE_NUMBER_LENGTH = 17 // e.g. "+61 4 12 345 678 " if text is auto-pasted from text message
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
2020-06-19 07:43:33 +00:00
var selectedCountry: Country?
2020-05-08 07:49:14 +00:00
var countryList: [String] = CountriesData.countryArray
// If this view is part of the reauthentiation flow of an expired JWT
var reauthenticating: Bool = false
var registrationInfo: RegistrationRequest?
2020-06-19 07:43:33 +00:00
var initialLabelTextColour: UIColor?
2020-07-03 04:26:13 +00:00
var initialTextFieldBorderColour: UIColor?
2020-05-08 07:49:14 +00:00
override func viewDidLoad() {
super.viewDidLoad()
self.phoneNumberField.addTarget(self, action: #selector(self.phoneNumberFieldDidChange), for: UIControl.Event.editingChanged)
self.phoneNumberFieldDidChange()
phoneNumberField.delegate = self
dismissKeyboardOnTap()
if (reauthenticating) {
2020-06-19 07:43:33 +00:00
self.titleLabel.text = "EnterPhoneReVerify".localizedString(comment: "Enter your mobile number to re-verify")
2020-05-08 07:49:14 +00:00
}
2020-07-03 04:26:13 +00:00
countryCodeField.text = "(+61) " + "country_region_name_au".localizedString()
countryCodeField.accessibilityValue = String.init(format: "SelectedCountryTemplate".localizedString(), "61", "country_region_name_au".localizedString())
let toolBar = UIToolbar()
toolBar.sizeToFit()
let nextBarButtonItem = UIBarButtonItem(title: "Done".localizedString(),
style: .plain,
target: self,
action: #selector(self.dismissKeyboard))
let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
toolBar.setItems([spacer, nextBarButtonItem], animated: true)
toolBar.isUserInteractionEnabled = true
self.phoneNumberField.inputAccessoryView = toolBar
2020-06-05 00:26:40 +00:00
2020-06-19 07:43:33 +00:00
// Set initial Country img
countryFlagContainerView = UIView(frame: CGRect(x: 0, y: 0, width: 60, height: 24))
let flag = Flag(countryCode: "AU")!
let flagImage = flag.originalImage
flagImageView = UIImageView(frame: CGRect(x: 0, y: 2, width: 28, height: 20))
flagImageView.contentMode = .scaleAspectFit
flagImageView.image = flagImage
countryFlagContainerView.addSubview(flagImageView)
2020-07-03 04:26:13 +00:00
2020-06-19 07:43:33 +00:00
// Set View
let chevronImg = UIImage(named: "ChevronRight")
let chevronImgView = UIImageView(frame: CGRect(x: 32, y: 0, width: 24, height: 24))
2020-07-03 04:26:13 +00:00
if UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft {
// change frame of the chevron and flag
chevronImgView.frame = CGRect(x: 0, y: 0, width: 24, height: 24)
flagImageView.frame = CGRect(x: 28, y: 2, width: 28, height: 20)
}
2020-06-19 07:43:33 +00:00
chevronImgView.image = chevronImg
countryFlagContainerView.addSubview(chevronImgView)
countryCodeField.rightView = countryFlagContainerView
countryCodeField.rightViewMode = .always
countryCodeField.delegate = self
initialLabelTextColour = phoneLabel.textColor
2020-07-03 04:26:13 +00:00
initialTextFieldBorderColour = phoneNumberField.borderColor
2020-06-19 07:43:33 +00:00
navigationController?.view.backgroundColor = UIColor.white
2020-05-08 07:49:14 +00:00
}
@IBAction func onBackTapped(_ sender: UIButton) {
self.navigationController?.popViewController(animated: true)
}
@IBAction func nextButtonClicked(_ sender: Any) {
parsePhoneNumberAndProceed(self.phoneNumberField.text ?? "")
}
2020-06-19 07:43:33 +00:00
2020-05-08 07:49:14 +00:00
@objc
func phoneNumberFieldDidChange() {
guard let phoneNumberString = self.phoneNumberField.text else { return }
2020-06-19 07:43:33 +00:00
if (selectedCountry == nil || selectedCountry?.name == "Australia") {
let result = PhoneNumberParser.parse(phoneNumberString, countryCode: "61")
2020-05-08 07:49:14 +00:00
if case .success = result {
self.phoneNumberField.resignFirstResponder()
}
}
}
func parsePhoneNumberAndProceed(_ number: String) {
2020-06-19 07:43:33 +00:00
let result = PhoneNumberParser.parse(number, countryCode: selectedCountry?.phoneCode ?? "61")
2020-05-08 07:49:14 +00:00
switch result {
case .success(let parsedNumber):
activityIndicator.startAnimating()
getOTPButton.isEnabled = false
verifyPhoneNumber(parsedNumber)
case .failure(let error):
2020-06-19 07:43:33 +00:00
let errorAlert = UIAlertController(title: "PhoneNumberFormatErrorTitle".localizedString(),
message: "PhoneNumberFormatErrorMessage".localizedString(),
2020-06-05 00:26:40 +00:00
preferredStyle: .alert)
2020-07-03 04:26:13 +00:00
errorAlert.addAction(UIAlertAction(title: "global_OK".localizedString(), style: .default, handler: { _ in
2020-05-08 07:49:14 +00:00
NSLog("Unable to verify phone number")
}))
present(errorAlert, animated: true)
DLog("Client side phone number verification error: \(error.localizedDescription)")
return
}
}
private func verifyPhoneNumber(_ phoneNumber: String) {
guard self.registrationInfo != nil else {
return
}
self.registrationInfo?.phoneNumber = phoneNumber
2020-06-19 07:43:33 +00:00
self.registrationInfo?.countryPhoneCode = selectedCountry?.phoneCode ?? "61"
2020-05-08 07:49:14 +00:00
PhoneValidationAPI.verifyPhoneNumber(regInfo: self.registrationInfo!) {[weak self] (session, error) in
self?.activityIndicator.stopAnimating()
self?.getOTPButton.isEnabled = true
if let error = error {
2020-06-19 07:43:33 +00:00
let errorAlert = UIAlertController(title: "PhoneVerificationErrorTitle".localizedString(),
message: "PhoneVerificationErrorMessage".localizedString(),
preferredStyle: .alert)
2020-07-03 04:26:13 +00:00
errorAlert.addAction(UIAlertAction(title: "global_OK".localizedString(), style: .default, handler: { _ in
2020-05-08 07:49:14 +00:00
DLog("Unable to verify phone number")
}))
self?.present(errorAlert, animated: true)
DLog("Phone number verification error: \(error.localizedDescription)")
return
}
UserDefaults.standard.set(session, forKey: "session")
self?.performSegue(withIdentifier: "segueFromNumberToOTP", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? OTPViewController {
vc.reauthenticating = self.reauthenticating
vc.registrationInfo = self.registrationInfo
}
2020-06-19 07:43:33 +00:00
if let vc = segue.destination as? SelectCountryViewController {
navigationController?.setNavigationBarHidden(false, animated: false)
vc.countrySelectionDelegate = self
}
2020-05-08 07:49:14 +00:00
}
// limit text field input to 17 characters
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool
{
let maxLength = PHONE_NUMBER_LENGTH
let currentString: NSString = textField.text! as NSString
let newString: NSString =
currentString.replacingCharacters(in: range, with: string) as NSString
return newString.length <= maxLength
}
2020-06-19 07:43:33 +00:00
func textFieldDidEndEditing(_ textField: UITextField) {
if textField == phoneNumberField {
validatePhoneNumber()
}
}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textField == countryCodeField {
phoneNumberField.resignFirstResponder()
performSegue(withIdentifier: "selectCountrySegue", sender: self)
return false
}
return true
}
2020-05-08 07:49:14 +00:00
@objc func action() {
view.endEditing(true)
}
2020-06-19 07:43:33 +00:00
func setCountry(country: Country) {
selectedCountry = country
flagImageView.image = selectedCountry?.flag?.originalImage
guard let countryName = country.name, let countryPhoneCode = country.phoneCode else {
return
}
countryCodeField.text = "(+\(countryPhoneCode)) \(countryName)"
2020-07-03 04:26:13 +00:00
countryCodeField.accessibilityValue = String.init(format: "SelectedCountryTemplate".localizedString(), countryPhoneCode, countryName)
2020-06-19 07:43:33 +00:00
if selectedCountry?.isoCode == "AU2" {
phoneExample.isHidden = false
} else {
phoneExample.isHidden = true
}
validatePhoneNumber()
}
func validatePhoneNumber() {
phoneExample.textColor = initialLabelTextColour
phoneLabel.textColor = initialLabelTextColour
2020-07-03 04:26:13 +00:00
phoneNumberField.borderColor = initialTextFieldBorderColour
2020-06-19 07:43:33 +00:00
phoneError.isHidden = true
let countryCode = selectedCountry?.phoneCode ?? "61"
guard let phoneText = phoneNumberField.text, phoneText.count > 0 else {
return
}
let parseResult = PhoneNumberParser.parse(phoneText, countryCode: countryCode)
switch parseResult {
case .failure( _):
phoneError.isHidden = false
phoneExample.textColor = UIColor.covidSafeErrorColor
phoneLabel.textColor = UIColor.covidSafeErrorColor
2020-07-03 04:26:13 +00:00
phoneNumberField.borderColor = UIColor.covidSafeErrorColor
2020-06-19 07:43:33 +00:00
if selectedCountry == nil || selectedCountry?.isoCode == "AU" {
2020-07-03 04:26:13 +00:00
phoneError.text = "invalid_australian_phone_number_error_prompt".localizedString()
2020-06-19 07:43:33 +00:00
} else if selectedCountry?.isoCode == "AU2" {
2020-07-03 04:26:13 +00:00
phoneError.text = "invalid_norfolk_island_phone_number_error_prompt".localizedString()
}
if UIAccessibility.isVoiceOverRunning {
UIAccessibility.post(notification: .layoutChanged, argument: phoneError)
2020-06-19 07:43:33 +00:00
}
break
default:
break
}
}
}
protocol CountrySelectionDelegate {
func setCountry(country: Country)
2020-05-08 07:49:14 +00:00
}