SwiftUI 异步数据获取

Posted

技术标签:

【中文标题】SwiftUI 异步数据获取【英文标题】:SwiftUI async Data fetch 【发布时间】:2020-03-06 12:01:09 【问题描述】:

我正在尝试学习 SwiftUI 并使用电影数据库 API 创建一个电影搜索应用程序

一旦滚动到列表末尾,我想获取新数据。我在列表中使用 ForEach 找到了一个可能的解决方案,并检查列表何时到达最后一项,然后执行 onAppear() 调用。

In SwiftUI, where are the control events, i.e. scrollViewDidScroll to detect the bottom of list data

当第一页加载完毕后,如何从搜索中加载新页面?

您可以在此处找到该项目,该项目太大,无法发布 https://github.com/aspnet82/MovieSearch

SearchMovieManager -> 进行 fetch 调用,我使用了 Dispatch.main.async

应用应该做什么

    从 API 获取数据并显示第一页 -> WORKING 当列表滚动到最后一项时,在下一页上发出另一个获取请求以加载更多数据 -> 这不起作用,它仅适用于请求的第二页。它总是在第二次 fetch 调用后显示相同的数据。

我认为问题出在 GCD 中,但我不知道如何排队以使其自动运行

更新:我找到的解决方法:

List(searchMovieManager.allMovies)  movie in
     Text(movie.title)

Text("load more").onTapGesture 
     fetchNextPage(obs: self.searchMovieManager, page: self.searchMovieManager.pageTofetch)
 

我认为可能是好的解决方案,一旦我点击按钮,它就会添加新页面,并且可能还可以控制数据下载?

感谢您的帮助:)

【问题讨论】:

0。在提出问题之前你做了一些工作,这很棒。 1.永远不要像github.com/aspnet82/MovieSearch/blob/master/Discover_Movie/…那样将API密钥发布到开源。 2.代码很长,需要学习。您是否有可能展示一些关键点,哪里出了问题? 3.我发现了一些方面,当你有 dataTask - 在它的闭包内你应该有[weak self]。 4. 您是否尝试将您的let movies = try decoder... 放入 Dispatch 异步块中? 请检查this example project,因为它与您的要求非常相似。特别是this view 和its model。附言这不是自我宣传,只是关于分页和附加数据列表的问题是我决定保存在某个地方以记住的流行事物。 我尝试将 try 解码器放在 Dispatch 异步块中,但返回错误。我也在更新问题以提供更多上下文 如果onTapGesture你与onAppear交换?喜欢:List ForEach(your_items) item in Cell(with: item) Text("loading...") .onAppear self.viewModel.loadNextPage() 见swiftui-lab.com/a-powerful-combo第二个例子正是你想要的。 【参考方案1】:

尝试下一个,它使用锚首选项和模拟异步操作的简单模型将一些记录添加到 ScrollView(或 List)

import SwiftUI

struct PositionData: Identifiable 
    let id: Int
    let center: Anchor<CGPoint>


struct Positions: PreferenceKey 
    static var defaultValue: [PositionData] = []
    static func reduce(value: inout [PositionData], nextValue: () -> [PositionData]) 
        value.append(contentsOf: nextValue())
    


struct Data: Identifiable 
    let id: Int


class Model: ObservableObject 
    var _flag = false
    var flag: Bool 
        get 
            _flag
        
        set(newValue) 
            if newValue == true 
                _flag = newValue

                DispatchQueue.main.asyncAfter(deadline: .now() + 2) 
                    self._flag = false
                    self.rows += 20
                    print("done")
                
            
        
    
    @Published var rows = 20

struct ContentView: View 
    @ObservedObject var model = Model()
    var body: some View 
        List 
            ForEach(0 ..< model.rows, id:\.self)  i in
                Text("row \(i)").font(.largeTitle).tag(i)
            
            Rectangle().tag(model.rows).frame(height: 0).anchorPreference(key: Positions.self, value: .center)  (anchor) in
                [PositionData(id: self.model.rows, center: anchor)]
            .id(model.rows)
        
        .backgroundPreferenceValue(Positions.self)  (preferences) in
            GeometryReader  proxy in
                Rectangle().frame(width: 0, height: 0).position(self.getPosition(proxy: proxy, tag: self.model.rows, preferences: preferences))
            
        
    
    func getPosition(proxy: GeometryProxy, tag: Int, preferences: [PositionData])->CGPoint 
        let p = preferences.filter( (p) -> Bool in
            p.id == tag
            )
        if p.isEmpty  return .zero 
        if proxy.size.height - proxy[p[0].center].y > 0 && model.flag == false 
            self.model.flag.toggle()
            print("fetch")
        
        return .zero
    




struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    

这就是它的样子......

【讨论】:

ios 13.6 和 14.5 上测试。该代码有效。你能添加一些cmets吗?我已经能够看懂大部分代码了。 @Darkwonder 我很高兴回答有关您有疑问的每一行代码的具体问题。

以上是关于SwiftUI 异步数据获取的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 和 CloudKit 异步数据加载

SWIFTUI Firebase 检索子集合数据

iOS - SwiftUI - 执行异步操作后导航到下一个屏幕

使用 SwiftUI 时异步填充/预填充多个用户可变的“@State”值?

swiftui 异步初始化

使用异步函数初始化应用程序 | SwiftUI