【问题标题】:Swift UITableViewController `await` until all data is loaded before rendering, or re-render after data has been loadedSwift UITableViewController `await` 直到在渲染之前加载所有数据,或者在加载数据后重新渲染
【发布时间】:2020-04-26 08:07:05
【问题描述】:

我使用的是 Swift 4。目标是在将地址簿呈现在视图中之前加载地址簿中的所有数据。在另一种语言(例如js)中,我可以在循环中的每个项目中使用await,然后再告诉视图呈现行。我正在寻找在Swift 4 和UITableViewController 中解决此问题的规范方法。

现在地址簿与Amplify 和GraphQL 一起存储在后端。我有一个User 形式的模型

type User @Model {
  id: ID!
  name: String!
  bio : String!
}

和Contact 的形式

type Contact @model {
  ownerId: ID!
  userId: ID!
  lastOpened: String
}

在ContactController: UITableViewController.viewDidLoad 中,我在ownerId 是我的用户标识令牌的数据库中获取所有Contact,然后我使用此contact 信息创建一个对象。然后对于每个Contact 对象实例,我在初始化对象时在数据库中获取其对应的User。根据这篇文章:Wait until swift for loop with asynchronous network requests finishes executing,我正在使用Dispatch 组,然后在循环完成并且Dispatch 组结束后重新加载UITableView。但是当我print 到控制台时,我看到循环在Contact 对象加载其User 信息之前完成。

代码sn-ps:

class ContactsController: UITableViewController, UISearchResultsUpdating {

        var dataSource : [Contact] = []


        override func viewDidLoad() {

            super.viewDidLoad()

            let fetchContactGrp = DispatchGroup()

            fetchContactGrp.enter()

            self.getMyContacts(){ contacts in

                for contact in contacts {

                    let _contactData = Contact(
                          userId     : contact.userId
                        , contactId  : contact.id
                        , timeStamp  : contact.timeStamp
                        , lastOpened : contact.lastOpened
                        , haveAccount: true
                    )

                    _contactData.loadData()
                    self.dataSource.append(_contactData)
                }

            }


            fetchContactGrp.leave()

            DispatchQueue.main.async{
                 self.tableView.reloadData()
            }
        }

    }

函数self.getMyContacts只是一个标准的GraphQL查询:

func getMyContacts( callBack: @escaping ([Contact]) -> Void ){

    let my_token = AWSMobileClient.default().username
    let contact = Contact.keys
    let predicate = contact.ownerId == my_token! 

    _ = Amplify.API.query(from: Contact.self, where: predicate) { (event) in
        switch event {
            case .completed(let result):
                switch result {
                    case .success(let cts):
                        /// @On success, output a user list
                        callBack(cts)
                    case .failure(let error):
                        break
                }
            case .failed(let error):
                break
            default:
                break
        }
    }
}

而Contact 对象从数据库中加载User 数据:

class Contact {

        let userId: String!
        let contactId: String!

        var name : String
        var bio  : String
        var website: String

        let timeStamp: String
        let lastOpened: String


        init( userId: String, contactId: String, timeStamp: String, lastOpened: String, haveAccount: Bool){

            self.userId     = userId
            self.contactId  = contactId
            self.timeStamp  = timeStamp
            self.lastOpened = lastOpened
            self.haveAccount = haveAccount


            self.name = ""
            self.bio  = ""
            self.website = ""

        }

        func loadData(){

            /// @use: fetch user data from db and populate field on initation
            let _ = Amplify.API.query(from: User.self, byId: self.userId) { (event) in

                switch event {
                    case .completed(let res):
                        switch res{
                            case .success (let musr):
                                if (musr != nil){

                                    let userData = musr!
                                    let em    = genEmptyString()
                                    self.name = (userData.name == em) ? "" : userData.name
                                    self.bio  = (userData.bio == em)  ? "" : userData.bio
                                    self.website = (userData.website == em) ? "" : userData.website

                                    print(">> amplify.query: \(self.name)")

                                } else {
                                    break
                                }
                            default:
                               break
                        }
                    default:
                        print("failed")
                }
            }
        }


    }

【问题讨论】:

  • 你使用DispatchGroup的方式不对。您必须向loadData() 添加一个完成处理程序,然后在viewDidLoad 的循环内调用enter() 并留在loadData() 的完成处理程序中。添加group.notify 并在闭包中重新加载表格视图。

标签: ios swift uitableview graphql


【解决方案1】:

这是因为函数 getMyContacts() 正在执行 Async 任务,并且控件会越过该任务并执行 leave 语句。您需要在 for 循环外的 getMyContacts() 函数中调用 leave 语句。

试试下面的代码:

override func viewDidLoad() {

            super.viewDidLoad()

            let fetchContactGrp = DispatchGroup()

            fetchContactGrp.enter()

            self.getMyContacts(){ contacts in

                for contact in contacts {

                    let _contactData = Contact(
                          userId     : contact.userId
                        , contactId  : contact.id
                        , timeStamp  : contact.timeStamp
                        , lastOpened : contact.lastOpened
                        , haveAccount: true
                    )

                    _contactData.loadData()
                    self.dataSource.append(_contactData)
                }
                fetchContactGrp.leave()
            }


            fetchContactGrp.wait()

            DispatchQueue.main.async{
                 self.tableView.reloadData()
            }
        }

【讨论】:

  • 不要等待,没有理由这样做。 DispatchGroup 语法与问题中的一样错误。
  • @vadian 你能提供一个替代解决方案吗?我用了这个例子:stackoverflow.com/questions/35906568/…
  • 请阅读我对这个问题的评论。 DispatchGroup 唯一有用的是在异步函数的完成处理程序中调用 leave()。问题和答案都忽略了loadData()的异步行为
  • @UmerFaroq 我得到了与wait 之前相同的行为。需要明确的是,当我print 执行步骤时,它进入、等待和离开调度组,在初始化Contact 并加载用户数据之前。 @vadian 好的,我有点明白你现在在说什么。请问可以提供代码吗?
  • 再一次,您必须在loadData 中添加一个完成处理程序,就像在getMyContacts 中一样。
【解决方案2】:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-03
    • 2018-02-08
    • 1970-01-01
    • 2016-11-22
    • 2023-03-29
    • 2019-05-28
    • 2016-06-09
    相关资源
    最近更新 更多