SwiftUI: TextFieldでCombineを使用し、テキストフィールドの変更に関する更新を取得します。

`Combine`をSwiftUI `TextField`と一緒に使用することで、テキストフィールド `TextField` に入力されたコンテンツユーザーに関する更新を取得することができます。

この記事では、以下について説明します。
- `Combine`を使用して`TextField`の変更を監視する
- ユーザーがテキストフィールドに入力できる文字数の制限
- ユーザーのパスワードに最低限の大文字、記号、数字が含まれているかどうか、更に文字数制限を超えていないかどうかを確認する。

##`Combine`を使用して`TextField`の変更を監視する

コードは簡単です。まず、`Combine` フレームワークをインポートする必要があります。それから `.onReceive(Just(messageText))` ビューモディファイアーを `TextField` へ追加してください。

完全なコードはこちらです：

```swift
import SwiftUI
import Combine

struct ContentView: View {
    @State var messageText: String = ""
    var body: some View {
        Form {
            TextField("Message", text: $messageText)
                .onReceive(Just(messageText)) { _ in
                    // テキストを変更する場合にはこちらにコードを入力して下さい
                }
                .padding()
        }
    }
}
```

これで `.onReceive` ブロックのコードは、テキストコンテンツが変わる度に呼び出されるようになります。

この記事は、上記のコードを使って追加することができるいくつかの機能性についても解説します。

## ユーザーがテキストフィールドに入力できる文字数の制限

ユーザーが SwiftUI `TextField` に入力することのできる文字数を制限する場合があります。もしユーザーが制限を超える文字を入力する場合は、余分なテキストを削除することもあります。

```swift
import SwiftUI
import Combine

struct ContentView: View {
    @State var messageText: String = ""
    var body: some View {
        Form {
            TextField("メッセージ", text: $messageText)
                .onReceive(Just(messageText)) { _ in
                    if messageText.count > 30 {
                        messageText = String(messageText.prefix(30))
                    }
                }
                .padding()
            Text(String(messageText.count))
        }
    }
}
```

## ユーザーに安全なパスワードを選ぶように依頼してください

ユーザーの入力したパスワードが最低でも1つの大文字、1つの記号、数字を含んでいるかどうか、および若干長すぎないかどうか、チェックすることができます。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/94d4b3b8-2455-abf0-d6c0-9472522163a5.gif">

```swift
import SwiftUI
import Combine

struct ContentView: View {
    
    @State var messageText: String = ""
    
    @State var hasCapitalLetter: Bool = false
    @State var hasSymbol: Bool = false
    @State var lengthExceeds16: Bool = false
    
    var body: some View {
        
        Form {
            TextField("メッセージ", text: $messageText)
                .onReceive(Just(messageText)) { _ in
                    self.lengthExceeds16 = (messageText.count > 16)
                    // 大文字
                    let capitalLetterExpression  = ".*[A-Z]+.*"
                    let capitalLetterCondition = NSPredicate(format:"SELF MATCHES %@", capitalLetterExpression)
                    self.hasCapitalLetter = capitalLetterCondition.evaluate(with: messageText)
                    // 数字: ".*[0-9]+.*"
                    // 符号
                    let symbolExpression  = ".*[!&^%$#@()/]+.*"
                    let symbolCondition = NSPredicate(format:"SELF MATCHES %@", symbolExpression)
                    self.hasSymbol = symbolCondition.evaluate(with: messageText)
                }
                .padding()
            // ユーザーによるパスワード入力をご希望の場合は、`SecureField` をご利用下さい。
            Text(String(messageText.count))
                .keyboardType(.asciiCapable)
                .autocapitalization(.none)
                .disableAutocorrection(true)
            // パスワード 条件
            PasswordConditionDisplay(isConditionMet: $hasCapitalLetter, conditionName: "大文字")
            PasswordConditionDisplay(isConditionMet: $hasSymbol, conditionName: "符号")
            PasswordConditionDisplay(isConditionMet: $lengthExceeds16, conditionName: "パスワードは16文字を越える必要があります")
        }
        
    }
    
}

struct PasswordConditionDisplay: View {
    @Binding var isConditionMet: Bool
    var conditionName: String
    var body: some View {
        HStack {
            Image(systemName: isConditionMet ? "circle.fill" : "xmark")
                .padding(.horizontal, 5)
            Text(conditionName)
        }
    }
}
```

---
:relaxed: [Twitter @MszPro](https://twitter.com/MszPro)

:sunny: [私の公開されているQiita記事のリストをカテゴリー別にご覧いただけます。](http://mszpro.com/ioskiji/)
<img width="150" src="https://cdn.mszmagic.com/static-web-content/AppClipImage_small.png">