翻译:Clean Swift iOS Architecture for Fixing Massive View Controller (二)

节选翻译来自 Clean Swift iOS Architecture for Fixing Massive View Controller 系列第二篇。
原作者:Raymond
翻译者:Roc Zhang

第一次尝试翻译,如有任何问题还请留言指出。

E-mail: roczhang9673@gmail.com
Weibo: @张鹏roczhang
Twitter: @Lighters9673


本文接Clean Swift iOS Architecture for Fixing Massive View Controller 翻译系列第一篇继续。

2.Interactor

Interactor 包括了你 App 中的业务逻辑。用户在 UI 上点击与滑动是为了和你的 App 进行交互。ViewController 从用户界面中收集用户的输入并将其传给 interactor,然后检索一些模型并要求一些 workers 来完成工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import UIKit
protocol CreateOrderInteractorInput
{
func doSomething(request: CreateOrderRequest)
}
protocol CreateOrderInteractorOutput
{
func presentSomething(response: CreateOrderResponse)
}
class CreateOrderInteractor: CreateOrderInteractorInput
{
var output: CreateOrderInteractorOutput!
var worker: CreateOrderWorker!
// MARK: Business logic
func doSomething(request: CreateOrderRequest)
{
// NOTE: Create some Worker to do the work
worker = CreateOrderWorker()
worker.doSomeWork()
// NOTE: Pass the result to the Presenter
let response = CreateOrderResponse()
output.presentSomething(response)
}
}

CreateOrderInteractorInput 和 CreateOrderInteractorOutput 协议

CreateOrderInteractorInput 协议指定了 CreateOrderInteractor 组件的输入(它遵循这个协议),CreateOrderInteractorOutput 协议则指明了输出。

我们在 CreateOrderInteractorInput 协议中看到了与在 CreateOrderViewControllerOutput 协议中相同的 doSomething() 方法。CreateOrderViewController 的输出连接着 CreateOrderInteractor 的输入。

CreateOrderInteractorOutput 协议包含一个 presentSomething() 方法,CreateOrderInteractor 的输出需要支持 presentSomething() 。提示:此处的 output 将是 VIP Cycle 中的 P。

这里另一件要注意的事情则是 doSomething() 方法的参数是 CreateOrderRequest 类型的请求对象,这和 CreateOrderViewControllerOutput 协议中的方法签名相同。Interactor 将在这个请求对象中检索出必要的数据来完成工作。
同样,presentSomething() 的参数是一个 CreateOrderResponse 类型的响应对象。

output 与 worker 变量

output 变量是一个遵循 CreateOrderInteractorOutput 协议的对象,虽然我们知道这这里它将会成为 presenter,但同样,这也不是必须的。

CreateOrderWorker 类型的 worker 变量是一个用于创建新订单的特定对象。由于创建订单可能会涉及到 Core Data 的持久化工作以及进行网络请求,这些工作如果全部由 interactor 来完成的话它所要做的任务就太多了。要记住,interactor 还需要去验证订单,这也同样可以被提取出来,成为 interactor 的 worker 来完成。

控制流

当 CreateOrderInteractor 的输入 (即 CreateOrderViewController) 调用 doSomething() 时,它将首先创建 worker 对象并通过调用 doSomeWork() 来要求 worker 去做一些工作。然后它将构造一个响应对象并调用输出的 presentSomething() 。

下面我们来快速的了解一下 worker。

3.worker

一个 Profile View 可能需要从 Core Data 中获取用户,下载照片,允许用户点赞与关注等。你不想让 interactor 陷入这些任务中不知所措,相反,我们可以将其拆解成许多的 workers,每个 worker 完成一件事,之后你还可以在其他地方复用 worker。

CreateOrderWorker 非常简单,它仅仅提供了一个接口,并实现它可以对 interactor 完成的工作。

1
2
3
4
5
6
7
8
9
10
11
12
import UIKit
class CreateOrderWorker
{
// MARK: Business Logic
func doSomeWork()
{
// NOTE: Do the work
}
}

4. Presenter

在 interactor 产生出一些结果之后,它会将其传给 presenter,presenter 会将其组织成适合展示的 view model,然后将 view model 传回给 view controll 以展示给用户。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import UIKit
protocol CreateOrderPresenterInput
{
func presentSomething(response: CreateOrderResponse)
}
protocol CreateOrderPresenterOutput: class
{
func displaySomething(viewModel: CreateOrderViewModel)
}
class CreateOrderPresenter: CreateOrderPresenterInput
{
weak var output: CreateOrderPresenterOutput!
// MARK: Presentation logic
func presentSomething(response: CreateOrderResponse)
{
// NOTE: Format the response from the Interactor and pass the result back to the View Controller
let viewModel = CreateOrderViewModel()
output.displaySomething(viewModel)
}
}

CreateOrderPresenterInput 与 CreateOrderPresenterOutput 协议

CreateOrderPresenterInput 协议指定了 CreateOrderPresenter 组件的输入,CreateOrderPresenterOutput 则指明输出。

现在,presentSometing() 和 displaySomething() 方法不再需要说明了。CreateOrderResponse 参数在 interactor 和 presenter 间的边界中传递,CreateOrderViewModel 在 presenter 和 view controller 间的边界中传递,VIP 环完成了。

output 变量

output 变量是一个遵循 CreateOrderPresenterOutput 协议的对象,尽管我们已经知道这里它就是 view controller,但同样它也可以不是。在这里一点细微的差别是,我们需要让 output 作为一个 weak 变量,当 CreateOrder 场景不被需要它的组件都要被 deallocate 的时候来避免循环引用。

控制流

当 CreateOrderInteractor 的输出被连接到 CreateOrderPresenter 的输入时,presentSomthing() 方法将在 interact 完成它的任务时被调用。它会构造 view model 对象,并在 output 上调用 displaySomething()。

之前我们说过会回过头来继续 view controller 中的 displaySomething() 方法,这是 VIP 环中的最后一步。它会将 view model 中任意的数据展示给用户。例如,我们也许想要在文本框中显示客户的姓名:nameTextField.text = viewModel.name。

恭喜!你刚刚学到了 Clean Swift 中的精华,你现在应该能够将业务与展示逻辑从你的用户界面代码中提取出来了。别担心,在介绍例子前我们不会就这样结束,但让我们先来说完 Clean Swift 中的其他组件。

5. Router

当用户点击 next 按钮导航到 storyboard 中的下一个场景时,一个 segue 将会被触发,一个新的 view controller 会被展示出来。Router 的作用就是将导航逻辑从 view controller 中提取出来,它同样是将数据传给下一个场景的最佳位置。最终的结果就是,view controller 中仅仅剩下控制各类视图的工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import UIKit
protocol CreateOrderRouterInput
{
func navigateToSomewhere()
}
class CreateOrderRouter
{
weak var viewController: CreateOrderViewController!
// MARK: Navigation
func navigateToSomewhere()
{
// NOTE: Teach the router how to navigate to another scene. Some examples follow:
// 1. Trigger a storyboard segue
// viewController.performSegueWithIdentifier("ShowSomewhereScene", sender: nil)
// 2. Present another view controller programmatically
// viewController.presentViewController(someWhereViewController, animated: true, completion: nil)
// 3. Ask the navigation controller to push another view controller onto the stack
// viewController.navigationController?.pushViewController(someWhereViewController, animated: true)
// 4. Present a view controller from a different storyboard
// let storyboard = UIStoryboard(name: "OtherThanMain", bundle: nil)
// let someWhereViewController = storyboard.instantiateInitialViewController() as! SomeWhereViewController
// viewController.navigationController?.pushViewController(someWhereViewController, animated: true)
}
// MARK: Communication
func passDataToNextScene(segue: UIStoryboardSegue)
{
// NOTE: Teach the router which scenes it can communicate with
if segue.identifier == "ShowSomewhereScene" {
passDataToSomewhereScene(segue)
}
}
func passDataToSomewhereScene(segue: UIStoryboardSegue)
{
// NOTE: Teach the router how to pass data to the next scene
// let someWhereViewController = segue.destinationViewController as! SomeWhereViewController
// someWhereViewController.output.name = viewController.output.name
}
}

CreateOrderRouterInput 协议

CreateOrderRouterInput协议指明了它的各类路由:场景可以导航到的 view controller。navigateToSomewhere() 方法告诉了 view controller: 如果你把我当作你的 router,我得知道该如何导航到一个叫做 somewhere 的场景。

正如你在 navigateToSomewhere() 方法中的注释中看到的那样,router 在它可以导航的场景数量上非常灵活。我将会在另一篇文章中关于 router 的全部。

viewController 变量

viewController 变量仅仅只是一个指向使用此 router 的 viewController 的引用。它是一个 weak 变量来避免循环引用问题,它被一个 configurator 配置好(稍后你就会看到)。Apple 在 segue 间转场的方式是将所有的 present 和 push 方法都放到了 UIViewController 类中。所以在这里我们需要 view controller 引用以让我们能够在 router 中调用这些方法。

passDataToNextScene() 与 passDataToSomewhereScene()

passDataToNextScene() 和 passDataToSomewhereScene() 方法提供了一种传递数据到下一个场景中去的方式。通常你会把同样的代码放到 prepareForSegue() 中。passDataToNextScene() 将试图匹配 segue ID 以分发到更具体的会传递实际的数据的 passDataToSomewhereScene() 方法。

See you in the next

第二篇先翻译到这里,坚持每周翻译一部分吧😂,下周我们再接着 configurator 继续。