0

I've got some sample data that loads in my list, but when swapping it out for data from my API service, the list is empty. This is how my View looks:

struct ChatListView: View {
    @StateObject var vm = ChatListViewModel()
    var uid: String?
    
    var body: some View {
            List {
                ForEach(vm.messages, id: \.uid) { message in
                        ChatListItemView(chat: message)
                }
            }
            .refreshable {
                if let uid = uid {
                    vm.getNearbyThreads(identifier: uid)
                }
        }
    }
}

My ViewModel looks like this:

final class ChatListViewModel: ObservableObject {

    @Published var messages: [MessageThread] = []
    @Published var hasError = false

    func getNearbyThreads(identifier: String) {
        let api = ApiService()
        guard let data = try? api.getNearestThreads(query: identifier) else {
            self.messages = []
            self.hasError = true
            return
        }
        DispatchQueue.main.async {
            self.messages = data
        }
    }
}

And my API service code looks is this:

func getNearestThreads(query: String) -> [MessageThread] {
    
        var messageThreads = [MessageThread]()
        
        guard var url = URLComponents(string: API_URL) else {
            return messageThreads
        }
        
        url.queryItems = [URLQueryItem(name: "uid", value: query)]
                
        let request = URLRequest(url: url.url!)
        
        let session = URLSession(configuration:  .default)
        let task = session.dataTask(with: request) { data, _, error in
            guard let data = data, error == nil else {
                print(error!)
                return
            }
            do {
                guard let threads = self.parse(json: data) else {
                    throw ApiServiceError.unableToParse(data: data)
                }
                DispatchQueue.main.async {
                    messageThreads = threads
                }
            }
            catch {
                print(error)
            }
        }
        task.resume()
        return messageThreads
    }

I have a feeling that perhaps using two DispatchQueue asyncs may be causing a problem here. This is my first time doing iOS programming in years and working with SwiftUI, so I'm keen to understand if what I'm doing is impacting the view hierarchy or if I'm just not passing data around properly.

HarryMoy
  • 142
  • 3
  • 20
  • Your API method never really returned a response, it’s always []. – Tushar Sharma Sep 13 '22 at 19:43
  • Sue do try catch instead of guard you will likely get an actual error try? Just ignores errors. Try the [Apple SwiftUI Tutorials](https://developer.apple.com/tutorials/swiftui) There are many issues with the code. – lorem ipsum Sep 13 '22 at 19:43
  • @loremipsum there are no errors returning from the API, I can see the API results printed to the console in the parse function I have written. – HarryMoy Sep 13 '22 at 19:50
  • @TusharSharma don't I need to initialize an empty value though in case there are errors so I can return an empty list? – HarryMoy Sep 13 '22 at 19:51

1 Answers1

1

You call an asynchronous method

 func getNearestThreads(query: String) -> [MessageThread] {

    let task = session.dataTask(with: request) { data, _, error in
        ......
 }

synchronously here

guard let data = try? api.getNearestThreads(query: identifier) else {
   self.messages = []
   self.hasError = true
   return
}

Which for sure will return an empty array before the request response returns , Instead you need

func getNearestThreads(query: String) async throws -> [MessageThread] {
    guard var url = URLComponents(string: API_URL) else { return [] }
    url.queryItems = [URLQueryItem(name: "uid", value: query)] 
    let request = URLRequest(url: url.url!)
    let session = URLSession(configuration:  .default)
    let (data, _) = try await session.data(for: request)
    let threads = try self.parse(json: data)
    return threads
}

And call it like this

Task {
    do {
         let data = await try api.getNearestThreads(query: identifier)
         self.messages = data
    }
    catch {
        self.hasError = true
    }
}
Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87