网络请求填充时如何初始化变量 [英] How to Initialize Variable when it is Populated by Network Request

查看:21
本文介绍了网络请求填充时如何初始化变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的内容视图是:

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 因为你没有包含你的类型的代码,你同时使用了 CalendarIDCCalendar,我不确定区别是什么.另外,请注意与 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆