简体   繁体   English

如何在我的 SwiftUI 应用程序中初始化我的 ViewModel

[英]How do I initialise my ViewModel in my SwiftUI app

I have a SwiftUI App which uses a public API to download cocktail data by name but I am not very familiar with SwiftUI and I cannot see a way of initialising my view model in my DetailsView file.我有一个 SwiftUI 应用程序,它使用公共 API 按名称下载鸡尾酒数据,但我对 SwiftUI 不是很熟悉,而且我在我的 DetailsView 文件中看不到初始化我的视图模型的方法。

here is my swift file if cocktail data structs;如果是鸡尾酒数据结构,这是我的 swift 文件;

struct Drinks: Decodable {
    var cocktails: [Cocktail]
}

struct Cocktail: Decodable, Identifiable {
    var id: String {
        return idDrink
    }
    let idDrink: String
    let strDrink: String
    let strDrinkThumb: String
    let strAlcoholic: String
    let strGlass: String
    let strInstructions: String
    let strIngredient1: String?
    let strIngredient2: String?
    let strIngredient3: String?
    let strIngredient4: String?
    let strIngredient5: String?
    let strIngredient6: String?
    let strIngredient7: String?
    let strIngredient8: String?
    let strIngredient9: String?
    let strIngredient10: String?
    let strIngredient11: String?
    let strIngredient12: String?
}

here is my NetworkManager class;这是我的 NetworkManager 类;

class NetworkManager {
        
    func fetchData(_ urlString: String, completion: @escaping (Drinks, Bool) -> Void) {
        guard let url = URL(string: urlString) else { return }
        var drinks: Drinks?
        let session = URLSession(configuration: .default)
        let task = session.dataTask(with: url) { data, response, error in
            if error == nil {
                let decoder = JSONDecoder()
                guard let safeData = data else { return }
                do {
                    drinks = try decoder.decode(Drinks.self, from: safeData)
                        completion(drinks!, false)
                } catch let error {
                    print(error.localizedDescription)
                    if error.localizedDescription == "The data couldn’t be read because it is missing." {
                            completion(drinks ?? Drinks(cocktails: [Cocktail]()), true)
                    } else {
                        print(error.localizedDescription)
                    }
                }
            }
        }
        task.resume()
    }
}

Here is my ViewModel class;这是我的 ViewModel 类;

class ViewModel: ObservableObject {
    
    let networkManager = NetworkManager()
    var urlString: String
    @Published var drinks: Drinks = Drinks(cocktails: [Cocktail]())
    @Published var dataIsFound: Bool = true
    
    init(urlString: String) {
        self.urlString = urlString
        FetchData()
    }
    
    func FetchData() {
    
        networkManager.fetchData(urlString) { results, error in
            DispatchQueue.main.async {
            self.drinks = results
            self.dataIsFound = !error
            }
        }
    }

and here is my DetailsView struct;这是我的 DetailsView 结构;

struct DetailsView: View {
    
    @StateObject var viewModel = ViewModel()
        
    var body: some View {
        
        List(viewModel.drinks.cocktails) { cocktail in

            VStack(alignment: .center) {
                HStack(alignment: .center) {
                    Text(cocktail.strDrink + "  -")
                        .navigationTitle("Cocktail by first letter")
                        .frame(alignment: .center)
                    Text(cocktail.strAlcoholic)
                        .frame(alignment: .center)
                }
                
                WebImage(url: URL(string: cocktail.strDrinkThumb))
                    .resizable()
                    .frame(width: UIScreen.main.bounds.width - 20.0, height: UIScreen.main.bounds.width - 20.0, alignment: .center)
                    
                
                Text("~ Ingredients List ~\n").frame(alignment: .center)
                
                ForEach(viewModel.buildIngredients(cocktail), id: \.self) { ingredient in
                    Text(ingredient)
                }
                
                Text("\n~ Recipe Instructions ~\n\n")
                
                Text(cocktail.strInstructions + "\n").fixedSize(horizontal: false, vertical: true)
            }
        }        
    }
}

Any help would be appreciated.任何帮助,将不胜感激。 Thanks.谢谢。

Here is one common pattern, using ViewModel() to initialize and then calling fetchData on onAppear :这是一种常见的模式,使用ViewModel()进行初始化,然后在fetchData上调用onAppear

class ViewModel: ObservableObject {
    
    let networkManager = NetworkManager()
    @Published var drinks: Drinks = Drinks(cocktails: [Cocktail]())
    @Published var dataIsFound: Bool = true
    
    
    func fetchData(urlString: String) {
        //call fetchData on network manager
    }
}

struct DetailsView: View {
    
    var urlString : String
    @StateObject private var viewModel = ViewModel()
        
    var body: some View {
        
        List(viewModel.drinks.cocktails) { cocktail in
            //list content
        }
        .onAppear {
            viewModel.fetchData(urlString: urlString)
        }
    }
}

Another option is to use your View' s init .另一种选择是使用您的View'init In this case, the @StateObject 's init is called with the urlString passed into the View .在这种情况下, @StateObject的 init 使用传递给ViewurlString调用。 Because StateObject 's wrappedValue parameter uses and autoclosure and only gets run if the view is added to the hierarchy, you don't have to worry that the view model will be re-initialized on every init of the View .因为StateObjectwrappedValue参数使用和自动wrappedValue并且只有在将视图添加到层​​次结构时才会运行,所以您不必担心视图模型会在每次init时重新初始化View

class ViewModel: ObservableObject {
    
    let networkManager = NetworkManager()
    @Published var drinks: Drinks = Drinks(cocktails: [Cocktail]())
    @Published var dataIsFound: Bool = true
    
    init(urlString: String) {
        fetchData(urlString: urlString)
    }
    
    func fetchData(urlString: String) {
        //call fetchData on network manager
    }
}

struct DetailsView: View {
    
    @StateObject private var viewModel : ViewModel
    
    init(urlString: String) {
        _viewModel = StateObject(wrappedValue: ViewModel(urlString: urlString))
    }
    
    var body: some View {
        //body content
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM