I have a view and I want to load the data from the database from the start of the view, because I want to edit the profile of my user.
So whenever I will start the my view, the data will be loaded.
This is my code but it gives me an exception on the TextField.
struct ProfileView: View {
@State var myUser = User()
var repo = myRepo()
var body: some View {
VStack{
Form{
Section(header: Text("edit the name")){
TextField("Nume produs", text: self.myUser.name) //IT DOES NOT WORK HERE
}
}
}.onAppear(){
getMyUser()
}
func getMyUser(){
Task{
do{
try await repo.getUserProfile().watch(block: {item in
self.myUser = item as! User
})
}catch{
print("error")
}
}
}
}
This just does not work when I put as a TextField What is the best way to have the data of my object (myUser) right away on the start of the view?
CodePudding user response:
You can show a ProgressView
in the interim.
struct ProfileView: View {
//mimic user, make variable optional until value is received
@State var myUser : String?
//var repo = myRepo() //No code provided
var body: some View {
//Check the optional, if a value is available show Custom View
if let m = Binding($myUser){
VStack{
Form{
Section(header: Text("edit the name")){
TextField("Nume produs", text: m) //use `m.name` in your code
}
}
}
}else{ //else show progress view, and fetch the user
ProgressView()
.onAppear(){
getMyUser()
}
}
}
func getMyUser(){
Task{
do{
//To mimic delay replace with your code
try await Task.sleep(for: .seconds(2))
//The code you have here is not async await, closures do not work well with async await, it could be the source of your issues.
// try await repo.getUserProfile().watch(block: {item in
// self.myUser = item as! User
// })
myUser = "test" //mimic
}catch{
print(error)
}
}
}
}
CodePudding user response:
Just init the String
as empty and create the @Binding
like so:
import SwiftUI
struct ProfileView: View {
@State var myUser = ""
var body: some View {
VStack{
Form{
Section(header: Text("edit the name")){
TextField("Nume produs", text: $myUser) //IT DOES NOT WORK HERE
}
}
}.onAppear(){
getMyUser()
}
}
func getMyUser() {
Task{
do{
try await Task.sleep(nanoseconds: 3_000_000_000)
myUser = "HELLO"
}catch{
print("error")
}
}
}
}
CodePudding user response:
If you need the data to be there when the view appears, you could load the data when a button is tapped on the previous screen and you could navigate to the details screen once the data is loaded.
Note: this example uses a depreciated NavigationLink initializer, you could use navigation stacks instead if you’re targeting iOS 16
class Model: ObservableObject {
var repo = myRepo()
@Published var user: User? = nil
@Published bar shouldShowUserView = false
init() {
$user
.map { $0 != nil }
.assign(to: &$shouldShowUserView)
}
func fetchUser() {
Task{
do {
try await repo.getUserProfile().watch(block: { item in
self.myUser = item as! User
})
} catch {
print("error")
}
}
}
func reset() {
user = nil
}
}
struct ProfileView: View {
@EnvironmentObject var model: Model
var body: some View {
VStack{
Form {
Section(header: Text("edit the name")){
TextField("Nume produs", text: $model.myUser.name) //IT DOES NOT WORK HERE
}
}
}
.onDisappear(perform: model.reset)
}
}
struct PreviousView: View {
@EnvironmentObject var model: Model
var body: some View {
ZStack {
NavigationLink(destination: { ProfileView() }, isActive: $model.shouldShowUserView, label: { EmptyView() })
Button(action: {
model.fetchUser()
}, label: {
Text(“Show user details”)
})
}
}
}