How to convert a String to generic T, where T is unconstrainted?

I need to write generic code to work with some SwiftUI.TextField of this kind:

TextField.init<S, T>(_ title: S, value: Binding, formatter: Formatter, onEditingChanged: @escaping (Bool) -> Void = { _ in }, onCommit: @escaping() -> Void = {}) where S : StringProtocol

basically I need whatever magic the TextField does inside to convert String to T, TextField must be doing this inside to convert the input String value to T and assign to a binding. Something kind of like this:

import SwiftUI

// I need something like this that work for any T
func convert<T>(_ s: String, _ b: Binding<T>) {
    // this is what I need:
    // how to convert s to T? so we can do this:
//    b.wrappedValue = s as! T   // of course this won't work, will crash
    // so for illustration, I'm just going to use `Double`
    b.wrappedValue = Double(s) as! T
}


var d: Double = Double.nan

let binding = Binding<Double>(get: { d }, set: { d = $0 })

// to be use like this: need to work for any Binding<T> where T is unconstraint
convert("123.456", binding)
print(d)

How to make convert work?

this is actually my code, look to the end of this code to see where my problem is:

import SwiftUI
import Introspect


extension View {
    func addDecimalAndDoneKeyboardToolbar<T>(_ value: Binding<T>) -> some View {
        modifier(AddDecimalAndDoneKeyboardToolbar(value: value))
    }
}

/// Use like this to add keyboard toolbar to a TextField:
/// ```
/// @State var value: Double
/// TextField("Enter a number", value: $value, formatter: NumberFormatter())
///     .addDecimalAndDoneKeyboardToolbar($value)
///     .keyboardType(.numberPad)
struct AddDecimalAndDoneKeyboardToolbar<T>: ViewModifier {
    @Binding var value: T

    func body(content: Content) -> some View {
        content
            .introspectTextField(customize: addToolbar)
    }


    // for keeping a strong ref when this created inside `addToolbar(...)`
    @State var buttonHandler: ButtonHadler<T>?

    // add a decimal point button to input a "."
    // add a done button to commit textField input and dismiss keyboard
    func addToolbar(to textField: UITextField) {
        let toolBar = UIToolbar(
            frame: CGRect(
                origin: .zero,
                size: CGSize(width: textField.frame.size.width, height: 44)
            )
        )
        let flexButton = UIBarButtonItem(
            barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace,
            target: nil,
            action: nil
        )

        buttonHandler = ButtonHadler(textField, $value)

        let decimalButton = UIBarButtonItem(
            title: " • ",
            style: .plain,
            target: buttonHandler,     // or self, action is not called :(
            action: #selector(buttonHandler?.decimalPointAction(_:))    // <<<<< why is this not called?
        )


        let doneButton = UIBarButtonItem(
            title: "Done",
            style: .done,
            target: buttonHandler,     // or self, action is not called :(
            action: #selector(buttonHandler?.doneAction(_:))    // <<<<< why is this not called?
        )

        toolBar.setItems([decimalButton, flexButton, doneButton], animated: true)
        textField.inputAccessoryView = toolBar
    }


    final class ButtonHadler<T> {
        let textField: UITextField
        @Binding var field: T

        init(_ t: UITextField, _ f: Binding<T>) {
            textField = t
            _field = f
        }

        @objc func decimalPointAction(_ button: UIBarButtonItem) -> Void {
            textField.text = (textField.text ?? "0") + "."
        }

        @objc func doneAction(_ button: UIBarButtonItem) -> Void {
            // 😳😩👇👇
            // problem here: how to convert a string to T? Don't even know what T is
            // ideally, I want to to be unconstraint, just like TextField
            // what to do here?
            field = (textField.text ?? "0") as! T   // this will crash at runtime! won't work!!!
            textField.resignFirstResponder()
        }
    }

}

How does the TextField do the convert from String to T? It might be using the Formatter, if so, I'm willing to pass the formatter into my code.

It uses Formatter.

Edit: I got some help here.

Terms of Service

Privacy Policy

Cookie Policy