SwiftUI code too long for the compiler to type-check it, but I can't seem to break it down into parts - ios

I am trying to make a view iteratively from a JSON file fetched from a PHP URL. The view is made with a for each loop to create an information box for every different station in the JSON and also makes them a navigation link. Ideally, I would tap the box to get more information on a specific tide station (all station data hasn't been presented on the view yet).
Since I am using a for each loop, I represent the current station number in the data array with i. In order to get the same data as the box I click I use the same i number for other relevant data.
My issue is that as I am building everything in the same for each loop to keep that number i in the scope, my code gets too long for the compiler to check. I heard that this could happen even if the code wasn't too long but simply for a typing mistake somewhere, but I have yet to find a mistake that breaks the code and truly believe it is due to the length as it was working if I commented-out some parts.
To resolve that I understand I need to break my code into different sections for the compiler to check them individually so as not to start the process every time.
However, as I am building everything into the for each loop to have the i number in the scope, I cannot make sub-views that use that number.
I am not entirely sure this is the question that would best solve my issue, but how can I pass the for each loop parameter into another view/function/or something else?
I apologize for the code being very rough and not following good coding practices, if not just for SwiftUI, I am quite inexperienced with programming in general. I left the entirety of the code to be sure not to leave any possibility out.
import Foundation
import SwiftUI
import Alamofire
import SwiftyJSON
import MapKit
public var array_tides_site_name = [String]()
public var array_tides_next_lw_time = [String]()
public var array_tides_next_lw_height = [Double]()
public var array_tides_next_hw_time = [String]()
public var array_tides_next_hw_height = [Double]()
public var array_tides_tidal_state = [String]()
public var array_tides_latitude = [Double]()
public var array_tides_longitude = [Double]()
public var array_tides_observed_height = [Double]()
public var array_tides_predicted_height = [Double]()
public var array_tides_surge = [Double]()
struct Previews_GeneralTides_Previews: PreviewProvider {
static var previews: some View {
GeneralTidesView()
}
}
struct GeneralTidesView: View {
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack (alignment: .center) {
Spacer()
Image(systemName: "chart.xyaxis.line")
.font(.largeTitle)
.frame(width: 300, height: 300, alignment: .center)
Spacer()
Text("More Stations")
.font(.title3)
.foregroundStyle(LinearGradient(colors: [.primary, .secondary], startPoint: .topLeading, endPoint: .bottomTrailing))
.frame(width: screenWidth, height: 40)
.background(.thickMaterial)
Spacer()
Divider()
.onAppear() {loadStationData()}
Spacer()
StationList()
}
.navigationBarTitle("Tides")
.navigationBarTitleDisplayMode(.inline)
}
}
}
func loadStationData(){
let generalTideUrl = "http://www.pla.co.uk/hydrographics/ajax/ltoverview.php"
AF.request(generalTideUrl, method: .get).responseJSON(){ (generalTideResponse) in
switch generalTideResponse.result {
case .success:
//print(generaltideresponse.result)
let generalTideResult = try? JSON(data: generalTideResponse.data!)
//print(generaltideresult)
//print(generalTideResult!["tides"])
let generalTideArray = generalTideResult!["tides"]
array_tides_site_name.removeAll()
array_tides_next_lw_time.removeAll()
array_tides_next_lw_height.removeAll()
array_tides_next_hw_time.removeAll()
array_tides_next_hw_height.removeAll()
array_tides_tidal_state.removeAll()
array_tides_latitude.removeAll()
array_tides_longitude.removeAll()
array_tides_observed_height.removeAll()
array_tides_predicted_height.removeAll()
array_tides_surge.removeAll()
for i in generalTideArray.arrayValue {
//print(i)
let site_name = i["site_name"].stringValue
array_tides_site_name.append(site_name)
var next_lw_time = i["next_lw_time"].stringValue
let lwRange = next_lw_time.startIndex..<next_lw_time.index(next_lw_time.startIndex, offsetBy: 11)
next_lw_time.removeSubrange(lwRange)
array_tides_next_lw_time.append(next_lw_time)
let next_lw_height = i["next_lw_height"].doubleValue
array_tides_next_lw_height.append(next_lw_height)
var next_hw_time = i["next_hw_time"].stringValue
let hwRange = next_hw_time.startIndex..<next_hw_time.index(next_hw_time.startIndex, offsetBy: 11)
next_hw_time.removeSubrange(hwRange)
array_tides_next_hw_time.append(next_hw_time)
let next_hw_height = i["next_hw_height"].doubleValue
array_tides_next_hw_height.append(next_hw_height)
let tidal_state = i["tidal_state"].stringValue
array_tides_tidal_state.append(tidal_state)
let latitude = i["latitude"].doubleValue
array_tides_latitude.append(latitude)
let longitude = i["longitude"].doubleValue
array_tides_longitude.append(longitude)
let predictedHeight = i["predicted_height"].doubleValue
array_tides_predicted_height.append(predictedHeight)
let observedHeight = i["observed_height"].doubleValue
array_tides_observed_height.append(observedHeight)
let surge = i["surge_height"].doubleValue
array_tides_surge.append(surge)
}
break
case .failure:
print(generalTideResponse.error!)
break
}
}.resume()
}
struct StationList: View {
#State private var mapRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.5, longitude: -0.12), span: MKCoordinateSpan(latitudeDelta: 0.6, longitudeDelta: 0.6))
var body: some View {
ForEach(0..<array_tides_site_name.count) { i in
NavigationLink(destination: Group{
VStack(alignment: .center) {
stationNavLink()
Text(array_tides_site_name[i])
.font(.largeTitle)
Map(coordinateRegion: $mapRegion, annotationItems: [Location(name: array_tides_site_name[i], coordinate: CLLocationCoordinate2D(latitude: array_tides_latitude[i], longitude: array_tides_longitude[i]))]) { location in
MapMarker(coordinate: location.coordinate)}
.frame(width: screenWidth, height: 250)
HStack {
RoundedRectangle(cornerRadius: 10)
.foregroundStyle(.thinMaterial)
.frame(width: 150, height: 120)
.overlay {
VStack(alignment: .leading , spacing: 10) {
Text("Next Low Tide:")
HStack {Text("Time: "); Text(array_tides_next_lw_time[i])}.foregroundColor(.secondary)
HStack {Text("Height: "); Text(array_tides_next_lw_height[i].description); Text("m")}.foregroundColor(.secondary)
}
}
RoundedRectangle(cornerRadius: 10)
.foregroundStyle(.thinMaterial)
.frame(width: 150, height: 120)
.overlay {
VStack(alignment: .leading, spacing: 10) {
Text("Next High Tide:")
HStack {Text("Time: "); Text(array_tides_next_hw_time[i])}.foregroundColor(.secondary)
HStack {Text("Height: "); Text(array_tides_next_hw_height[i].description); Text("m")}.foregroundColor(.secondary)
}
}
}
Text(array_tides_surge[i].description)
}
}){
ZStack {
RoundedRectangle(cornerRadius: 8)
.strokeBorder(.white.opacity(0.3), lineWidth: 1)
.background(RoundedRectangle(cornerRadius: 8).fill(.thinMaterial))
.frame(width: screenWidth - 40, height: 80)
.overlay() {
VStack(alignment: .leading) {
Spacer()
Text(array_tides_site_name[i])
.padding(.leading, 10)
.foregroundStyle(LinearGradient(colors: [.secondary, .secondary.opacity(0.8)], startPoint: .leading, endPoint: .trailing))
Spacer()
Divider()
Spacer()
Group {
HStack(){
Spacer()
Text("High Water: ")
Text(array_tides_next_hw_time[i])
Spacer()
Text("Low Water: ")
Text(array_tides_next_lw_time[i])
Spacer()
if array_tides_tidal_state[i] == "Flood" { Image(systemName: "arrow.up").foregroundColor(.green) }
else { Image(systemName: "arrow.down").foregroundColor(.red) }
Spacer()
}
}
Spacer()
}
}
}
}
}
}
}
var stationNavLink: some View {
Text(array_tides_surge[currentStation])
}
struct Location: Identifiable {
let id = UUID()
let name: String
let coordinate: CLLocationCoordinate2D
}

Having multiple, associated, arrays to hold your data is a definite code smell. You risk having data get out of sync between the arrays and as you can see, loading and accessing data involves a lot of code.
You can start by creating some [Codable] structs to hold you data. quicktype.io can do this for. Simply paste in your JSON and you get the structs you need.
I have modified Tide to conform to Hashable and Identifiable to make it easier to use in SwiftUI lists.
Also note the use of CodingKeys to convert the properties to Swift naming conventions.
// MARK: - TideData
struct TideData: Codable {
let timezone: String
let tides: [Tide]
}
// MARK: - Tide
struct Tide: Codable, Hashable, Identifiable {
let siteID, siteStation: Int
let siteName: String
let latitude, longitude: Double
let received: String
let observedHeight, predictedHeight, surgeHeight: Double
let nextLwTime: String
let nextLwHeight: Double
let nextHwTime: String
let nextHwHeight: Double
let tidalState: String
var id: Int {
return siteID
}
enum CodingKeys: String, CodingKey {
case siteID = "site_id"
case siteStation = "site_station"
case siteName = "site_name"
case latitude, longitude
case received = "Received"
case observedHeight = "observed_height"
case predictedHeight = "predicted_height"
case surgeHeight = "surge_height"
case nextLwTime = "next_lw_time"
case nextLwHeight = "next_lw_height"
case nextHwTime = "next_hw_time"
case nextHwHeight = "next_hw_height"
case tidalState = "tidal_state"
}
}
Now you can move your loading code into its own class. Make this an ObservableObject and have it publish the tide data.
Since our structs conform to Codable we can use AlamoFire's inbuilt JSON decoding and transformation
lass Loader:ObservableObject {
#Published var tides: [Tide] = []
func load() {
let generalTideUrl = "https://www.pla.co.uk/hydrographics/ajax/ltoverview.php"
AF.request(generalTideUrl, method: .get).responseDecodable(of: TideData.self){ (generalTideResponse) in
switch generalTideResponse.result {
case .success(let tideData):
self.tides = tideData.tides
print(self.tides)
case .failure(let error):
print(error)
}
}
}
}
Much more succinct!
Finally, we can use this data in a SwiftUI view
I have simplified your views for the sake of this answer, but I am sure you will get the idea.
struct GeneralTidesView: View {
#ObservedObject var loader: Loader
var body: some View {
NavigationView {
List {
ForEach(loader.tides) { station in
NavigationLink(destination: StationView(station:station)) {
Text(station.siteName)
}
}
}
}.onAppear {
self.loader.load()
}
}
}
struct Previews_GeneralTides_Previews: PreviewProvider {
static var previews: some View {
GeneralTidesView(loader: Loader())
}
}
struct StationView: View {
var station: Tide
var body: some View {
Form {
HStack {
Text("Predicted Height")
Text("\(station.observedHeight)")
}
HStack {
Text("Predicted Height")
Text("\(station.predictedHeight)")
}
}.navigationTitle(station.siteName)
}
}
You can see how having a struct with properties makes it much easier to pass data around. Also, since all of the properties are in a single struct, we no longer need to work about array indices.

Related

SwiftUI: How can I add a new component to the list? Idea: Click the "plus"-button, enter a "Text", store it and... it's been added to the List

So here is the declaration of the variable (passwordNewName):
#State var passwordNewName = ""
It is being updated from the input of a TextField which I coded to accept user's data.
So the idea is that the String which is stored in the variable will - eventually - get transmitted into this List():
If I understand it right, the .setname files are stored inside this static func all() database or whatever these [*braces*] are:
Basically, the String-variable passwordNewName should somehow be added to these [braces] automatically... I am really so lost.((
Thank you in advance!!!
According to the question it looks that you want to update the list along with to send/save it permanently. The actual solution for this problem is to use ObservableObject class with #Published property. But the code below will give you more understanding of scenario.
import SwiftUI
struct ContentView: View {
#State private var lernSet_array: [Lernset] = Lernset.get_all()
var body: some View {
VStack(spacing: 0) {
HStack {
Text("Collection")
Button {
lernSet_array.append(Lernset(setname: "Testing", color: "black"))
} label: {
Image(systemName: "plus")
.resizable()
.frame(width: 30, height: 30)
.padding()
}
}
ScrollView {
VStack(alignment: .leading) {
ForEach(lernSet_array) { lernset in
HStack {
Image(systemName: "folder")
.resizable()
.frame(width: 80, height: 80)
.foregroundColor(.gray)
Text(lernset.setname)
}
}
}
}
}.onChange(of: lernSet_array) { newValue in
// Update this database, and somehow store the data
// and save it permanently… even after restarting the phone
print("Update this database, and somehow store the data and save it permanently… even after restarting the phone")
print(lernSet_array.count)
}
}
}
struct Lernset: Identifiable, Equatable {
let id = UUID()
let setname: String
let color: String
static func get_all() -> [Lernset] {
return [Lernset(setname: "Biology - Tail", color: "green"),
Lernset(setname: "Math - Tail", color: "blue"),
Lernset(setname: "Phy - Tail", color: "black"),
]
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

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
}

How to refresh Core Data array when user enters new view with SwiftUI?

I have 3 views. Content View, TrainingView and TrainingList View. I want to list exercises from Core Data but also I want to make some changes without changing data.
In ContentView; I am trying to fetch data with CoreData
struct ContentView: View {
// MARK: - PROPERTY
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Training.timestamp, ascending: false)],
animation: .default)
private var trainings: FetchedResults<Training>
#State private var showingAddProgram: Bool = false
// FETCHING DATA
// MARK: - FUNCTION
// MARK: - BODY
var body: some View {
NavigationView {
Group {
VStack {
HStack {
Text("Your Programs")
Spacer()
Button(action: {
self.showingAddProgram.toggle()
}) {
Image(systemName: "plus")
}
.sheet(isPresented: $showingAddProgram) {
AddProgramView()
}
} //: HSTACK
.padding()
List {
ForEach(trainings) { training in
TrainingListView(training: training)
}
} //: LIST
Spacer()
} //: VSTACK
} //: GROUP
.navigationTitle("Good Morning")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
print("test")
}) {
Image(systemName: "key")
}
}
} //: TOOLBAR
.onAppear() {
}
} //: NAVIGATION
}
private func showId(training: Training) {
guard let id = training.id else { return }
print(id)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
In TrainingView; I am getting exercises as a array list and I am pushing into to TrainingListView.
import SwiftUI
struct TrainingView: View {
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
#State var training: Training
#State var exercises: [Exercise]
#State var tempExercises: [Exercise] = [Exercise]()
#State var timeRemaining = 0
#State var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
#State var isTimerOn = false
var body: some View {
VStack {
HStack {
Text("\(training.name ?? "")")
Spacer()
Button(action: {
presentationMode.wrappedValue.dismiss()
}) {
Text("Finish")
}
}
.padding()
ZStack {
Circle()
.fill(Color.blue)
.frame(width: 250, height: 250)
Circle()
.fill(Color.white)
.frame(width: 240, height: 240)
Text("\(timeRemaining)s")
.font(.system(size: 100))
.fontWeight(.ultraLight)
.onReceive(timer) { _ in
if isTimerOn {
if timeRemaining > 0 {
timeRemaining -= 1
} else {
isTimerOn.toggle()
stopTimer()
removeExercise()
}
}
}
}
Button(action: {
startResting()
}) {
if isTimerOn {
Text("CANCEL")
} else {
Text("GIVE A BREAK")
}
}
Spacer()
ExerciseListView(exercises: $tempExercises)
}
.navigationBarHidden(true)
.onAppear() {
updateBigTimer()
}
}
private func startResting() {
tempExercises = exercises
if let currentExercise: Exercise = tempExercises.first {
timeRemaining = Int(currentExercise.rest)
startTimer()
isTimerOn.toggle()
}
}
private func removeExercise() {
if let currentExercise: Exercise = tempExercises.first {
if Int(currentExercise.rep) == 1 {
let index = tempExercises.firstIndex(of: currentExercise) ?? 0
tempExercises.remove(at: index)
} else if Int(currentExercise.rep) > 1 {
currentExercise.rep -= 1
let index = tempExercises.firstIndex(of: currentExercise) ?? 0
tempExercises.remove(at: index)
tempExercises.insert(currentExercise, at: index)
}
updateBigTimer()
}
}
private func updateBigTimer() {
timeRemaining = Int(tempExercises.first?.rest ?? 0)
}
private func stopTimer() {
timer.upstream.connect().cancel()
}
private func startTimer() {
timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
}
}
struct TrainingView_Previews: PreviewProvider {
static var previews: some View {
TrainingView(training: Training(), exercises: [Exercise]())
}
}
In TrainingListView; I am listing all exercises.
struct TrainingListView: View {
#ObservedObject var training: Training
#Environment(\.managedObjectContext) private var managedObjectContext
var body: some View {
NavigationLink(destination: TrainingView(training: training, exercises: training.exercises?.toArray() ?? [Exercise]())) {
HStack {
Text("\(training.name ?? "")")
Text("\(training.exercises?.count ?? 0) exercises")
}
}
}
}
Also, I am adding video: https://twitter.com/huseyiniyibas/status/1388571724346793986
What I want to do is, when user taps any Training Exercises List should refreshed. It should be x5 again like in the beginning.
I had a hard time understanding your question but I guess I got the idea.
My understanding is this:
You want to store the rep count in the Core Data. (Under Training > Exercises)
You want to count down the reps one by one as the user completes the exercise.
But you don't want to change the original rep count stored in the Core Data.
I didn't run your code since I didn't want to recreate all the models and Core Data files. I guess I've spotted the problem. Here I'll explain how you can solve it:
The Core Data models are classes (reference types). When you pass around the classes (as you do in your code) and change their properties, you change the original data. In your case, you don't want that.
(Btw, being a reference type is a very useful and powerful property of classes. Structs and enums are value types, i.e. they are copied when passed around. The original data is unchanged.)
You have several options to solve your problem:
Just generate a different struct (something like ExerciseDisplay) from Exercise, and pass ExerciseDisplay to TrainingView.
You can write an extension to Exercise and "copy" the model before passing it to TrainingView. For this you'll need to implement the NSCopying protocol.
extension Exercise: NSCopying {
func copy(with zone: NSZone? = nil) -> Any {
return Exercise(...)
}
}
But before doing this I guess you'll need to change the Codegen to Manual/None of your entry in your .xcdatamodeld file. This is needed when you want to create the attributes manually. I'm not exactly sure how you can implement NSCopying for a CoreDate model, but it's certainly doable.
The first approach is easier but kinda ugly. The second is more versatile and elegant, but it's also more advanced. Just try the first approach first and move to the second once you feel confident.
Update:
This is briefly how you can implement the 1st approach:
struct ExerciseDisplay: Identifiable, Equatable {
public let id = UUID()
public let name: String
public var rep: Int
public let rest: Int
}
struct TrainingView: View {
// Other properties and states etc.
let training: Training
#State var exercises: [ExerciseDisplay] = []
init(training: Training) {
self.training = training
}
var body: some View {
VStack {
// Views
}
.onAppear() {
let stored: [Exercise] = training.exercises?.toArray() ?? []
self.exercises = stored.map { ExerciseDisplay(name: $0.name ?? "", rep: Int($0.rep), rest: Int($0.rest)) }
}
}
}

The compiler is unable to type-check this expression in a reasonable time in SwiftUI?

I have a line of code that sets the background of Text to an Image that is fetched by finding the first three letters of the string. For some reason this won't run and keeps giving me the error above. Any ideas on how I can fix this?
There are a lot of images that need to be set as the backgrounds for multiple different pieces of text. I believe I have the right idea by using the prefix of the string, but it seems like Xcode is having difficulty/won't run this.
Pretty sure this specific line is giving me issues, but would love some feedback.
.background(Image(colorOption.prefix(3)).resizable())
import SwiftUI
struct ColorView: View {
// #ObservedObject var survey = Survey()
#ObservedObject var api = ColorAPIRequest(survey: DataStore.instance.currentSurvey!)
#State var showingConfirmation = true
#State var showingColorView = false
#State var tempSelection = ""
#EnvironmentObject var survey: Survey
//#EnvironmentObject var api: APIRequest
var colorOptionsGrid: [[String]] {
var result: [[String]] = [[]]
let optionsPerRow = 4
api.colorOptions.dropFirst().forEach { colorOption in
if result.last!.count == optionsPerRow { result.append([]) }
result[result.count - 1].append(colorOption)
}
return result
}
var body: some View {
VStack {
Text("Select Tape Color")
.font(.system(size:70))
.bold()
.padding(.top, 20)
NavigationLink("", destination: LengthView(), isActive: $showingColorView)
HStack {
List {
ForEach(colorOptionsGrid, id: \.self) { colorOptionRow in
HStack {
ForEach(colorOptionRow, id: \.self) { colorOption in
Button(action: {
// self.survey.length = lengthOption
self.tempSelection = colorOption
self.showingConfirmation = false
}
) {
ZStack {
Color.clear
Text(colorOption.prefix(3))
.font(.title)
.foregroundColor(self.tempSelection == colorOption ? Color.white : Color.black)
.frame(width: 200, height: 100)
.background(Image(colorOption.prefix(3)).resizable())
//Image(colorOption.prefix(3)).resizable()
}
}.listRowBackground(self.tempSelection == colorOption ? Color.pink : Color.white)
.multilineTextAlignment(.center)
}
}
}
}.buttonStyle(PlainButtonStyle())
}
Button(action: {
self.survey.color = self.tempSelection
self.showingColorView = true
self.showingConfirmation = true
}) {
Text("Press to confirm \(tempSelection)")
.bold()
.padding(50)
.background(Color.pink)
.foregroundColor(.white)
.font(.system(size:40))
.cornerRadius(90)
}.isHidden(showingConfirmation)
.padding(.bottom, 50)
}
}
}
The compiler actually gives a fairly decent suggestion when it tells you to break the expression up. The simplest you can do is extract the background image into a separate function like this:
func backgroundImage(for colorOption: String) -> some View {
Image(String(colorOption.prefix(3))).resizable()
}
and then replace the call to
.background(Image(colorOption.prefix(3)).resizable())
with
.background(self.backgroundImage(for: colorOption))
Also note that I wrapped colorOption.prefix(3) in a String constructor, simply because .prefix(_:) returns a Substring, but the Image(_:) constructor requires a String.

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