SwiftUI : onAppear method doesn't update State

Viewed 457

I am a beginner at SwiftUI. I am having a problem with fetching some data and updating my state. How can I do that?

import SwiftUI

struct ContentView: View {
   @State var todos : TodoListViewModel
        var body: some View {
            NavigationView {
                TodoList(todos: todos)
            }.onAppear(){
                DispatchQueue.main.async {
                    getTodos()
                }
                print(todos.todolist)
            }
       
        }
    
    func getTodos(){
        let url = URL(string:"https://xxx.xxx.xxx/xxx/xx/xxxx")!
        
        TodoWebService().fetchTodos(url: url) { (todoList) in
            self.todos = TodoListViewModel(todolist: todoList!)
            print(todos.todolist)
        }
       
    }
   
}

It shows only what I pass to ContentView

@main
struct SwiftUITodoAppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView(todos: TodoListViewModel(todolist: [Todo(id: 1, title: "Title", description: "Description")]))
        }
    }
}

My TodoWebService is working. I can print the data successfully

TodoWebService.swift

import Foundation

class TodoWebService {
    func fetchTodos(url:URL,completion: @escaping ([Todo]?) -> ()){
        URLSession.shared.dataTask(with: url) { (data,response,error) in
            if let error = error {
                print(error.localizedDescription)
                completion(nil)
            } else if let data = data {
               let todoList = try? JSONDecoder().decode([Todo].self, from: data)
                completion(todoList)
            }
        }.resume()
    }
}

TodoListViewModel.swift

struct TodoListViewModel{
    var todolist : [Todo]
    
    func numberOfRows() -> Int {
        return self.todolist.count
    }
    
    func todoAtIndex(_ index:Int) -> TodoViewModel {
        let todo = self.todolist[index]
        return TodoViewModel(todo: todo)
    }
}
1 Answers

I solved by changing @State with @ObservedObject and

TodoListViewModel

class TodoListViewModel : ObservableObject{
   @Published var todolist = [Todo]()
   
    init() {
        getAllTodos()
       }

    
    func numberOfRows() -> Int {
        return self.todolist.count
    }
    
    func todoAtIndex(_ index:Int) -> TodoViewModel {
        let todo = self.todolist[index]
        return TodoViewModel(todo: todo)
    }
    
    func getAllTodos(){
        let url = URL(string:"https://xxx.xxx.xxx/xxx/xxx/xxx")!

         
        URLSession.shared.dataTaskPublisher(for: url)
                    .map { $0.data }
                    .decode(type: [Todo].self, decoder: JSONDecoder())
                    .replaceError(with: [])
                    .eraseToAnyPublisher()
                    .receive(on: DispatchQueue.main)
                    .assign(to: &$todolist)
    }
}

ContentView

struct ContentView: View {
   @ObservedObject var todos : TodoListViewModel
        var body: some View {
            NavigationView {
                TodoList(todos: todos)
            }.onAppear(){
                todos.getAllTodos()
                print(todos.todolist)
            }
       
        }
    
    func getTodos(){
        let url = URL(string:"https://xxx.xxx.xxx/xxx/xx/xxxx")!
        
        TodoWebService().fetchTodos(url: url) { (todoList) in
            self.todos = TodoListViewModel(todolist: todoList!)
            print(todos.todolist)
        }
       
    }
   
}
Related