网络请求填充时如何初始化变量 [英] How to Initialize Variable when it is Populated by Network Request
问题描述
我的内容视图是:
struct ContentView {@State private var calendar = Bundle.main.decode(Calendar.self, from: "calendar.json")var主体:一些视图{导航视图{.... 循环日历并在列表中显示事件的内容}.onAppear {self.getData()}}}功能获取数据(){let url = "";let encoding = "";var request = URLRequest(url: url)request.setValue("application/json", forHTTPHeaderField: "Content-Type")request.httpMethod = "POST";让编码器 = JSONEncoder()如果让数据=尝试?编码器.编码(编码){request.httpBody = 数据}URLSession.shared.dataTask(with: request) { data1, response, error in//解码响应并处理数据如果让数据 1 = 数据 1 {让解码器 = JSONDecoder()如果让日历 = 尝试?解码器.解码(IDCCalendar.self,来自:data1){self.calendar = 日历}别的 {打印(没有正确解码")}}别的 {打印(数据1错误")}}.恢复()}
这是我的问题:日历变量需要初始化,所以我用一个名为 calendar.json 的文件中的虚拟数据初始化它.理想情况下,我只想声明@State 私有变量日历:日历并让网络请求填充变量.使用虚拟数据方法,当应用加载时,虚拟数据首先出现,然后在网络调用完成时替换为实际数据.
任何解决此问题的建议将不胜感激.
如果使用可选值,则无需为其设置初始值.您的 View
可以根据变量是 nil
还是有值来有条件地呈现.
通常,异步请求不应在 View
内完成,因此我将逻辑移至 ObservableObject
:
class ViewModel : ObservableObject {@Published var 日历:CalendarModel?功能获取数据(){//从API获取数据并用结果设置self.calendar}}结构内容视图{@StateObject var viewModel = ViewModel()var主体:一些视图{导航视图{团体 {如果让日历 = viewModel.calendar {//这里使用`calendar`变量} 别的 {文本(加载中...")}}}.onAppear {viewModel.getData()}}}
您还可以通过执行以下操作将日历视图重构为自己的组件(具有非可选属性):
<预><代码>结构内容视图{@StateObject var viewModel = ViewModel()var主体:一些视图{导航视图{团体 {如果让日历 = viewModel.calendar {CalendarView(日历:日历)} 别的 {文本(加载中...")}}}.onAppear {viewModel.getData()}}}结构日历视图:查看{无功日历:日历模型var主体:一些视图{//渲染日历}}(注意:我使用了 CalendarModel 因为你没有包含你的类型的代码,你同时使用了 Calendar
和 IDCCalendar
,我不确定区别是什么.另外,请注意与 Calendar
的命名空间冲突,因为 Foundation 中已经有一个名为该类型的类型)
My Content view is:
struct ContentView {
@State private var calendar = Bundle.main.decode(Calendar.self, from: "calendar.json")
var body: some View {
NavigationView {
.... Content that loops through calendar and displays events in a list
}
.onAppear {
self.getData()
}
}
}
func getData() {
let url = "<URL goes here>"
let encoded = "<JSON FOR REQUEST>"
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let encoder = JSONEncoder()
if let data = try? encoder.encode(encoded) {
request.httpBody = data
}
URLSession.shared.dataTask(with: request) { data1, response, error in
// DECODE the response and process the data
if let data1 = data1 {
let decoder = JSONDecoder()
if let calendar = try? decoder.decode(IDCCalendar.self, from: data1)
{
self.calendar = calendar
}
else {
print("Does not decode correctly")
}
}
else {
print("error with data1")
}
}.resume()
}
Here is my problem: The calendar variable needs to be initialised, so I am initialising it with dummy data from a file called calendar.json. Ideally, I'd just like to declare @State private var calendar : Calendar and have the network request populate the variable. Using the dummy data approach, when the app loads up, the dummy data appears first, and then gets replaced with actual data when the network call finishes up.
Any suggestions to solve this problem will be most appreciated.
If you use an optional value, you don't need to set it with an initial value. Your View
can render conditionally depending on if the variable is nil
or has a value.
In general, async requests should not be done inside a View
, so I've moved the logic to an ObservableObject
:
class ViewModel : ObservableObject {
@Published var calendar : CalendarModel?
func getData() {
//get data from API and set self.calendar with result
}
}
struct ContentView {
@StateObject var viewModel = ViewModel()
var body: some View {
NavigationView {
Group {
if let calendar = viewModel.calendar {
//use `calendar` variable here
} else {
Text("Loading...")
}
}
}
.onAppear {
viewModel.getData()
}
}
}
You could also refactor the calendar view into its own component (with a non-optional property) by doing something like this:
struct ContentView {
@StateObject var viewModel = ViewModel()
var body: some View {
NavigationView {
Group {
if let calendar = viewModel.calendar {
CalendarView(calendar: calendar)
} else {
Text("Loading...")
}
}
}
.onAppear {
viewModel.getData()
}
}
}
struct CalendarView : View {
var calendar : CalendarModel
var body: some View {
//render calendar
}
}
(Note: I used CalendarModel since you didn't include the code for your types and you used both Calendar
and IDCCalendar
and I wasn't sure what the difference was. Also, beware of namespace collisions with Calendar
since there's there's already a type named that in Foundation)
这篇关于网络请求填充时如何初始化变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!