从 Swift 中的多个 ViewController 收集数据
Collecting data from multiple ViewControllers in Swift
我有多个视图控制器(在导航堆栈中或可能不在导航堆栈中)并且每个控制器都根据用户输入收集一些数据。最后我需要在最后一个控制器中使用这些数据。
那么什么是最好的approach/design-pattern来实现这个场景?
可以通过多种方式实现,
沿着控制器传递数据,直到最后一个你想使用它的地方。
使用Singleton来存储来自每个控制器的数据。 Singleton 的唯一问题是它将持续存在于您的应用程序中。
您也可以将数据存储在UserDefaults
中。使用数据后,可以从UserDefaults
.
中清空
如果您仍然遇到任何问题,请告诉我。
好吧,在我看来,最好的方法是使用 MVVM (Model-View-ViewModel) 架构模式。
1) 对于您的每个 UIViewController,创建一个您认为是 "view model" 的单独 class(如果您愿意,可以从 NSObject 派生)属于那个UIViewController。您的视图控制器不允许访问模型 classes...现在这是您 "view model" classes 的工作。
2) 创建一个名为 DependencyManager 的单例。您的视图模型访问它以获取您的模型(或者至少是顶级模型,如果它们是分层的)以及它们可能需要的任何其他东西,例如网络服务等...... DependencyManager 充当您的单元测试可以注入替换的方式"mock" 版本的模型、网络服务等...何时需要测试每个 viewModel。
3) 创建包含数据的模型 classe(s)。您的视图控制器已经在 UI 控件中收集了各种数据,并将这些原始数据提供给您的 viewModel。 viewModels 可能会改变(或不改变)该数据并将其粘贴到它们从 DependencyManager 获取的适当模型中。
4) A ViewController 也被允许向他们的 viewModel 询问数据。因此,您的最后一个 ViewController 将从其 viewModel 中获取所需的任何数据。
记住:ViewControllers 不应该直接操作你的模型 classes。
此外,您的 ViewModel 不应引用任何 UI 对象,甚至不应引用其 ViewController。
旁注:我建议让每个 ViewModel 都遵循一个协议,该协议派生自一个名为 "MockableViewModelProtocol" 的空协议。您可以向类型为 MockableViewModelProtocol 的 DependencyManager 添加一个 属性,您的每个 ViewController 都可以在创建自己的 ViewModel 之前进行检查,以防单元测试为 ViewController 来代替。这种协议的另一个好处是可以快速清楚地理解 ViewModel 与其 ViewController 之间的关系。通常,您不仅会拥有属性和方法,还会拥有回调属性(闭包属性)。
好了。在我看来,这是最好的方法,不仅可以设计一种方法来管理和访问一堆 viewController 中的数据,而且还可以测试所有 classes 及其对数据的使用。
并且与所有当前声称 Storyboard 阻止依赖项注入并因此阻止测试您的 ViewControllers 的说法不同,这只是胡说八道。通过使用 DependencyManager,以便您的视图控制器测试在那里注入模拟,测试获得与将模拟直接注入 ViewController 相同的好处,但 ViewController 仍然由 Storyboard 实例化.我已经使用这种方法非常成功地发布了一个大型应用程序。
如果你有(一个导航栈)并且你需要从所有这个视图控制器收集数据
不使用 User-defaults 或 Singleton 的最佳方法
对于你的情况,你必须做视图控制器的容器来处理特定的 Process 。注意(UINavigationController
、UITabBarController
和 UISplitViewController
* 只是容器)
例如创建帐户 需要 4 个步骤来收集用户输入,每个步骤由 ViewController 表示,最后您需要将所有这些数据推送到 API 服务器 ,
因此创建子 viewController 的父容器ViewController 并在每一步后使用 Delegation
将数据传递给 ParentViewController
,父ViewController 将是领导者允许下一步并为此步骤提供数据。以下是如何开始创建您自己的容器 managing-view-controllers-with-container
不要使用单例
可以从应用程序的任何位置直接访问单例。你无法控制。你的代码中有很多耦合,使你的对象在未来难以测试
不使用 UserDefaults
UserDefaults 用于存储在应用程序之间持续存在的用户首选项 executions.Anything 存储在那里将一直保留到您删除为止,因此它不是一种在对象之间传递数据的机制。
所以你以后要用架构模式
建筑
每个 ViewController 只应照顾自己的屏幕。 viewController 也不应该互相认识。如果我们这样做,我们将消除我们的视图控制器之间的很多耦合 类.
所以您可以使用 Coordniator 作为处理所有导航的领导者
Coordniator
并在 MVVM-C
查看如何与 MVVM 集成
Viper 也使用这种技术来处理导航检查Viper
在我看来,处理此问题的最佳方法是使用 Flux 设计模式。 github 上有一个名为 ReSwift 的框架,它允许类似于 Redux.
的单向数据流
即使您不专门使用此框架,单向数据流的概念也非常适合这种情况。从每个视图控制器收集的状态将存在于任何视图控制器之外,视图控制器将保持彼此不可知,并且不需要任何父视图控制器来执行具有更高阶协调器的操作。然后在最后一个视图控制器上,访问状态并根据需要使用它。
另一个好处是,如果您需要通过容器将视图控制器分解为多个视图控制器,即一个屏幕有 2 个以上的视图控制器,您将不需要创建更多的基础设施来在它们之间传递数据,这在我的体验变得非常混乱。
我有多个视图控制器(在导航堆栈中或可能不在导航堆栈中)并且每个控制器都根据用户输入收集一些数据。最后我需要在最后一个控制器中使用这些数据。
那么什么是最好的approach/design-pattern来实现这个场景?
可以通过多种方式实现,
沿着控制器传递数据,直到最后一个你想使用它的地方。
使用Singleton来存储来自每个控制器的数据。 Singleton 的唯一问题是它将持续存在于您的应用程序中。
您也可以将数据存储在
UserDefaults
中。使用数据后,可以从UserDefaults
. 中清空
如果您仍然遇到任何问题,请告诉我。
好吧,在我看来,最好的方法是使用 MVVM (Model-View-ViewModel) 架构模式。
1) 对于您的每个 UIViewController,创建一个您认为是 "view model" 的单独 class(如果您愿意,可以从 NSObject 派生)属于那个UIViewController。您的视图控制器不允许访问模型 classes...现在这是您 "view model" classes 的工作。
2) 创建一个名为 DependencyManager 的单例。您的视图模型访问它以获取您的模型(或者至少是顶级模型,如果它们是分层的)以及它们可能需要的任何其他东西,例如网络服务等...... DependencyManager 充当您的单元测试可以注入替换的方式"mock" 版本的模型、网络服务等...何时需要测试每个 viewModel。
3) 创建包含数据的模型 classe(s)。您的视图控制器已经在 UI 控件中收集了各种数据,并将这些原始数据提供给您的 viewModel。 viewModels 可能会改变(或不改变)该数据并将其粘贴到它们从 DependencyManager 获取的适当模型中。
4) A ViewController 也被允许向他们的 viewModel 询问数据。因此,您的最后一个 ViewController 将从其 viewModel 中获取所需的任何数据。
记住:ViewControllers 不应该直接操作你的模型 classes。 此外,您的 ViewModel 不应引用任何 UI 对象,甚至不应引用其 ViewController。
旁注:我建议让每个 ViewModel 都遵循一个协议,该协议派生自一个名为 "MockableViewModelProtocol" 的空协议。您可以向类型为 MockableViewModelProtocol 的 DependencyManager 添加一个 属性,您的每个 ViewController 都可以在创建自己的 ViewModel 之前进行检查,以防单元测试为 ViewController 来代替。这种协议的另一个好处是可以快速清楚地理解 ViewModel 与其 ViewController 之间的关系。通常,您不仅会拥有属性和方法,还会拥有回调属性(闭包属性)。
好了。在我看来,这是最好的方法,不仅可以设计一种方法来管理和访问一堆 viewController 中的数据,而且还可以测试所有 classes 及其对数据的使用。
并且与所有当前声称 Storyboard 阻止依赖项注入并因此阻止测试您的 ViewControllers 的说法不同,这只是胡说八道。通过使用 DependencyManager,以便您的视图控制器测试在那里注入模拟,测试获得与将模拟直接注入 ViewController 相同的好处,但 ViewController 仍然由 Storyboard 实例化.我已经使用这种方法非常成功地发布了一个大型应用程序。
如果你有(一个导航栈)并且你需要从所有这个视图控制器收集数据
不使用 User-defaults 或 Singleton 的最佳方法
对于你的情况,你必须做视图控制器的容器来处理特定的 Process 。注意(UINavigationController
、UITabBarController
和 UISplitViewController
* 只是容器)
例如创建帐户 需要 4 个步骤来收集用户输入,每个步骤由 ViewController 表示,最后您需要将所有这些数据推送到 API 服务器 ,
因此创建子 viewController 的父容器ViewController 并在每一步后使用 Delegation
将数据传递给 ParentViewController
,父ViewController 将是领导者允许下一步并为此步骤提供数据。以下是如何开始创建您自己的容器 managing-view-controllers-with-container
不要使用单例
可以从应用程序的任何位置直接访问单例。你无法控制。你的代码中有很多耦合,使你的对象在未来难以测试
不使用 UserDefaults
UserDefaults 用于存储在应用程序之间持续存在的用户首选项 executions.Anything 存储在那里将一直保留到您删除为止,因此它不是一种在对象之间传递数据的机制。
所以你以后要用架构模式
建筑
每个 ViewController 只应照顾自己的屏幕。 viewController 也不应该互相认识。如果我们这样做,我们将消除我们的视图控制器之间的很多耦合 类.
所以您可以使用 Coordniator 作为处理所有导航的领导者 Coordniator
并在 MVVM-C
查看如何与 MVVM 集成Viper 也使用这种技术来处理导航检查Viper
在我看来,处理此问题的最佳方法是使用 Flux 设计模式。 github 上有一个名为 ReSwift 的框架,它允许类似于 Redux.
的单向数据流即使您不专门使用此框架,单向数据流的概念也非常适合这种情况。从每个视图控制器收集的状态将存在于任何视图控制器之外,视图控制器将保持彼此不可知,并且不需要任何父视图控制器来执行具有更高阶协调器的操作。然后在最后一个视图控制器上,访问状态并根据需要使用它。
另一个好处是,如果您需要通过容器将视图控制器分解为多个视图控制器,即一个屏幕有 2 个以上的视图控制器,您将不需要创建更多的基础设施来在它们之间传递数据,这在我的体验变得非常混乱。