2

I have a problem with Array using ObservableObject in my view. I have an empty array. I call a function at page onAppear. When the data is returned, the view does not update with the new data in array:

class NewsState: ObservableObject {
    
    private let base: String = "api"
    let objectWillChange = ObservableObjectPublisher()
    
    @Published var wagsList: Array<UserSlider> = [] {
        willSet {
            objectWillChange.send()
        }
    }
    
    func getList() {
        let url = NSURL(string: "\(base)/UserApi/getList")
        var mutableURLRequest = URLRequest(url: url! as URL)
        mutableURLRequest.httpMethod = "GET"
        mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
        AF.request(mutableURLRequest).responseData { response in
            guard let data = response.data else { return }
            let resp = try! JSONDecoder().decode(Array<UserSlider>.self, from: data)
            for i in resp {
                let userSlider = UserSlider(id: i.id, uid: i.uid, image: i.image)
                self.wagsList.append(userSlider)
            }
        }
    }
}

In my view I have this:

HStack {
    ScrollView(.horizontal, showsIndicators: false) {
        HStack(spacing: 20) {
            if(self.newsState.wagsList.count != 0) {
                ForEach(self.newsState.wagsList, id: \.self) { wags in
                    VStack {
                        HStack {
                            URLImage(URL(string: "\(wags.image)")!, expireAfter: Date(timeIntervalSinceNow: 10)) { proxy in
                                proxy.image
                                    .renderingMode(.original)
                                    .resizable()
                                    .aspectRatio(contentMode: .fill)
                                    .frame(width: 60, height: 60)
                                    .clipShape(Circle())
                                    .overlay(
                                        RoundedRectangle(cornerRadius: 30)
                                            .stroke(Color.white, lineWidth: 2)
                                    )
                                    .contentShape(Circle())
                            }.frame(width: 62, height: 62)
                        }
                        HStack {
                            Text("10K")
                                .foregroundColor(Color.white)
                                .font(Font.custom("Metropolis-Bold", size: 15))
                        }
                        HStack {
                            Text("followers")
                                .foregroundColor(Color.white)
                                .font(Font.custom("Metropolis-Normal", size: 15))
                        }
                    }
                }
            } else {
                //loader
            }
        }.onAppear(perform: initPage)
    }
}

What am I doing wrong? I see that the problem is caused by ScrollView.

McKinley
  • 1,123
  • 1
  • 8
  • 18
Stefano Toppi
  • 626
  • 10
  • 28
  • Have you for e.g. printed out the array before and after the on appear? This would help to determine where the error might lay. It can be either in the getList or also in your View. – Simon May 26 '20 at 10:11
  • 1
    Why do you even use `ObservableObjectPublisher`? `Published` should already do that. Also, as you are using `willSet` here, just know that there's a bug related with those functions not calling on `Xcode 11.4 and 11.4.1`. See here: https://stackoverflow.com/questions/60907882/swiftui-toggle-function-on-published-values-stopped-triggering-didset-with-swi – Cuneyt May 26 '20 at 10:36

2 Answers2

0

Try this one

class NewsState: ObservableObject {

    private let base: String = "api"

    @Published var wagsList: Array<UserSlider> = []

    func getList() {
        let url = NSURL(string: "\(base)/UserApi/getList")
        var mutableURLRequest = URLRequest(url: url! as URL)
        mutableURLRequest.httpMethod = "GET"
        mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
        AF.request(mutableURLRequest).responseData { response in
            guard let data = response.data else { return }
            let resp = try! JSONDecoder().decode(Array<UserSlider>.self, from: data)

            let results = resp.map { UserSlider(id: $0.id, uid: $0.uid, image: $0.image) }
            DispatchQueue.main.async {
                self.wagsList = results
            }
        }
    }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • If i move getList in the init of observable the list appear, I however need both on Apper – Stefano Toppi May 26 '20 at 10:51
  • This sounds most likely like an Issue in your View. Might consider sharing the whole View? Do you have a @ObservedObject in front of your ```var newsState``` in view? Also give ForEach ids to differentiate like this: ```ForEach(self.newsState.wagsList, id: \.self)``` – Simon May 26 '20 at 11:06
  • I edited the original question, I noticed that the problem is given by the scrollview, if I remove that they appear – Stefano Toppi May 26 '20 at 11:24
  • I gave a fixed height at HStack inside ScrollView and now they appear – Stefano Toppi May 26 '20 at 11:29
0

As it is not clear to me where the error might lay. It could be either in getList or in your View.

This is an easy example of how it works with a Published and ObserverdObject: Note: your getList function is not in this solution as the error could be with your API, JSON ect.

import SwiftUI

struct ContentView: View {

    @ObservedObject var state = NewsState()

    var body: some View {
        Group { //needed for the IF Statement below
            if state.stringList.count > 0 {
                ForEach(self.state.stringList, id: \.self){ s in
                    Text(String(s))
                }
            }
        }.onTapGesture {
            self.state.getNewList()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


class NewsState: ObservableObject {

    @Published var stringList: Array<String> = []

    init() {
        self.getList()
    }

    func getList() {
        self.stringList.append("New")
    }

    func getNewList() {
        self.stringList = []
        self.stringList.append("New new")
    }
}

Simon
  • 1,754
  • 14
  • 32