Сделать сетевой вызов из текущего ViewController или родительского ViewController?
Я пишу приложение, которое содержит сетевой вызов на каждом другом экране. Результатом вызовов будет источник данных для определенного экрана.
Вопрос заключается в том, должен ли я выполнять сетевой вызов в родительском viewController и вводить данные перед тем, как нажать текущий viewController или push currentViewController, и выполнять сетевой вызов для viewDidLoad()/viewWillAppear()?
Оба метода имеют смысл для меня.
1 ответ
Где вы делаете запрос к сети, на самом деле не должно иметь никакого значения. Вы запрашиваете некоторые данные, которые вам придется подождать и представить их. Ваш вопрос заключается в том, где вы должны ждать получения данных.
Как уже упоминалось @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()
}
Теперь я надеюсь, что вы можете представить себе прелесть такой системы, что ваша модель может, например, сначала асинхронно загружать пользователей из некоторого локального кэша или базы данных и вызывать делегат, а затем вызывать запрос к серверу и снова вызывать делегат, пока ваш контроллер представления будет показывать соответствующие данные для любой ситуации.