Cannot compile SwiftUI ForEach Table - foreach

I'm trying to make a table view from dynamic array, but compiler cannot build it with unrelated error:
The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
struct RecentView: View {
#State var recentRooms: [[String: String]] = []
#Binding var recentIsPressed: Bool
#Binding var nextIsPressed: Bool
#Binding var conferenceName: String
let screenWidth = UIScreen.main.bounds.size.width
var body: some View {
if let recent = UserDefaults.standard.array(forKey: "recentRooms") as? [[String: String]] {
recentRooms = recent
}
return VStack {
...
VStack(alignment: .leading) {
ForEach(0 ..< recentRooms.count) { i in
Button(action: { self.meetingSelected(name: self.recentRooms[i]["name"] ?? "") }) {
HStack {
ZStack {
Circle()
.foregroundColor(Color("ProjectBlue"))
.frame(width: 76, height: 76)
Text(self.recentRooms[i]["short_name"] ?? "")
.font(.custom("Roboto-Bold", size: 36))
.foregroundColor(Color("AlmostWhite"))
}
VStack(alignment: .leading) {
Text(self.recentRooms[i]["name"] ?? "")
.font(.custom("OpenSans-Bold", size: 17))
.foregroundColor(Color("AlmostWhite"))
Text(self.recentRooms[i]["date"] ?? "")
.font(.custom("OpenSans-Regular", size: 17))
.foregroundColor(Color("AlmostWhite"))
Text(self.recentRooms[i]["duration"] ?? "")
.font(.custom("OpenSans-Regular", size: 17))
.foregroundColor(Color("AlmostWhite"))
}
}
}
.frame(width: self.screenWidth, alignment: .leading)
.padding(.leading, 20)
}
}
}
}
private func meetingSelected(name: String) {
conferenceName = name
nextIsPressed = true
recentIsPressed = false
}
}
Any suggestions on what is wrong with the ForEach part?

This variant of ForEach shouldn't be used with a dynamic content.
ForEach(0 ..< recentRooms.count)
If the recentRooms variable can be changed, use this instead:
ForEach(recentRooms, id: \.self)

Related

SwiftUI Change icon by number

I want the Images to change according to the text, but it works over .degisim String and int value in the ready-made code block I found. That's why it doesn't work for String.
import SwiftUI
struct altin: View {
#State var users: [Altin] = []
var body: some View {
ZStack{
List(users) { altin in
NavigationLink {
detayView(altinKur: altin)
} label: {
HStack{
Image(systemName: getImage(percent: Int(altin.degisim)))
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 9, height: 9)
.foregroundColor(
(altin.degisim).contains("-") ?
Color.down :
Color.up)
.padding(.trailing, 5)
Text(altin.degisim)
.font(.system(size: 16))
.fontWeight(.medium)
.foregroundColor(
(altin.degisim).contains("-") ?
Color.down :
Color.up)
}
}
}
}
.listStyle(PlainListStyle())
.padding(0)
.onAppear {
apiCall().getUsers { (users) in
self.users = users
}
}
}
}
struct altin_Previews: PreviewProvider {
static var previews: some View {
altin()
}
}
func getImage(percent: Int) -> String{
if percent > 0 {
return "chevron.up"
}else{
return "chevron.down"
}
}
.foregroundcolor works but not within Image.
The code is more complex, I simplified it for solution.
Model:
struct Altin: Codable, Identifiable {
let id = UUID()
let name: String
let alis: String
let satis: String
let degisim: String
}

Passing data from firebase to textfield

I'm having difficulties with the textfields. I'm fetching the user data from Firebase RTDB and i want to display some of the data in a profile view which contains textfields for the ability to edit the data.
My UserData:
struct UserData: Codable, Identifiable {
var email : String?
var name : String?
var firstname : String?
var lastname : String?
var type: String?
var uid: String?
var profileImageUrl : String?
var id : String?
var fcmToken2 : String?
var onboarding : Bool?
var phone : String?
}
The View with TextFields:
struct ProfileViewDemo: View {
#Binding var user : UserData
#State private var email: String = ""
var body: some View {
TextField("\(user.firstname!)", text: $email, onEditingChanged: { edit in
self.editing = edit
})
}
}
How do i preset the email var to the value of user.email? I tried using .onAppear, but that caused problem with the photo loading. Are there any ways to preset the email var with the data from UserData? Thanks.
EDIT (Adding the ProfileView code)
struct ProfileViewDemo: View {
#ObservedObject var session : SessionStore
#Binding var user : UserData
#State private var number: String = ""
#Binding var email: String = "123"
#State private var editing = false
#State private var editing2 = false
#State private var editing3 = false
#State private var editing4 = false
#State var refresh: Bool = false
#State private var image: UIImage?
#State private var shouldPresentImagePicker = false
#State private var shouldPresentActionScheet = false
#State private var shouldPresentCamera = false
var body: some View {
ScrollView{
VStack{
VStack{
ZStack{
ZStack{
if(image == nil){
KFImage.url(URL(string: "\(user.profileImageUrl!)"))
.loadDiskFileSynchronously()
.cacheMemoryOnly()
.fade(duration: 0.25)
.onProgress { receivedSize, totalSize in }
.onSuccess { result in }
.onFailure { error in }
.resizable()
.aspectRatio(contentMode: .fill)
.clipShape(Circle())
.overlay(Circle().stroke(Color(red: 50 / 255, green: 51 / 255, blue: 53 / 255), lineWidth: 2))
.frame(width: 140, height: 140)
}
if (image != nil) {
Image(uiImage: image!)
.resizable()
.aspectRatio(contentMode: .fill)
.clipShape(Circle())
.overlay(Circle().stroke(Color(red: 50 / 255, green: 51 / 255, blue: 53 / 255), lineWidth: 2))
.frame(width: 140, height: 140)
}
}
ZStack{
Button {
self.shouldPresentActionScheet = true
} label: {
Image(systemName: "camera")
.font(.system(size: 12))
.padding(.all, 5)
.background(Color(red: 50 / 255, green: 51 / 255, blue: 53 / 255))
.foregroundColor(Color.white)
.clipShape(Circle())
}
.sheet(isPresented: $shouldPresentImagePicker) {
SUImagePickerView(sourceType: self.shouldPresentCamera ? .camera : .photoLibrary, image: self.$image, isPresented: self.$shouldPresentImagePicker)
}.actionSheet(isPresented: $shouldPresentActionScheet) { () -> ActionSheet in
ActionSheet(title: Text("Choose mode"), message: Text("Please choose your preferred mode to set your profile image"), buttons: [ActionSheet.Button.default(Text("Camera"), action: {
self.shouldPresentImagePicker = true
self.shouldPresentCamera = true
}), ActionSheet.Button.default(Text("Photo Library"), action: {
self.shouldPresentImagePicker = true
self.shouldPresentCamera = false
}), ActionSheet.Button.cancel(Text("Atšaukti"))])
}
}
.padding(.all, 3)
.background(Color.white)
.clipShape(Circle())
.offset(x: 50)
.offset(y: 50)
}
}
.padding(.bottom, 15)
ZStack {
ZStack(alignment: .leading){
ZStack(alignment: .leading){
TextField("\(user.email!)", text: $email, onEditingChanged: { edit in
self.editing3 = edit
})
.padding(.leading, 5)
.padding(.trailing, 5)
.padding()
.offset(y: 0)
}.frame(maxWidth: .infinity, maxHeight: 50)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(editing3 ? Color(UIColor.systemGray2) : Color(UIColor.systemGray5), lineWidth: 2)
)
.background(Color(UIColor.white))
.cornerRadius(5)
Text("El. Pašto adresas")
.disableAutocorrection(true)
.padding(.leading, 5)
.padding(.trailing, 5)
.background(Color.white)
.font(Font.custom("Montserrat-Regular", size: 15.0))
.foregroundColor(Color.gray)
.padding()
.offset(y: -25)
}
.frame(maxWidth: .infinity, maxHeight: 50)
}
.padding(.bottom, 15)
}
.padding()
.navigationBarItems(trailing:
Button("Išsaugoti") {
print("Presed Profilio Informacija išsaugota!")
updateProfileInfo()
UIApplication.shared.endEditing()
})
}
.onTapGesture {
self.endTextEditing()
}
}
func updateProfileInfo () {
if (image != nil) {
session.uplaodImage(image!) { (url) in
if let url = url {
session.updateProfileImage(url: url)
print("Profile Image updated")
}
}
}
if (image == nil){
print("Skipping image update - empty")
}
refresh.toggle()
}
}
Code to fetch UserData:
func fetchUsers(){
ref.child("users").observe(.childAdded) { (snapshot) in
guard let dictionary = snapshot.value as? [String: AnyObject] else { return}
var user = UserData()
user.email = (dictionary["email"] as! String)
user.name = (dictionary["name"] as! String)
user.firstname = (dictionary["firstname"] as! String)
user.lastname = (dictionary["lastname"] as! String)
user.type = (dictionary["type"] as! String)
user.uid = (dictionary["uid"] as! String)
user.profileImageUrl = (dictionary["profileImageUrl"] as! String)
user.id = snapshot.key
user.fcmToken2 = (dictionary["fcmToken"] as! String)
user.phone = (dictionary["phone"] as! String)
user.onboarding = (dictionary["onboarding"] as! Bool)
self.users.append(user)
}
}
Use OnAppear function on the View and assign values or Fire your API call from there.
struct ProfileViewDemo: View {
#Binding var user : UserData
#Binding var email: String = ""
var body: some View {
if email == "" {
TextField("Enter email", text: $email)
}
else {
TextField("", text: $email)
}
}.onAppear(perform: {
email = user.email
//You can fire your API call in here and assign the values
}
}

Update each button item inside a list instead of all buttons at once in Swift / SwiftUI

I'm currently trying to change a button from one view to another depending on what the user presses. They would get a list/scrollview of pending requests and if the user clicks on accept, it should change that button to an accepted button and if the user clicks on reject, it should change that button to a rejected button. I'm running into a problem where if a user clicks on accept, all the pending request buttons gets changed to an accepted button and similarly for the reject case.
import SwiftUI
import Firebase
import SDWebImageSwiftUI
import Foundation
import Combine
struct BottomSheet: View {
// #ObservedObject var userData : UserViewModel
var edges = UIApplication.shared.windows.first?.safeAreaInsets
#StateObject var userData = UserViewModel()
#State var declinedRequest: Bool = false
#State var acceptedRequest: Bool = false
var body: some View {
VStack{
Spacer()
VStack(spacing: 12){
Divider()
// here are the buttons inside the scrollview
ScrollView{
ForEach(userData.pendingFriendUsers){ person in
HStack{
if person.pic != ""{
WebImage(url: URL(string: person.pic)!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 60, height: 60)
.clipShape(Circle())
.padding(.leading, 30)
.padding(.trailing, 10)
}else{
Circle()
.stroke(Color.black.opacity(0.8), lineWidth: 2)
.frame(width: 60, height: 60)
.padding(.leading, 20)
.padding(.trailing, 10)
}
VStack(alignment: .leading){
Text("\(person.name)")
.font(.custom("Helvetica Neue", size: 16))
.foregroundColor(Color.white).bold()
Text("#\(person.username)")
.font(.custom("Helvetica Neue", size: 16))
.foregroundColor(Color.white)
.opacity(0.8)
}
Spacer()
if person.isFriends == 2 {
RoundedRectangle(cornerRadius: 15, style: .continuous)
.fill(Color("Dark-Grey"))
.frame(width: 150, height: 40)
.padding(.trailing, 25)
.overlay(
Text("Request removed")
.font(.custom("Helvetica Neue", size: 14))
.foregroundColor(Color.white)
.padding(.trailing, 25)
)
}else if person.isFriends == 1 {
RoundedRectangle(cornerRadius: 15, style: .continuous)
.fill(Color.white)
.frame(width: 150, height: 40)
.padding(.trailing, 25)
.overlay(
Text("Request accepted")
.font(.custom("Helvetica Neue", size: 14))
.foregroundColor(Color.black)
.padding(.trailing, 25)
)
}else {
Button(action: {
print("declined friend request for \(person.uid)")
withAnimation(){declinedRequest = true}
userData.declineFriendRequest(otherUserUID: person.uid)
}){
RoundedRectangle(cornerRadius: 12, style: .continuous)
.fill(Color("Dark-Grey"))
.frame(width: 55, height: 26.5)
.padding(.trailing, 13)
.overlay(
Image("x")
.renderingMode(.template)
.resizable()
.foregroundColor(Color.white)
.opacity(0.8)
.frame(width: 12, height: 12)
.padding(.trailing, 12)
)
}
Button(action: {
print("accepted friend request for \(person.uid)")
withAnimation(){acceptedRequest = true}
userData.acceptFriendRequest(otherUserUID: person.uid)
}){
RoundedRectangle(cornerRadius: 15, style: .continuous)
.fill(Color.white)
.frame(width: 50, height: 25)
.padding(.trailing, 35)
.overlay(
Image("check")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 20, height: 20)
.padding(.trailing, 33)
)
}
}
}.padding(.top, 12.5)
}
}
Spacer()
.contentShape(Rectangle())
}
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height / 1.15)
.padding(.top)
.background(Color("gray")
.clipShape(CustomCorner(corners: [.topLeft,.topRight])))
.offset(y: offset)
// bottom sheet remove swipe gesture....
.gesture(DragGesture().onChanged(onChanged(value:)).onEnded(onEnded(value:)))
.offset(y: showSheet ? 0 : UIScreen.main.bounds.height)
}
.ignoresSafeArea()
.background(
Color.black.opacity(showSheet ? 0.3 : 0).ignoresSafeArea()
.onTapGesture(perform: {
withAnimation{showSheet.toggle()}
})
)
}
}
When declinedRequest or acceptedRequest get modified, its not mapped to the button so everything in the for view gets changed instead of the individual button. I've added my basic code below but some things I tried are making a published variable in my user data model class but it doesn't get updated in the for each as it moves on by then and I also tried making a published variable here. It looks like I have something to do with indices and mapping but I'm unsure what the best approach is. Thanks for your help
Edit:
Here's relevant sections from my user view model:
import SwiftUI
import Firebase
import Combine
import Foundation
struct pendingFriendUser: Identifiable {
var id: Int
var uid: String
var name: String
var username: String
var pic: String
var isFriends: Int
init(id: Int, uid: String, name: String, username: String, pic: String, isFriends: Int){
self.id = id
self.uid = uid
self.name = name
self.username = username
self.pic = pic
self.isFriends = isFriends
}
}
class UserViewModel : ObservableObject{
#Published var userInfo = UserModel(username: "", pic: "", name: "", age: 1, uid: "", phoneNumber: "")
let ref = Firestore.firestore()
let uid = Auth.auth().currentUser!.uid
#Published var pendingFriendUsers: [pendingFriendUser]
//show add friends sheet
#AppStorage("showSheet") var showSheet = false
//check friendship variabe
#Published var isFriend = 0
init() {
self.searchedUsers = []
self.pendingFriendUsers = []
fetchUser(uid: uid) { (user) in
self.userInfo = user
}
}
func getPendingRequests(){
//check if friends has any false memberships
var pendingFriendRequests: [String: Bool] = [:]
self.ref.collection("Users").document(uid).getDocument(){
(document, err) in
if let err = err {
print("Error getting documents \(err)")
} else {
if document!.data()!["friends"] != nil {
pendingFriendRequests = document!.data()!["friends"] as! [String : Bool]
}
//filter based on false pending friend requests
self.pendingFriendUsers.removeAll()
var friendUserID = 0
for key in pendingFriendRequests.keys {
if pendingFriendRequests[key] == false {
self.ref.collection("Users").document(key).getDocument(){
(friendDocument, err) in
if let err = err {
print("Error getting documents \(err)")
} else {
let pendingFriendUsername = (friendDocument?.data()?["username"]) as! String
let pendingFriendUID = (friendDocument?.data()?["uid"]) as! String
let pendingFriendName = (friendDocument?.data()?["name"]) as! String
let pendingFriendPic = (friendDocument?.data()?["imageurl"]) as! String
self.pendingFriendUsers.append(pendingFriendUser(id: friendUserID, uid: pendingFriendUID , name: pendingFriendName , username: pendingFriendUsername, pic: pendingFriendPic, isFriends: 0))
friendUserID += 1
}
}
}
}
}
}
}
...
func acceptFriendRequest(otherUserUID: String){
for var pendingFriend in pendingFriendUsers {
if pendingFriend.uid == otherUserUID {
pendingFriend.isFriends = 1
}
}
self.ref.collection("Users").document(self.uid).setData(
[ "friends": [
otherUserUID: true
] ]
, merge: true)
self.ref.collection("Users").document(otherUserUID).setData(
[ "friends": [
self.uid: true
] ]
, merge: true)
}
func declineFriendRequest(otherUserUID: String){
for var pendingFriend in pendingFriendUsers {
if pendingFriend.uid == otherUserUID {
pendingFriend.isFriends = 2
}
}
self.ref.collection("Users").document(self.uid).updateData([
"friends.\(otherUserUID)": FieldValue.delete(),
]) { err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Document successfully updated")
}
}
}
func checkFriendRequest(otherUserUID: String){
//0 if not found in friend list or friend request
//1 means theyre friends
//2 means that user sent self/me a friend request
var pendingFriendRequests: [String: Bool] = [:]
self.ref.collection("Users").document(self.uid).getDocument(){
(document, err) in
if let err = err {
print("Error getting documents \(err)")
} else {
pendingFriendRequests = document!.data()!["friends"] as! [String : Bool]
for key in pendingFriendRequests.keys {
if key == otherUserUID{
if pendingFriendRequests[key] == true {
self.isFriend = 1
}else if pendingFriendRequests[key] == false {
self.isFriend = 2
}
}
}
}
}
}
}
Edited with isFriends variable
Right now, you have this:
for var pendingFriend in pendingFriendUsers {
if pendingFriend.uid == otherUserUID {
pendingFriend.isFriends = 1
}
}
This doesn't actually modify anything in pendingFriendUsers, because pendingFriendUser is a struct, which gets passed by value in Swift, not by reference. So var pendingFriend is a copy of the version in pendingFriendUsers.
Instead, you could do something like this:
self.pendingFriendUsers = self.pendingFriendUsers.map { pendingFriend in
guard pendingFriend.uid == otherUserUID else { return pendingFriend } //return the original if the UID doesn't match
var modified = pendingFriend //make a mutable copy
modified.isFriends = 1
return modified //return the modified version
}
or:
guard let index = self.pendingFriendUsers.firstIndex(where: { $0.uid == otherUserUID }) else { return } //get the index of the matching UID
self.pendingFriendUsers[index].isFriends = 1 //modify the item at that index

View doesn't get updated when using ObservableObject

I'm trying to build an Instagram clone app using SwiftUI.
I'm fetching the data through Firebase and trying to achieve a UI update every time the data in the server changes.
For some reason, when I first open the app and fetch the data, the body of my view gets called, but the UI doesn't change. I even put a breakpoint and saw the body gets called and contains the correct information, it's just the UI which doesn't get updated.
I have a few tabs in my app, and when I switch to another tab (which doesn't contain anything but a Text yet), suddenly the UI does gets updated.
Please see the gif below:
Here is my code:
HomeView:
struct HomeView: View {
#ObservedObject private var fbData = firebaseData
var body: some View {
TabView {
//Home Tab
NavigationView {
ScrollView(showsIndicators: false) {
ForEach(self.fbData.posts.indices, id: \.self) { postIndex in
PostView(post: self.$fbData.posts[postIndex])
.listRowInsets(EdgeInsets())
.padding(.vertical, 5)
}
}
.navigationBarTitle("Instagram", displayMode: .inline)
.navigationBarItems(leading:
Button(action: {
print("Camera btn pressed")
}, label: {
Image(systemName: "camera")
.font(.title)
})
, trailing:
Button(action: {
print("Messages btn pressed")
}, label: {
Image(systemName: "paperplane")
.font(.title)
})
)
} . tabItem({
Image(systemName: "house")
.font(.title)
})
Text("Search").tabItem {
Image(systemName: "magnifyingglass")
.font(.title)
}
Text("Upload").tabItem {
Image(systemName: "plus.app")
.font(.title)
}
Text("Activity").tabItem {
Image(systemName: "heart")
.font(.title)
}
Text("Profile").tabItem {
Image(systemName: "person")
.font(.title)
}
}
.accentColor(.black)
.edgesIgnoringSafeArea(.top)
}
}
FirebaseData:
let firebaseData = FirebaseData()
class FirebaseData : ObservableObject {
#Published var posts = [Post]()
let postsCollection = Firestore.firestore().collection("Posts")
init() {
self.fetchPosts()
}
//MARK: Fetch Data
private func fetchPosts() {
self.postsCollection.addSnapshotListener { (documentSnapshot, err) in
if err != nil {
print("Error fetching posts: \(err!.localizedDescription)")
return
} else {
documentSnapshot!.documentChanges.forEach { diff in
if diff.type == .added {
let post = self.createPostFromDocument(document: diff.document)
self.posts.append(post)
} else if diff.type == .modified {
self.posts = self.posts.map { (post) -> Post in
if post.id == diff.document.documentID {
return self.createPostFromDocument(document: diff.document)
} else {
return post
}
}
} else if diff.type == .removed {
for index in self.posts.indices {
if self.posts[index].id == diff.document.documentID {
self.posts.remove(at: index)
}
}
}
}
}
}
}
private func createPostFromDocument(document: QueryDocumentSnapshot) -> Post {
let data = document.data()
let id = document.documentID
let imageUrl = data["imageUrl"] as! String
let authorUsername = data["authorUsername"] as! String
let authorProfilePictureUrl = data["authorProfilePictureUrl"] as! String
let postLocation = data["postLocation"] as! String
let postDescription = data["postDescription"] as! String
let numberOfLikes = data["numberOfLikes"] as! Int
let numberOfComments = data["numberOfComments"] as! Int
let datePosted = (data["datePosted"] as! Timestamp).dateValue()
let isLiked = data["isLiked"] as! Bool
return Post(id: id, imageUrl: imageUrl, authorUsername: authorUsername, authorProfilePictureUrl: authorProfilePictureUrl, postLocation: postLocation, postDescription: postDescription, numberOfLikes: numberOfLikes, numberOfComments: numberOfComments, datePosted: datePosted, isLiked: isLiked)
}
}
If you need me to post more code please let me know.
Update:
PostView:
struct PostView: View {
#Binding var post: Post
var body: some View {
VStack(alignment: .leading) {
//Info bar
HStack {
WebImage(url: URL(string: post.authorProfilePictureUrl))
.resizable()
.frame(width: 40, height: 40)
.clipShape(Circle())
VStack(alignment: .leading, spacing: 2) {
Text(post.authorUsername).font(.headline)
Text(post.postLocation)
}
Spacer()
Button(action: {
print("More options pressed")
}, label: {
Image(systemName: "ellipsis")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
}
.padding(.horizontal)
//Main Image
WebImage(url: URL(string: post.imageUrl))
.resizable()
.aspectRatio(contentMode: .fit)
//Tools bar
HStack(spacing: 15) {
Button(action: {
self.post.isLiked.toggle()
print("Like btn pressed")
}, label: {
Image(systemName: post.isLiked ? "heart.fill" : "heart")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
Button(action: {
print("Comments btn pressed")
}, label: {
Image(systemName: "message")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
Button(action: {
print("Share btn pressed")
}, label: {
Image(systemName: "paperplane")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
Spacer()
Button(action: {
print("Bookmark btn pressed")
}, label: {
Image(systemName: "bookmark")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
}.padding(8)
Text("Liked by \(post.numberOfLikes) users")
.font(.headline)
.padding(.horizontal, 8)
Text(post.postDescription)
.font(.body)
.padding(.horizontal, 8)
.padding(.vertical, 5)
Button(action: {
print("Show comments btn pressed")
}, label: {
Text("See all \(post.numberOfComments) comments")
.foregroundColor(.gray)
.padding(.horizontal, 8)
}).buttonStyle(BorderlessButtonStyle())
Text(post.datePostedString)
.font(.caption)
.foregroundColor(.gray)
.padding(.horizontal, 8)
.padding(.vertical, 5)
}
}
}
Post:
struct Post : Identifiable, Hashable {
var id: String
var imageUrl: String
var authorUsername: String
var authorProfilePictureUrl: String
var postLocation: String
var postDescription: String
var numberOfLikes: Int
var numberOfComments: Int
var datePostedString: String
var isLiked: Bool
init(id: String, imageUrl: String, authorUsername: String, authorProfilePictureUrl: String, postLocation: String, postDescription : String, numberOfLikes: Int, numberOfComments: Int, datePosted: Date, isLiked: Bool) {
self.id = id
self.imageUrl = imageUrl
self.authorUsername = authorUsername
self.authorProfilePictureUrl = authorProfilePictureUrl
self.postLocation = postLocation
self.postDescription = postDescription
self.numberOfLikes = numberOfLikes
self.numberOfComments = numberOfComments
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM dd, yyyy"
self.datePostedString = dateFormatter.string(from: datePosted)
self.isLiked = isLiked
}
}
Thank you!
The problem is that when the app starts your array is empty, and the ScrollView stops updating, you can replace it for a VStack and it will work (just for testing).
The solution is to wrap the ForEach(or the ScrollView) with a condition, like this:
if (fbData.posts.count > 0) {
ForEach(self.fbData.posts.indices, id: \.self) { postIndex in
PostView(post: self.$fbData.posts[postIndex])
.listRowInsets(EdgeInsets())
.padding(.vertical, 5)
}
}

How we get the data and show like viewdidload using swiftUI

i'm trying to work with swiftUI but i'm having an issue. i want to fetch the data from firebase and show that data to the list. i used mutating function to modify the variable. now when i called that function than it give me error. how i can solve this please help me. below are the code i'm using.
struct UserListView : View {
var body: some View{
VStack{
List{
VStack(alignment:.leading){
HStack{
Text("Favroute").frame(alignment: .leading).padding(.leading, 20)
Spacer()
Text("All").frame(alignment: .trailing)
}
ScrollView(.horizontal, content: {
HStack(spacing: 10) {
ForEach(0..<10) { index in
StatusView(statusImage: "nature")
}
}
.padding(.leading, 10)
})
.frame(height: 190)
}
ForEach(0..<10){_ in
HStack{
Group{
NavigationLink(destination: ChatView()){
UserImageView(imageName: "profileDummy").padding(.leading, 5)
Text("User Name").padding(.leading,10)
}
}
}.frame( height: 40)
}.environment(\.defaultMinListRowHeight, 40)
}
}
.navigationBarTitle("UserList",displayMode: .inline)
.onAppear {
self.userList()
}
}
var userDataList = [UserModel]()
mutating func userList() {
databaseReference.child(DatabaseNode.Users.root.rawValue).observe(.value) { (snapShot) in
if snapShot.exists(){
if let friendsDictionary = snapShot.value{
for each in friendsDictionary as! [String : AnyObject]{
print(each)
if let user = each.value as? [String : Any]{
let data = UserModel(userId: JSON(user["userId"] ?? "").stringValue, name: JSON(user["name"] ?? "").stringValue)
print(data)
self.userDataList.append(data)
}
}
}
}
}
}
}
below are the error message i'm getting:
Cannot use mutating member on immutable value: 'self' is immutable
You should declare your userDataList property like so #State var userDataList = [UserModel](). This way this property will be stored by SwiftUI outside of your struct and can be mutated.

Resources