Reputation: 1389
I've noticed that the one-time-password from keyboard suggestions sometimes fails to paste into my text field in my SwiftUI iOS app. Most of the time it works, but sometimes the first time I click the suggestion it doesn't paste. Then I try to click the suggestion a second time, and luckily that second time always seems to work.
I think it might be an issue with iOS 17, since I've only experienced the issue on devices running versions of iOS 17. I have never experienced it on iOS 15 or iOS 16.
Has anyone else experienced this issue? Anyone have any solutions?
Here's the code for my verification code field, if it's of use:
struct VerificationCodeField: View {
// Adapted from: https://medium.com/flawless-app-stories/swiftui-passcode-field-for-otp-and-pin-entry-b61ba663dc31
let maxDigits = 6
let showCodeShowingToggle = false
@Binding var code: String
@State var showCode = true
@State var isDisabled = false
var onFinishedEntry:
(_ code: String, _ onVerifiedCode: @escaping (_ didSucceed: Bool) -> Void) -> Void
var body: some View {
VStack(spacing: 10) {
ZStack {
codeBoxes
codeBackgroundField
}
if showCodeShowingToggle {
codeShowingToggle
}
}
}
private var codeBoxes: some View {
HStack {
Spacer()
ForEach(0..<maxDigits, id: \.self) { index in
Image(systemName: getImageName(at: index))
.resizable()
.font(.body.weight(.thin))
.aspectRatio(1, contentMode: .fit)
.foregroundColor(.reelspaceLightGray)
Spacer()
}
}
}
private var codeBackgroundField: some View {
TextField("", text: $code, onCommit: submitCode)
.font(.system(size: 35))
.accentColor(.clear) // Prevent cursor from appearing in iOS 15
.tint(.clear) // Prevent cursor from appearing in iOS 16+
.foregroundColor(.clear)
.textContentType(.oneTimeCode)
.keyboardType(.numberPad)
.disabled(isDisabled)
.onChange(of: code) { _ in submitCode() }
}
private var codeShowingToggle: some View {
HStack {
Spacer()
if !code.isEmpty {
codeShowingButton
}
}
.frame(height: 20)
.padding([.trailing])
}
private var codeShowingButton: some View {
Button(action: {
showCode.toggle()
}, label: {
showCode ?
Image(systemName: "eye.slash.fill").foregroundColor(.primary) :
Image(systemName: "eye.fill").foregroundColor(.primary)
})
}
private func submitCode() {
if code.count == maxDigits {
UIApplication.shared.closeKeyboard()
isDisabled = true
onFinishedEntry(code) { verifiedCode in
if !verifiedCode {
code = ""
}
isDisabled = false
}
}
// This code is never reached under normal circumstances. If the user pastes a text with count
// higher than the max digits, we remove the extra leading characters and make a recursive call.
if code.count > maxDigits {
code = String(code.suffix(maxDigits))
submitCode()
}
}
private func getImageName(at index: Int) -> String {
if index >= code.count {
return "square"
}
if showCode {
return code.digits[index].numberString + ".square"
}
return "square.fill"
}
}
private extension String {
var digits: [Int] {
// Map characters to digits, defaulting to 0 if character is not a digit.
map { Int(String($0)) ?? 0 }
}
}
private extension Int {
var numberString: String {
guard self < 10 else { return "0" }
return String(self)
}
}
Upvotes: 1
Views: 298