SwiftVIPER is an sample iOS App written in Swift using the VIPER architecture.
Also SwiftVIPER is not a strict VIPER architecture.
Part of the project is a unique way.
-
Clone this repository.
git clone git@github.com:yokurin/Swift-VIPER-iOS.git
-
Open
SwiftVIPER.xcodeproj
in Xcode. -
Run
SwiftVIPER is not a strict VIPER architecture.
Part of the project is a unique way. This is just a suggestion.
View must implement Viewable. Viewable has Default Extension.
※ View is not just View like UIView etc in this case.
protocol Viewable: AnyObject {
func push(_ vc: UIViewController, animated: Bool)
func present(_ vc: UIViewController, animated: Bool)
func pop(animated: Bool)
func dismiss(animated: Bool)
func dismiss(animated: Bool, completion: @escaping (() -> Void))
}
extension Viewable where Self: UIViewController {
func push(_ vc: UIViewController, animated: Bool) {
self.navigationController?.pushViewController(vc, animated: animated)
}
func present(_ vc: UIViewController, animated: Bool) {
self.present(vc, animated: animated, completion: nil)
}
func pop(animated: Bool) {
self.navigationController?.popViewController(animated: animated)
}
func dismiss(animated: Bool) {
self.dismiss(animated: animated, completion: nil)
}
func dismiss(animated: Bool, completion: @escaping (() -> Void)) {
self.dismiss(animated: animated, completion: completion)
}
}
Example
protocol ViewInputs: AnyObject {
}
protocol ViewOutputs: AnyObject {
func viewDidLoad()
}
final class ListViewController: UIViewController {
internal var presenter: ViewOutputs?
override func viewDidLoad() {
super.viewDidLoad()
presenter?.viewDidLoad()
}
...
}
extension ListViewController: ListViewInputs {}
extension ListViewController: Viewable {}
Interactor must implement Interactorable. But Interactorable has no properties.
protocol Interactorable {
// nop
}
Example
protocol InteractorOutputs: AnyObject {
}
final class Interactor: Interactorable {
weak var presenter: InteractorOutputs?
}
Presenter must implement Presenterable.
protocol Presenterable {
associatedtype I: Interactorable
associatedtype R: Routerable
var dependencies: (interactor: I, router: R) { get }
}
Example
/// Must not import UIKit
typealias PresenterDependencies = (
interactor: Interactor,
router: RouterOutput
)
final class Presenter: Presenterable {
...
internal var entities: Entities
private weak var view: ViewInputs!
let dependencies: PresenterDependencies
init(entities: Entities,
view: ViewInputs,
dependencies: PresenterDependencies)
{
self.view = view
self.entities = entities
self.dependencies = dependencies
}
...
}
Entity has no protocol.
Example
struct EntryEntity {}
final class Entities {
let entryEntity: EntryEntity
var entities: [SomeEntity] = []
init(entryEntity: EntryEntity) {
self.entryEntity = entryEntity
}
}
Router must implement Routerable.
protocol Routerable {
var view: Viewable! { get }
func dismiss(animated: Bool)
func dismiss(animated: Bool, completion: @escaping (() -> Void))
func pop(animated: Bool)
}
extension Routerable {
func dismiss(animated: Bool) {
view.dismiss(animated: animated)
}
func dismiss(animated: Bool, completion: @escaping (() -> Void)) {
view.dismiss(animated: animated, _completion: completion)
}
func pop(animated: Bool) {
view.pop(animated: animated)
}
}
Example
struct RouterInput {
private func view(entryEntity: EntryEntity) -> ViewController {
let view = ViewController()
let interactor = Interactor()
let dependencies = PresenterDependencies(interactor: interactor, router: RouterOutput(view))
let presenter = Presenter(entities: Entities(entryEntity: entryEntity), view: view, dependencies: dependencies)
view.presenter = presenter
interactor.presenter = presenter
return view
}
func push(from: Viewable, entryEntity: EntryEntity) {
let view = self.view(entryEntity: entryEntity)
from.push(view, animated: true)
}
func present(from: Viewable, entryEntity: EntryEntity) {
let nav = UINavigationController(rootViewController: view(entryEntity: entryEntity))
from.present(nav, animated: true)
}
}
final class RouterOutput: Routerable {
private(set) weak var view: Viewable!
init(_ view: Viewable) {
self.view = view
}
func transitionToOther() {
OtherRouterInput().push(from: view, entryEntity: OtherEntryEntity())
}
}
WIP ...
WIP ...
- Xcode 10.0+
- Swift 4.2+
git clone git@github.com:yokurin/Swift-VIPER-iOS.git
Tsubasa Hayashi, yoku.rin.99@gmail.com
SwiftVIPER is available under the MIT license. See the LICENSE file for more info.