web-dev-qa-db-ja.com

ユーザーがUITextFieldでパスワードオートフィルをいつ使用したかを検出する方法

IOS 11でパスワードの自動入力をサポートするために必要なすべてのアプリとサーバーの変更を実装しましたが、うまく機能します。もう少し上手くいきたいです。

ユーザー名とパスワードのフィールドはUITextFieldsです。ユーザーが2つのUITextFieldの1つを「自動入力」したことを確認したいので、次のステップに進みます。現在、ユーザーはアイテムを自動入力し、次に進むには、画面キーボードの「次へ」ボタンを押す必要があります。ユーザーに代わってこれをトリガーします。

WWDC2017パスワード自動入力セッションで、UITextFieldTextDidChangeを使用するように指示されています。これは機能しますが、もちろん、ユーザーが手動でこれらのフィールドに入力しているときにもトリガーされます。

私の考えは、以前のバージョンのテキストと新しいバージョンのテキストを比較し、長さがゼロから最小の長さ(2以上)に増加した場合、ユーザーがオートフィルを使用したと想定しています。これはほとんどの場合機能するはずですが、誤ったトリガー(遅いデバイスでの高速タイピング)のリスクがあります。だから、私には、これは危険な仮定かもしれません。

パスワードの自動入力がUITextFieldで使用されているかどうかを確認するためのより確実な方法を誰かが見つけたのか、または誤ったトリガーについての心配が根拠がないと思っているだけなのです。

11
Mitch Cohen

以前の回答がどこかの時点で機能しなくなったかどうかはわかりませんが、機能しません。オートフィルが使用されている場合は、didBeginEditingが1回しか呼び出されません。

しかし、私はオートフィルを検出する方法を見つけました。また、一部の文字が入力された後にオートフィルを使用することもできます。たとえば、ユーザーが電話番号にすでにいくつかの番号を入力している場合は、完全な番号をオートフィルします。

Swift 4の場合:

var rangeReplacedWithSpaceAt: Date?

var rangeReplacedWithSpace: NSRange?

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    // To detect AutoFill, look for two quick replacements. The first replaces a range with a single space.
    // The next replaces the same range with the autofilled content.
    if string == " " {
        self.rangeReplacedWithSpace = range
        self.rangeReplacedWithSpaceAt = Date()
    } else {
        if rangeReplacedWithSpace == range,
            let rangeReplacedWithSpaceAt = self.rangeReplacedWithSpaceAt,
            Date().timeIntervalSince(rangeReplacedWithSpaceAt) < 0.1 {

            DispatchQueue.main.async {
                // Whatever you use to move forward.
                self.moveForward()
            }
        }
        self.rangeReplacedWithSpace = nil
        self.rangeReplacedWithSpaceAt = nil
    }
}
5
Robin Daugherty

解決策を見つけました。

パスワードマネージャーを使用してユーザー名とパスワードを自動入力すると、didBeginEditingが2回トリガーされます。これは、人間ができるよりも速くなります。

そこで、イベント間の時間を計算します。時間が非常に速い場合は、自動入力(FaceIDやTouchIDなど)を使用して資格情報を入力し、次にUIが自動トリガーされると想定します。私の場合は、ユーザーが[サインイン]をタップします。

当然のことながら、監視するUITextFieldsの正しい委任を設定する必要がありますが、これを実行すると、次のようになります。

var biometricAutofillTime: Date!

func textFieldDidBeginEditing(_ textField: UITextField) {
    if biometricAutofillTime != nil {
        if Date().timeIntervalSince(biometricAutofillTime) < 0.1 {
            // NOTE: Need to hesitate for a very short amount of time,
            //        because, otherwise, the second UITextField (password)
            //        won't yet be populated
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { self.didTapSignin() }
        }
        biometricAutofillTime = nil
    }
    biometricAutofillTime = Date()
}
1
Dan

私は別の簡単な方法を見つけました。それがそれを探している人を助けることを願っています。

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Usually string is coming as single character when user hit the keyboard .. 
// but at AutoFill case it will come as whole string which recieved .. 
// at this moment you can replace what you currently have by what you have recieved.
// In below case I'm expecting to recieve 4 digits as OTP code .. you can change it by what you are expecting to recieve.
    if string.count == 4 {
            doWhatEverYouByAutoFillString(text: string)
            return true
        } 
    }
}
0
Mohammed Gomaa

ユーザーに代わってこれをトリガーします。

これがあなたの主な目標である場合、私はここで少し異なるアプローチをしています。

ログインフォームを表示したら、最初にSecRequestSharedWebCredentialを使用してiCloudキーチェーンを確認します。クロージャが資格情報を返した場合、つまりユーザーの意図がそれでログインすることを意味する場合、私は彼/彼女のために自動的にログインします。それ以外の場合は、ログインテキストファイルをbecomeFirstResponder()にします。

このアプローチはサードパーティのパスワードマネージャーをサポートしていませんが、ほとんどの人がiCloudキーチェーンを使用していると思います。

0
ethanhuang13

これは、ユーザーがパスワードを介して自動入力したことを検出します。また、ユーザーがクリップボードからテキストを貼り付けたときにもトリガーされます。 (テキストフィールドが空の場合)

おそらく、このリンクでユーザーが貼り付けたケースを削除するロジックを処理できます。 テキストがUITextViewに貼り付けられたことを知る方法

  private var didAutofillTextfield: Bool = false {
    didSet {
      if didAutofillTextfield {
        // Fire analytics for user autofilling
      }
    }
  }


 func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    // If the range is {0,0} and the string count > 1, then user copy paste text or used password autofill.
    didAutofillTextfield = range == NSRange(location: 0, length: 0) && string.count > 1
    return true
  }
0
Mocha

より良い解決策があるとは思いません。

私が気づいたことの1つは、テキストフィールドが空の場合にのみ自動入力が有効になることです。

そのため、テキストフィールドが空から最小のパスワード/ユーザー名よりも長い長さになった場合、それはおそらくオートフィル/貼り付けです。

UITextFieldの変更を検出するためにshouldChangeCharactersInを使用しています。キーボードからのテキストがデリゲートメソッドが呼び出される前にバッチ処理される場合があるかどうかはわかりません。

0
duncanc4

次のデリゲートメソッドは、ユーザーがテキストフィールドに何かを入力するたびにトリガーされます。その文字列カウントは通常1であるため、それよりも大きい数は、オートフィルまたはユーザーがテキストをフィールドに貼り付けることになります。このパスワードフィールドでこのデリゲートのみを使用しています。

extension LoginView: UITextFieldDelegate {

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if string.count > 1 {
            DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) { [weak self] in
                self?.endEditing(true)
            }
        }
        return true
    }
}
0
José