[英]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 使用传递给View
的urlString
调用。 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
.因为StateObject
的wrappedValue
参数使用和自动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.