I want to show text in TextField
and able to edit it. But i'm having a problem while binding it. Here's my code:
struct DataView: View {
@ObservedObject private var dataPresenter = DataPresenter()
var body: some View {
VStack {
TextField("Name", text: $dataPresenter.data?.name ?? "") // This is the error
}
.onAppear(perform: {
dataPresenter.getData()
})
}
}
class DataPresenter: ObservableObject {
@Injected private var getDataInteractor: GetDataInteractor
@Injected private var cancellables: Set<AnyCancellable>
@Published var data: GetDataResp?
func getData() {
getDataInteractor.execute()
.sink { error in
print("Error:", error)
} receiveValue: { response in
if response.errorCode == "00" {
print(response)
self.data = response
}
}
.store(in: &cancellables)
}
}
struct GetDataResp: Codable, Equatable {
var name : String?
var address : String?
var email : String?
enum CodingKeys: String, CodingKey {
case name = "name"
case address = "address"
case email = "email"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decodeIfPresent(String.self, forKey: .name)
address = try values.decodeIfPresent(String.self, forKey: .address)
email = try values.decodeIfPresent(String.self, forKey: .email)
}
}
When I'm not using ?
in $dataPresenter.data.name?? ""
$dataPresenter.data.name?? ""
, I always get an error like this:
Then, when I fix it with ?
, I get more errors like this:
I am stuck in this error cycle, using force unwrap
will crash the app. I have more variables than just name, so I can't just self.name = response.name?? ""
self.name = response.name?? ""
inside the response. How I'm able to fix this?
Any suggestions would be appreciated.
The TextField
needs a Binding
, not just a String
. So, when you offer it ""
in the event that the Binding
is nil, you end up with non-equivalent types ( Binding<String>
vs String
).
To solve this, you'll probably need to create a custom binding that deals with the case of data
being nil
.
Here's one possible solution:
struct DataView: View {
@ObservedObject private var dataPresenter = DataPresenter()
var body: some View {
VStack {
Text(dataPresenter.data?.name ?? "Empty")
TextField("Name", text: dataPresenter.nonOptionalNameBinding())
}
}
}
struct GetDataResp {
var name : String
}
class DataPresenter: ObservableObject {
@Published var data: GetDataResp?
func nonOptionalNameBinding() -> Binding<String> {
.init {
return self.data?.name ?? ""
} set: { newValue in
if self.data == nil {
self.data = GetDataResp(name: newValue)
} else {
self.data?.name = newValue
}
}
}
}
You may need to handle the situation differently in the set:
section based on your needs. And, of course, I stubbed out GetDataResp
, which I'm assuming is more detailed than what I have there, but this should get you started.
Update, based on comments:
struct GetDataResp: Codable, Equatable {
var name : String?
var address : String?
var email : String?
}
struct DataView: View {
@ObservedObject private var dataPresenter = DataPresenter()
var body: some View {
VStack {
Text(dataPresenter.data?.name ?? "Empty")
TextField("Name", text: dataPresenter.nonOptionalBinding(keyPath: \.name))
TextField("Address", text: dataPresenter.nonOptionalBinding(keyPath: \.address))
TextField("Email", text: dataPresenter.nonOptionalBinding(keyPath: \.email))
}
}
}
class DataPresenter: ObservableObject {
@Published var data: GetDataResp?
func nonOptionalBinding(keyPath: WritableKeyPath<GetDataResp,String?>) -> Binding<String> {
.init {
return self.data?[keyPath: keyPath] ?? ""
} set: { newValue in
if self.data == nil {
self.data = GetDataResp()
}
self.data?[keyPath: keyPath] = newValue
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.