Brief
What does VIP stand for then? View – Interactor – Presenter.
The VIP architecture for writing clean Swift code was introduced by Raymond Law. He created a clean-swift website where you can read about how to use VIP pattern in UIKit.
What Is VIP?
VIP is an architectural pattern like MVC, MVP or MVVM, MVI(Model-View-Intent). Its main difference is the clear logic separation between its components. VIP was created to fix MVC, or Massive View Controllers, a common problem in UIKit apps where View Controllers become the main object where code is kept and they become Massive in that most if not all of your logic is kept within them. This can also cause problems when testing your code.
VIP- The
View
is your app’s UI. This corresponds to a SwiftUI View. It sends requests to the interactor every time the user interacts with the UI. - The
Interactor
handles your app’s business logic, such as fetching data from the network or saving data in a database. When the view requests something, the interactor does the processing and sends the result as a response to the presenter. - The
Presenter
handles the presentation logic. It formats the received data into view models suitable for display and then passes it to the view.
Xcode Files Groups
FoldersModels
contains your domain’s model that you use across the scenes and isn’t tied to any specific scene. Each scene will have its scene-specific model that you’ll add later.
Scenes
contains all the scenes for your domain. A scene normally is a single screen or collection of screens for a feature. ScoopsAndScones has a single scene called CreateIceCream.
VIP
VIP FlowThe view creates a Request object and sends it to the interactor. The interactor takes the request object, performs work and sends the result as a Response to the presenter. The presenter then takes the response, formats the data into primitive types and sends the result as a ViewModel back to the view.
Creating a Model
enum CreateIceCream {
enum LoadIceCream {
struct Request {}
struct Response {
var iceCreamData: IceCream
}
struct ViewModel {
var cones: [String]
var flavors: [String]
var toppings: [String]
}
}
}
LoadIceCream
wraps the data model and represents a single functionality of your code, loading the ice cream and showing it in the view.
If the app had extra logic such as saving or deleting the data, you’d create separate enums called SaveIceCream
and DeleteIceCream
. Both of them would then have their own data models.
let request = CreateIceCream.LoadIceCream.Request()
Setting Up the View
var interactor: CreateIceCreamBusinessLogic?
This links the interactor with the view through the CreateIceCreamBusinessLogic
protocol.
Components communicate with each other through protocols. This makes each component of a scene less tightly tied to other components. The view asks the interactor to perform the business logic but doesn’t know how it’s done.
func fetchIceCream() {
let request = CreateIceCream.LoadIceCream.Request()
interactor?.loadIceCream(request: request)
}
Setting Up the Interactor
Create a Swift File named CreateIceCreamInteractor.swift in the CreateIceCream group.
protocol CreateIceCreamBusinessLogic {
func loadIceCream(request: CreateIceCream.LoadIceCream.Request)
}
class CreateIceCreamInteractor {
var presenter: CreateIceCreamPresentationLogic?
}
As already mentioned, protocols allow you to decouple your components. The interactor passes the response to the presenter but doesn’t know who’s presenting the data or how.
extension CreateIceCreamInteractor: CreateIceCreamBusinessLogic {
func loadIceCream(request: CreateIceCream.LoadIceCream.Request) {
// TODO
}
}
Presenting the Response
let iceCream = Bundle.main.decode(IceCream.self, from: "icecream.json")
let response = CreateIceCream.LoadIceCream.Response(iceCreamData: iceCream)
presenter?.presentIceCream(response: response)
The interactor is your app’s brain and handles all the business logic such as loading, deleting or saving the data. But there’s another component you could add to the interactor, called Worker.
VIP FlowsYou can have multiple workers for the interactor, with each handling a specific logic. If your app fetched the data from an API, you’d create a NetworkWorker and have all the networking logic inside. If your app saved the data using CoreData, you’d add a CoreDataWorker and so on.
Setting Up the Presenter
Create a Swift File named CreateIceCreamPresenter.swift in the CreateIceCream group.
protocol CreateIceCreamPresentationLogic {
func presentIceCream(response: CreateIceCream.LoadIceCream.Response)
}
class CreateIceCreamPresenter {
var view: CreateIceCreamDisplayLogic?
}
extension CreateIceCreamPresenter: CreateIceCreamPresentationLogic {
func presentIceCream(response: CreateIceCream.LoadIceCream.Response) {
// TODO
}
}
Sending ViewModel to View
let viewModel = CreateIceCream.LoadIceCream.ViewModel(
cones: response.iceCreamData.cones,
flavors: response.iceCreamData.flavors,
toppings: response.iceCreamData.toppings
)
view?.displayIceCream(viewModel: viewModel)
Creating a Display Logic Protocol
protocol CreateIceCreamDisplayLogic {
func displayIceCream(viewModel: CreateIceCream.LoadIceCream.ViewModel)
}
Adding a Configurator
You’ve created all the components, but you need to create instances of the presenter and interactor and connect them using a Configurator.
The configurator’s job is to instantiate and connect the components of the VIP cycle. This is where you create the unidirectional cycle between the VIP components. There’s only one configurator for every scene and you need to call it only once, so you’ll create it in a separate file.
Create a Swift File named CreateIceCreamConfigurator.swift
in the CreateIceCream group.
extension CreateIceCreamView {
func configureView() -> some View {
var view = self
let interactor = CreateIceCreamInteractor()
let presenter = CreateIceCreamPresenter()
view.interactor = interactor
interactor.presenter = presenter
presenter.view = view
return view
}
}
configureView() creates instances of the interactor and presenter and assigns the corresponding references.
Now, all you have to do is call the function on the view.
Open ContentView.swift
and replace CreateIceCreamView()
with:
CreateIceCreamView().configureView()
网友评论