I am trying to fetch all the replies for a particular Tweet. I am using Firestore to persist the tweets, replies etc. Below is the replies property inside the Tweet struct. I always checked and the compactMap function does return an array of Tweets.
var replies: [Tweet] {
get async {
let documents = try? await documentRef?.collection("replies").getDocuments().documents
if let documents {
// I checked and it does return replies as I can print them on the terminal
return documents.compactMap { doc in
return try? doc.data(as: Tweet.self)
}
}
return []
}
}
But inside the SwiftUI, when I assign and print out the self.replies property it comes as an empty array.
struct TweetDetailView: View {
let tweet: Tweet
@State private var replies: [Tweet] = []
init(tweet: Tweet) {
self.tweet = tweet
setup()
}
private func setup() {
Task {
self.replies = await self.tweet.replies
print(self.replies) // This prints an empty array WHY
}
}
Any ideas?
CodePudding user response:
The issue in your example doesn't actually have anything to do with the async code -- it has to do with how @State
is set. For example, this code, without anything async, suffers from the same "issue":
struct ContentView: View {
@State private var count = 0
init() {
launchTask()
}
func launchTask() {
let incremented = self.count 1
self.count = incremented
print(self.count)
}
var body: some View {
VStack {
Text("hello, world")
}
.frame(width: 500, height: 500)
}
}
But, there are a couple things going wrong here:
- Most directly, you cannot and should not depend on
@State
changing is value immediately after setting it -- it will be set on the next render loop. For example, if you printincremented
in my example, it'll be the value one expects. In your example, you could do:
var result = await self.tweet.replies
print(result)
self.replies = result
And, it too, will print the expect result (assuming your debugging is right and the async getter is indeed returning results).
- The second issue, although it doesn't relate directly to your problem, is that you should never do any 'work' in
init
in aView
in SwiftUI. Instead, use the.task { ... }
or.onAppear { ... }
modifiers to launch theTask
. Otherwise, your function will run on every init of theView
, which could be many many times.