我使用的是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
我取了所有 Contact
的数据库中,其中 ownerId
是我的用户的id-token,然后我用这个创建一个对象 contact
信息。然后对每个 Contact
对象实例,我得到了它对应的 User
在数据库中,当对象被初始化时。根据这篇文章。等到带有异步网络请求的swift for循环完成执行后,我使用的是 Dispatch
组,然后重新加载 UITableView
循环完成后,在 Dispatch
组已经结束。但当我 print
到控制台,我看到循环完成之前的 Contact
对象已经加载了它的 User
信息。
代码片段。
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")
}
}
}
}
这是因为该函数 getMyContacts() 正在执行一个Async任务,而控件会越过这个任务执行leave语句。你需要在 getMyContacts() 函数在for循环之外。
试试下面的代码。
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 "或一些concurency构造来加载数据并依次填充 "UITableViewController "中的单元格。
而且已经解决了。