您向网络发出请求的位置实际上应该没有区别。您正在请求一些数据,您将不得不等待并呈现它。您的问题是应该在哪里等待接收数据。
正如@Scriptable 已经提到的,你可以做这两者中的任何一个。使用哪个取决于您希望拥有什么样的用户体验。这因情况而异,但一般来说,当我们创建资源时,我们通常会在当前屏幕上等待它,而当我们读取资源时,我们会在下一个屏幕上等待它:
- 例如,如果您在输入新用户名和密码后创建新用户(注册),则会出现一个指示符,一旦请求完成,您将导航到下一个屏幕“输入您的个人数据”,或者您将收到类似“用户已存在”的消息。
- 然后,例如,当您按下“我的朋友”时,您将被导航到首先看到活动指示器的列表。然后会出现列表,或者通常会出现一些屏幕,例如“我们无法加载您的数据,请重试。”
还有其他一些事情需要考虑,因为对于第二种情况,您可以添加更多功能,例如数据缓存。例如,许多消息传递应用程序会将您的聊天记录保存在本地,一旦您按下某个聊天线程,您将直接导航到查看缓存的内容,并且在加载和显示一些新消息后您可能会看到。
因此,如果我们回到您应该“调用”请求的位置,那么使用所有这些似乎您最好在展示新控制器之前或同时这样做。同时,我的意思是将其称为前一个视图控制器上的负载,但在收到新数据之前加载新的视图控制器。
如何最好地做到这一点是拥有一个数据模型。考虑这样的事情:
class UsersModel {
private(set) var users: [User]?
}
对于用户,我们只需要一个列表,所以我所做的只是包装一个数组。因此,在您的情况下,我们应该可以选择加载这些用户:
extension UsersModel {
func fetchUsers() {
User.fetchAll { users, error in
self.users = users
self.error = error // A new property needed
}
}
}
现在添加了一个加载用户并将其分配给内部属性的方法。这足以满足我们在第一个视图控制器中的需要:
func goToUsers() {
let controller = UserListViewController()
let model = UserModel()
controller.model = model
model.fetchUsers()
navigationController.push(controller...
}
现在我们需要在第二个视图控制器内部建立通信。显然我们需要在viewDidLoad 上刷新,否则甚至会出现在视图中。但是我们还需要一些委托(或其他类型的连接),以便我们的视图控制器收到更改的通知:
func viewDidLoad() {
super.viewDidLoad()
self.refreshList()
self.model.delegate = self
}
在刷新时,我们现在应该拥有所需的所有数据:
func refreshList() {
guard let model = model else {
// TODO: no model? This looks like a developer bug
return
}
if let users = model.users {
self.users = users
tableView?.reloadData()
if users.count.isEmpty {
if let error = model.error {
// TODO: show error screen
} else {
// TODO: show no data screen
}
}
} else {
// TODO: show loading indicator screen
}
}
现在这里需要做的就是用委托完成模型:
extension UsersModel {
func fetchUsers() {
User.fetchAll { users, error in
self.users = users
self.error = error // A new property needed
self.delegate?.usersModel(self, didUpdateUsers: self.users)
}
}
}
而视图控制器只是简单地实现:
func usersModel(_ sender: UserModel, didUpdateUsers users: [User]?) {
refreshList()
}
现在我希望您能想象这样一个系统的美妙之处,例如,您的模型可以首先从某个本地缓存或数据库异步加载用户并调用委托,然后将请求调用到服务器并在您查看时再次调用委托控制器会针对任何情况显示适当的数据。