【问题标题】:do network call from current ViewController or parent ViewController?从当前 ViewController 或父 ViewController 进行网络调用?
【发布时间】:2018-03-13 10:42:24
【问题描述】:

我正在编写一个在每个其他屏幕中都包含网络调用的应用程序。调用的结果将是特定屏幕的数据源。

问题是,我应该在父 viewController 中进行网络调用并在推送当前 viewController 之前注入数据还是推送 currentViewController 并在 viewDidLoad()/viewWillAppear() 上进行网络调用?

这两种方法对我来说都有意义。

【问题讨论】:

  • 您可以在其中任何一个中执行此操作。如果您的新 VC 完全依赖于数据,请先执行请求,如果成功接收到数据,则仅推送 VC。否则,只需将其加载到 viewDidLoad/viewWillAppear

标签: ios swift networking urlsession ios-lifecycle


【解决方案1】:

您向网络发出请求的位置实际上应该没有区别。您正在请求一些数据,您将不得不等待并呈现它。您的问题是应该在哪里等待接收数据。

正如@Scriptable 已经提到的,你可以做这两者中的任何一个。使用哪个取决于您希望拥有什么样的用户体验。这因情况而异,但一般来说,当我们创建资源时,我们通常会在当前屏幕上等待它,而当我们读取资源时,我们会在下一个屏幕上等待它:

  1. 例如,如果您在输入新用户名和密码后创建新用户(注册),则会出现一个指示符,一旦请求完成,您将导航到下一个屏幕“输入您的个人数据”,或者您将收到类似“用户已存在”的消息。
  2. 然后,例如,当您按下“我的朋友”时,您将被导航到首先看到活动指示器的列表。然后会出现列表,或者通常会出现一些屏幕,例如“我们无法加载您的数据,请重试。”

还有其他一些事情需要考虑,因为对于第二种情况,您可以添加更多功能,例如数据缓存。例如,许多消息传递应用程序会将您的聊天记录保存在本地,一旦您按下某个聊天线程,您将直接导航到查看缓存的内容,并且在加载和显示一些新消息后您可能会看到。

因此,如果我们回到您应该“调用”请求的位置,那么使用所有这些似乎您最好在展示新控制器之前或同时这样做。同时,我的意思是将其称为前一个视图控制器上的负载,但在收到新数据之前加载新的视图控制器。

如何最好地做到这一点是拥有一个数据模型。考虑这样的事情:

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()
}

现在我希望您能想象这样一个系统的美妙之处,例如,您的模型可以首先从某个本地缓存或数据库异步加载用户并调用委托,然后将请求调用到服务器并在您查看时再次调用委托控制器会针对任何情况显示适当的数据。

【讨论】:

  • 我想考虑的另一个因素是请求的错误处理。例如,如果请求失败,则显示空屏幕是不好的 UX,因此在推送下一个屏幕之前等待上一个屏幕上的成功响应是有意义的,这样如果请求失败,则可以显示错误屏幕/对话框。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-10
相关资源
最近更新 更多