├── .gitignore
├── .gitmodules
├── .travis.yml
├── Assets
├── Instructions
│ └── module_creation.gif
└── logo_light.png
├── Example
├── AppDelegate.swift
├── AppModule.swift
├── Base.lproj
│ └── LaunchScreen.storyboard
├── Info.plist
└── modules
│ ├── Cool
│ ├── CoolInteractor.swift
│ ├── CoolModuleApi.swift
│ ├── CoolPresenter.swift
│ ├── CoolRouter.swift
│ ├── CoolView.swift
│ └── viewModels
│ │ └── UserSettings.swift
│ ├── Home
│ ├── Home.storyboard
│ ├── HomeDisplayData.swift
│ ├── HomeInteractor.swift
│ ├── HomePresenter.swift
│ ├── HomeRouter.swift
│ └── HomeView.swift
│ ├── Perfect
│ ├── PerfectInteractor.swift
│ ├── PerfectModuleApi.swift
│ ├── PerfectPresenter.swift
│ ├── PerfectRouter.swift
│ └── PerfectView.swift
│ ├── Second
│ ├── Second.storyboard
│ ├── SecondDisplayData.swift
│ ├── SecondInteractor.swift
│ ├── SecondPresenter.swift
│ ├── SecondRouter.swift
│ ├── SecondView.swift
│ └── SecondViewPad.swift
│ ├── Simple
│ ├── SimpleDisplayData.swift
│ ├── SimpleInteractor.swift
│ ├── SimpleModuleApi.swift
│ ├── SimplePresenter.swift
│ ├── SimpleRouter.swift
│ └── SimpleView.swift
│ └── TableOfContents
│ ├── TableOfContentsDisplayData.swift
│ ├── TableOfContentsInteractor.swift
│ ├── TableOfContentsModuleApi.swift
│ ├── TableOfContentsPresenter.swift
│ ├── TableOfContentsRouter.swift
│ └── TableOfContentsView.swift
├── ExampleTests
├── HomeTests.swift
└── Info.plist
├── LICENSE
├── Package.swift
├── README.md
├── Viperit.podspec
├── Viperit.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── xcshareddata
│ └── xcschemes
│ ├── Viperit.xcscheme
│ └── ViperitTests.xcscheme
├── Viperit
├── Core
│ ├── DisplayData.swift
│ ├── Interactor.swift
│ ├── Module.swift
│ ├── Presenter.swift
│ ├── Router.swift
│ ├── UserInterface.swift
│ ├── ViperComponent.swift
│ ├── ViperitError.swift
│ └── ViperitModule.swift
├── Extensions
│ ├── String+.swift
│ ├── UserInterface+Collection.swift
│ ├── UserInterface+Split.swift
│ ├── UserInterface+SwiftUI.swift
│ └── UserInterface+Table.swift
└── Info.plist
├── ViperitTests
├── ExtensionsTests.swift
├── Info.plist
├── ModuleTests.swift
├── PresenterTests.swift
├── RouterTests.swift
└── Sample
│ ├── Modules
│ ├── CodeModule
│ │ ├── CodeModuleDisplayData.swift
│ │ ├── CodeModuleInteractor.swift
│ │ ├── CodeModuleModuleApi.swift
│ │ ├── CodeModulePresenter.swift
│ │ ├── CodeModuleRouter.swift
│ │ └── CodeModuleView.swift
│ ├── Sample
│ │ ├── Sample.storyboard
│ │ ├── SampleDisplayData.swift
│ │ ├── SampleInteractor.swift
│ │ ├── SamplePresenter.swift
│ │ ├── SampleRouter.swift
│ │ ├── SampleView.swift
│ │ └── SampleViewPad.swift
│ ├── SwiftUI
│ │ ├── SwiftUIInteractor.swift
│ │ ├── SwiftUIModuleApi.swift
│ │ ├── SwiftUIPresenter.swift
│ │ ├── SwiftUIRouter.swift
│ │ └── SwiftUIView.swift
│ ├── SwiftUIWithEnv
│ │ ├── SwiftUIWithEnvInteractor.swift
│ │ ├── SwiftUIWithEnvModuleApi.swift
│ │ ├── SwiftUIWithEnvPresenter.swift
│ │ ├── SwiftUIWithEnvRouter.swift
│ │ └── SwiftUIWithEnvView.swift
│ └── XibModule
│ │ ├── XibModule.xib
│ │ ├── XibModuleDisplayData.swift
│ │ ├── XibModuleInteractor.swift
│ │ ├── XibModuleModuleApi.swift
│ │ ├── XibModulePresenter.swift
│ │ ├── XibModuleRouter.swift
│ │ └── XibModuleView.swift
│ ├── TestCleanModules.swift
│ └── TestModules.swift
└── codecov.yml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xcuserstate
23 |
24 | ## Obj-C/Swift specific
25 | *.hmap
26 | *.ipa
27 | *.dSYM.zip
28 | *.dSYM
29 |
30 | ## Playgrounds
31 | timeline.xctimeline
32 | playground.xcworkspace
33 |
34 | # Swift Package Manager
35 | #
36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
37 | # Packages/
38 | .build/
39 |
40 | # CocoaPods
41 | #
42 | # We recommend against adding the Pods directory to your .gitignore. However
43 | # you should judge for yourself, the pros and cons are mentioned at:
44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
45 | #
46 | # Pods/
47 |
48 | # Carthage
49 | #
50 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
51 | # Carthage/Checkouts
52 |
53 | Carthage/Build
54 |
55 | # fastlane
56 | #
57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
58 | # screenshots whenever they are needed.
59 | # For more information about the recommended setup visit:
60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
61 |
62 | fastlane/report.xml
63 | fastlane/Preview.html
64 | fastlane/screenshots
65 | fastlane/test_output
66 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "Templates/Viperit"]
2 | path = Templates/Viperit
3 | url = https://github.com/ferranabello/ViperitTemplates.git
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | osx_image: xcode12.5
3 |
4 | branches:
5 | only:
6 | - develop
7 | - master
8 | env:
9 | - LC_CTYPE=en_US.UTF-8 LANG=en_US.UTF-8
10 | before_install:
11 | - rvm install ruby-2.4.3
12 | - gem install xcpretty -N
13 | script:
14 | - set -o pipefail
15 | - xcodebuild -scheme Viperit -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -c
16 | - xcodebuild test -scheme ViperitTests -destination 'platform=iOS Simulator,name=iPhone 12 Pro,OS=14.5' | xcpretty -c
17 | after_success:
18 | - bash <(curl -s https://codecov.io/bash)
19 |
--------------------------------------------------------------------------------
/Assets/Instructions/module_creation.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ferranabello/Viperit/49d2d5a7439d7c6deed8e56aa3d80a70ec58a07d/Assets/Instructions/module_creation.gif
--------------------------------------------------------------------------------
/Assets/logo_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ferranabello/Viperit/49d2d5a7439d7c6deed8e56aa3d80a70ec58a07d/Assets/logo_light.png
--------------------------------------------------------------------------------
/Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 11/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Viperit
11 |
12 | @UIApplicationMain
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 |
15 | var window: UIWindow?
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | window = UIWindow(frame: UIScreen.main.bounds)
19 | let module = AppModules.tableOfContents.build()
20 | module.router.show(inWindow: window, embedInNavController: true, setupData: nil, makeKeyAndVisible: true)
21 | return true
22 | }
23 |
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/Example/AppModule.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppModule.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 11/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | //MARK: - Application modules
13 | enum AppModules: String, ViperitModule {
14 | case home
15 | case second
16 | case tableOfContents
17 | case cool
18 | case simple
19 | case perfect
20 |
21 | var viewType: ViperitViewType {
22 | switch self {
23 | case .cool, .perfect: return .swiftUI
24 | case .tableOfContents: return .code
25 | case .simple: return .code
26 | default: return .storyboard
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Example/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIRequiredDeviceCapabilities
26 |
27 | armv7
28 |
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Example/modules/Cool/CoolInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CoolInteractor.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - CoolInteractor Class
13 | final class CoolInteractor: Interactor {
14 | }
15 |
16 | // MARK: - CoolInteractor API
17 | extension CoolInteractor: CoolInteractorApi {
18 | }
19 |
20 | // MARK: - Interactor Viper Components Api
21 | private extension CoolInteractor {
22 | var presenter: CoolPresenterApi {
23 | return _presenter as! CoolPresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Example/modules/Cool/CoolModuleApi.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CoolModuleApi.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Viperit
10 |
11 | //MARK: - CoolRouter API
12 | protocol CoolRouterApi: RouterProtocol {
13 | func goToPerfect()
14 | }
15 |
16 | //MARK: - CoolPresenter API
17 | protocol CoolPresenterApi: PresenterProtocol {
18 | func settings() -> UserSettings
19 | func changeScore()
20 | func changeRandomName()
21 | func showAnotherModule()
22 | }
23 |
24 | //MARK: - CoolInteractor API
25 | protocol CoolInteractorApi: InteractorProtocol {
26 | }
27 |
--------------------------------------------------------------------------------
/Example/modules/Cool/CoolPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CoolPresenter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - CoolPresenter Class
13 | final class CoolPresenter: Presenter {
14 | var _viewModel: UserSettings!
15 |
16 | override func viewHasAppeared() {
17 | print("The swiftUI host has appeared!")
18 | }
19 | }
20 |
21 | // MARK: - CoolPresenter API
22 | extension CoolPresenter: CoolPresenterApi {
23 | func settings() -> UserSettings {
24 | _viewModel = UserSettings()
25 | return _viewModel
26 | }
27 |
28 | func changeRandomName() {
29 | _viewModel.randomName = "Loading..."
30 | DispatchQueue.global(qos: .background).async { [weak self] in
31 | let url = URL(string: "https://www.random.org/strings/?num=1&len=10&upperalpha=on&loweralpha=on&unique=off&format=plain")!
32 |
33 | let name = try! String(contentsOf: url)
34 | DispatchQueue.main.async { [weak self] in
35 | self?._viewModel.randomName = name
36 | }
37 | }
38 | }
39 |
40 | func changeScore () {
41 | _viewModel.score += 1
42 | }
43 |
44 | func showAnotherModule() {
45 | print("Show another module")
46 | router.goToPerfect()
47 | }
48 | }
49 |
50 | // MARK: - Cool Viper Components
51 | private extension CoolPresenter {
52 | var interactor: CoolInteractorApi {
53 | return _interactor as! CoolInteractorApi
54 | }
55 | var router: CoolRouterApi {
56 | return _router as! CoolRouterApi
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Example/modules/Cool/CoolRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CoolRouter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - CoolRouter class
13 | final class CoolRouter: Router {
14 | }
15 |
16 | // MARK: - CoolRouter API
17 | extension CoolRouter: CoolRouterApi {
18 | func goToPerfect() {
19 | let module = AppModules.perfect.build { _ in
20 | PerfectView()
21 | }
22 |
23 | let router = module.router as! PerfectRouter
24 | router.show(from: viewController)
25 | }
26 | }
27 |
28 | // MARK: - Cool Viper Components
29 | private extension CoolRouter {
30 | var presenter: CoolPresenterApi {
31 | return _presenter as! CoolPresenterApi
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Example/modules/Cool/CoolView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CoolView.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import Viperit
11 |
12 | //MARK: CoolView SwiftUI
13 | struct CoolView : View {
14 | weak var presenter: CoolPresenterApi?
15 | @EnvironmentObject var settings: UserSettings
16 |
17 | var body: some View {
18 | VStack(spacing: 10) {
19 | Text(/*@START_MENU_TOKEN@*/"Hello World!"/*@END_MENU_TOKEN@*/)
20 | Text("SwiftUI is really amazing")
21 | Text("Random name: \(settings.randomName)")
22 | Text("Score: \(settings.score)")
23 |
24 | Button(action: { self.presenter?.changeScore() }) {
25 | Text("Add score +1")
26 | }
27 | Button(action: { self.presenter?.changeRandomName() }) {
28 | Text("Fetch new random name")
29 | }
30 | Button(action: { self.presenter?.showAnotherModule() }) {
31 | Text("Show another SwiftUI module")
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Example/modules/Cool/viewModels/UserSettings.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserSettings.swift
3 | // Example
4 | //
5 | // Created by Ferran on 27/08/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class UserSettings: ObservableObject {
12 | @Published var randomName = "random"
13 | @Published var score = 0
14 | }
15 |
--------------------------------------------------------------------------------
/Example/modules/Home/Home.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
30 |
37 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/Example/modules/Home/HomeDisplayData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HomeDisplayData.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 19/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Viperit
10 |
11 | class HomeDisplayData: DisplayData {
12 |
13 | let loadingText = "..."
14 | }
15 |
--------------------------------------------------------------------------------
/Example/modules/Home/HomeInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HomeInteractor.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 11/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | final class HomeInteractor: Interactor {
13 | func someInteractorOperation() {
14 | DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) { [weak self] in
15 | self?.presenter.reactToSomeInteractorOperation()
16 | }
17 | }
18 | }
19 |
20 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
21 | private extension HomeInteractor {
22 | var presenter: HomePresenter {
23 | return _presenter as! HomePresenter
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Example/modules/Home/HomePresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HomePresenter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 11/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | final class HomePresenter: Presenter {
13 |
14 | override func viewIsAboutToAppear() {
15 | loadContent()
16 | }
17 |
18 | func reactToSomeInteractorOperation() {
19 | print("Home Presenter Reacted to Some Interactor Operation")
20 | self.view.showInfo(message: "CONTENT_LOADED")
21 | }
22 |
23 | func showSecondModule() {
24 | router.showSecondModule()
25 | }
26 | }
27 |
28 | extension HomePresenter {
29 | func loadContent() {
30 | view.showLoading()
31 | interactor.someInteractorOperation()
32 | }
33 | }
34 |
35 |
36 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
37 | private extension HomePresenter {
38 | var view: HomeViewInterface {
39 | return _view as! HomeViewInterface
40 | }
41 | var interactor: HomeInteractor {
42 | return _interactor as! HomeInteractor
43 | }
44 | var router: HomeRouter {
45 | return _router as! HomeRouter
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Example/modules/Home/HomeRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HomeRouter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 11/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Viperit
11 |
12 | final class HomeRouter: Router {
13 | func showSecondModule() {
14 | let module = AppModules.second.build()
15 | let router = module.router as! SecondRouter
16 | router.present(from: viewController, embedInNavController: true)
17 | }
18 | }
19 |
20 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
21 | private extension HomeRouter {
22 | var presenter: HomePresenter {
23 | return _presenter as! HomePresenter
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Example/modules/Home/HomeView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HomeView.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 11/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Viperit
11 |
12 |
13 | //MARK: - Public Interface Protocol
14 | protocol HomeViewInterface {
15 | func showLoading()
16 | func showInfo(message: String)
17 | }
18 |
19 | //MARK: Home View
20 | final class HomeView: UserInterface {
21 | @IBOutlet weak var messageLabel: UILabel!
22 |
23 | @IBAction func goToSecondModuleButtonPressed() {
24 | presenter.showSecondModule()
25 | }
26 | }
27 |
28 | //MARK: - Public interface
29 | extension HomeView: HomeViewInterface {
30 | func showLoading() {
31 | messageLabel.text = displayData.loadingText
32 | }
33 | func showInfo(message: String) {
34 | messageLabel.text = message
35 | }
36 | }
37 |
38 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
39 | private extension HomeView {
40 | var presenter: HomePresenter { return _presenter as! HomePresenter }
41 | var displayData: HomeDisplayData { return _displayData as! HomeDisplayData }
42 | }
43 |
44 |
45 |
--------------------------------------------------------------------------------
/Example/modules/Perfect/PerfectInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PerfectInteractor.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - PerfectInteractor Class
13 | final class PerfectInteractor: Interactor {
14 | }
15 |
16 | // MARK: - PerfectInteractor API
17 | extension PerfectInteractor: PerfectInteractorApi {
18 | }
19 |
20 | // MARK: - Interactor Viper Components Api
21 | private extension PerfectInteractor {
22 | var presenter: PerfectPresenterApi {
23 | return _presenter as! PerfectPresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Example/modules/Perfect/PerfectModuleApi.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PerfectModuleApi.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Viperit
10 |
11 | //MARK: - PerfectRouter API
12 | protocol PerfectRouterApi: RouterProtocol {
13 | }
14 |
15 | //MARK: - PerfectPresenter API
16 | protocol PerfectPresenterApi: PresenterProtocol {
17 | }
18 |
19 | //MARK: - PerfectInteractor API
20 | protocol PerfectInteractorApi: InteractorProtocol {
21 | }
22 |
--------------------------------------------------------------------------------
/Example/modules/Perfect/PerfectPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PerfectPresenter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - PerfectPresenter Class
13 | final class PerfectPresenter: Presenter {
14 | }
15 |
16 | // MARK: - PerfectPresenter API
17 | extension PerfectPresenter: PerfectPresenterApi {
18 | }
19 |
20 | // MARK: - Perfect Viper Components
21 | private extension PerfectPresenter {
22 | var interactor: PerfectInteractorApi {
23 | return _interactor as! PerfectInteractorApi
24 | }
25 | var router: PerfectRouterApi {
26 | return _router as! PerfectRouterApi
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Example/modules/Perfect/PerfectRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PerfectRouter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - PerfectRouter class
13 | final class PerfectRouter: Router {
14 | }
15 |
16 | // MARK: - PerfectRouter API
17 | extension PerfectRouter: PerfectRouterApi {
18 | }
19 |
20 | // MARK: - Perfect Viper Components
21 | private extension PerfectRouter {
22 | var presenter: PerfectPresenterApi {
23 | return _presenter as! PerfectPresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Example/modules/Perfect/PerfectView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PerfectView.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import Viperit
11 |
12 | //MARK: PerfectView SwiftUI
13 | struct PerfectView : View {
14 |
15 | var body: some View {
16 | Text("Perfect SwiftUI View")
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Example/modules/Second/Second.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
30 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
70 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/Example/modules/Second/SecondDisplayData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SecondDisplayData.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 10/11/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | final class SecondDisplayData: DisplayData {
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/Example/modules/Second/SecondInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SecondInteractor.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 10/11/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | final class SecondInteractor: Interactor {
13 | }
14 |
15 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
16 | private extension SecondInteractor {
17 | var presenter: SecondPresenter {
18 | return _presenter as! SecondPresenter
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Example/modules/Second/SecondPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SecondPresenter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 10/11/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | final class SecondPresenter: Presenter {
13 |
14 | func close() {
15 | router.close()
16 | }
17 | }
18 |
19 |
20 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
21 | private extension SecondPresenter {
22 | var view: SecondViewInterface {
23 | return _view as! SecondViewInterface
24 | }
25 | var interactor: SecondInteractor {
26 | return _interactor as! SecondInteractor
27 | }
28 | var router: SecondRouter {
29 | return _router as! SecondRouter
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Example/modules/Second/SecondRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SecondRouter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 10/11/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Viperit
11 |
12 | final class SecondRouter: Router {
13 | func close() {
14 | dismiss(animated: true, completion: nil)
15 | }
16 | }
17 |
18 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
19 | private extension SecondRouter {
20 | var presenter: SecondPresenter {
21 | return _presenter as! SecondPresenter
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Example/modules/Second/SecondView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SecondView.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 10/11/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Viperit
11 |
12 | //MARK: - Public Interface Protocol
13 | protocol SecondViewInterface {
14 | }
15 |
16 | //MARK: Second View
17 | final class SecondView: UserInterface {
18 |
19 | override func viewDidLoad() {
20 | super.viewDidLoad()
21 | navigationItem.title = "Second Module"
22 | }
23 |
24 | @IBAction func closeButtonPressed() {
25 | presenter.close()
26 | }
27 | }
28 |
29 | //MARK: - Public interface
30 | extension SecondView: SecondViewInterface {
31 | }
32 |
33 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
34 | private extension SecondView {
35 | var presenter: SecondPresenter {
36 | return _presenter as! SecondPresenter
37 | }
38 | var displayData: SecondDisplayData {
39 | return _displayData as! SecondDisplayData
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Example/modules/Second/SecondViewPad.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SecondViewPad.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 10/11/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Viperit
11 |
12 | //MARK: Second View for Tablet
13 | final class SecondViewPad: UserInterface {
14 | }
15 |
16 | //MARK: - Public interface implementation
17 | extension SecondViewPad: SecondViewInterface {
18 | @IBAction func close() {
19 | presenter.close()
20 | }
21 | }
22 |
23 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
24 | private extension SecondViewPad {
25 | var presenter: SecondPresenter {
26 | return _presenter as! SecondPresenter
27 | }
28 | var displayData: SecondDisplayData {
29 | return _displayData as! SecondDisplayData
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Example/modules/Simple/SimpleDisplayData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimpleDisplayData.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | //Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - SimpleDisplayData class
13 | final class SimpleDisplayData: DisplayData {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/Example/modules/Simple/SimpleInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimpleInteractor.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | //Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - SimpleInteractor Class
13 | final class SimpleInteractor: Interactor {
14 | }
15 |
16 | // MARK: - SimpleInteractor API
17 | extension SimpleInteractor: SimpleInteractorApi {
18 | }
19 |
20 | // MARK: - Interactor Viper Components Api
21 | private extension SimpleInteractor {
22 | var presenter: SimplePresenterApi {
23 | return _presenter as! SimplePresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Example/modules/Simple/SimpleModuleApi.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimpleModuleApi.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | //Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Viperit
10 |
11 | //MARK: - SimpleRouter API
12 | protocol SimpleRouterApi: RouterProtocol {
13 | }
14 |
15 | //MARK: - SimpleView API
16 | protocol SimpleViewApi: UserInterfaceProtocol {
17 | }
18 |
19 | //MARK: - SimplePresenter API
20 | protocol SimplePresenterApi: PresenterProtocol {
21 | }
22 |
23 | //MARK: - SimpleInteractor API
24 | protocol SimpleInteractorApi: InteractorProtocol {
25 | }
26 |
--------------------------------------------------------------------------------
/Example/modules/Simple/SimplePresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimplePresenter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | //Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - SimplePresenter Class
13 | final class SimplePresenter: Presenter {
14 | }
15 |
16 | // MARK: - SimplePresenter API
17 | extension SimplePresenter: SimplePresenterApi {
18 | }
19 |
20 | // MARK: - Simple Viper Components
21 | private extension SimplePresenter {
22 | var view: SimpleViewApi {
23 | return _view as! SimpleViewApi
24 | }
25 | var interactor: SimpleInteractorApi {
26 | return _interactor as! SimpleInteractorApi
27 | }
28 | var router: SimpleRouterApi {
29 | return _router as! SimpleRouterApi
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Example/modules/Simple/SimpleRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimpleRouter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | //Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - SimpleRouter class
13 | final class SimpleRouter: Router {
14 | }
15 |
16 | // MARK: - SimpleRouter API
17 | extension SimpleRouter: SimpleRouterApi {
18 | }
19 |
20 | // MARK: - Simple Viper Components
21 | private extension SimpleRouter {
22 | var presenter: SimplePresenterApi {
23 | return _presenter as! SimplePresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Example/modules/Simple/SimpleView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimpleView.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | //Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Viperit
11 |
12 | //MARK: SimpleView Class
13 | final class SimpleView: UserInterface {
14 |
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 | navigationItem.title = "Simple modal view"
18 | view.backgroundColor = .yellow
19 |
20 | let label = UILabel()
21 | label.text = "This simple module is generated by code, no storyboard, nibs or swift ui"
22 | label.textColor = .black
23 | label.font = .systemFont(ofSize: 22, weight: .bold)
24 | label.numberOfLines = 0
25 | label.frame = view.frame.insetBy(dx: 20, dy: 20)
26 | view.addSubview(label)
27 | }
28 | }
29 |
30 | //MARK: - SimpleView API
31 | extension SimpleView: SimpleViewApi {
32 | }
33 |
34 | // MARK: - SimpleView Viper Components API
35 | private extension SimpleView {
36 | var presenter: SimplePresenterApi {
37 | return _presenter as! SimplePresenterApi
38 | }
39 | var displayData: SimpleDisplayData {
40 | return _displayData as! SimpleDisplayData
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Example/modules/TableOfContents/TableOfContentsDisplayData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableOfContentsDisplayData.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 01/04/2019.
6 | //Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - TableOfContentsDisplayData class
13 | final class TableOfContentsDisplayData: DisplayData {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/Example/modules/TableOfContents/TableOfContentsInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableOfContentsInteractor.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 01/04/2019.
6 | //Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - TableOfContentsInteractor Class
13 | final class TableOfContentsInteractor: Interactor {
14 | }
15 |
16 | // MARK: - TableOfContentsInteractor API
17 | extension TableOfContentsInteractor: TableOfContentsInteractorApi {
18 | }
19 |
20 | // MARK: - Interactor Viper Components Api
21 | private extension TableOfContentsInteractor {
22 | var presenter: TableOfContentsPresenterApi {
23 | return _presenter as! TableOfContentsPresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Example/modules/TableOfContents/TableOfContentsModuleApi.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableOfContentsModuleApi.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 01/04/2019.
6 | //Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Viperit
10 |
11 | //MARK: - TableOfContentsRouter API
12 | protocol TableOfContentsRouterApi: RouterProtocol {
13 | func goHome()
14 | func goToCool()
15 | func goToSimple()
16 | }
17 |
18 | //MARK: - TableOfContentsView API
19 | protocol TableOfContentsViewApi: UserInterfaceProtocol {
20 | }
21 |
22 | //MARK: - TableOfContentsPresenter API
23 | protocol TableOfContentsPresenterApi: PresenterProtocol {
24 | func showHome()
25 | func showCool()
26 | func showSimple()
27 | }
28 |
29 | //MARK: - TableOfContentsInteractor API
30 | protocol TableOfContentsInteractorApi: InteractorProtocol {
31 | }
32 |
--------------------------------------------------------------------------------
/Example/modules/TableOfContents/TableOfContentsPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableOfContentsPresenter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 01/04/2019.
6 | //Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - TableOfContentsPresenter Class
13 | final class TableOfContentsPresenter: Presenter {
14 | }
15 |
16 | // MARK: - TableOfContentsPresenter API
17 | extension TableOfContentsPresenter: TableOfContentsPresenterApi {
18 | func showHome() {
19 | router.goHome()
20 | }
21 |
22 | func showCool() {
23 | router.goToCool()
24 | }
25 |
26 | func showSimple() {
27 | router.goToSimple()
28 | }
29 | }
30 |
31 | // MARK: - TableOfContents Viper Components
32 | private extension TableOfContentsPresenter {
33 | var view: TableOfContentsViewApi {
34 | return _view as! TableOfContentsViewApi
35 | }
36 | var interactor: TableOfContentsInteractorApi {
37 | return _interactor as! TableOfContentsInteractorApi
38 | }
39 | var router: TableOfContentsRouterApi {
40 | return _router as! TableOfContentsRouterApi
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Example/modules/TableOfContents/TableOfContentsRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableOfContentsRouter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 01/04/2019.
6 | //Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - TableOfContentsRouter class
13 | final class TableOfContentsRouter: Router {
14 | }
15 |
16 | // MARK: - TableOfContentsRouter API
17 | extension TableOfContentsRouter: TableOfContentsRouterApi {
18 | func goHome() {
19 | let home = AppModules.home.build()
20 | home.router.show(from: viewController, embedInNavController: false, setupData: nil)
21 | }
22 |
23 | func goToCool() {
24 | let module = AppModules.cool.build { presenter -> (CoolView, UserSettings) in
25 | let p = presenter as! CoolPresenterApi
26 | let settings = p.settings()
27 | return (CoolView(presenter: p), settings)
28 | }
29 |
30 | let router = module.router as! CoolRouter
31 | router.show(from: viewController)
32 | }
33 |
34 | func goToSimple() {
35 | let simple = AppModules.simple.build()
36 | simple.router.show(from: viewController, embedInNavController: true, setupData: nil)
37 | }
38 | }
39 |
40 | // MARK: - TableOfContents Viper Components
41 | private extension TableOfContentsRouter {
42 | var presenter: TableOfContentsPresenterApi {
43 | return _presenter as! TableOfContentsPresenterApi
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Example/modules/TableOfContents/TableOfContentsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableOfContentsView.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 01/04/2019.
6 | //Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Viperit
11 |
12 | //MARK: TableOfContentsView Class
13 | final class TableOfContentsView: TableUserInterface {
14 |
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 | navigationItem.title = "Viperit App"
18 | }
19 |
20 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
21 | switch indexPath.row {
22 | case 0: presenter.showHome()
23 | case 1: presenter.showCool()
24 | case 2: presenter.showSimple()
25 | default: break
26 | }
27 | }
28 |
29 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
30 | return 3
31 | }
32 |
33 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
34 | let cell = UITableViewCell()
35 | switch indexPath.row {
36 | case 0:
37 | cell.textLabel?.text = "Storyboard module"
38 | case 1:
39 | cell.textLabel?.text = "Swift UI module"
40 | case 2:
41 | cell.textLabel?.text = "Code-generated module"
42 | default: break
43 | }
44 | return cell
45 | }
46 |
47 | }
48 |
49 | //MARK: - TableOfContentsView API
50 | extension TableOfContentsView: TableOfContentsViewApi {
51 | }
52 |
53 | // MARK: - TableOfContentsView Viper Components API
54 | private extension TableOfContentsView {
55 | var presenter: TableOfContentsPresenterApi {
56 | return _presenter as! TableOfContentsPresenterApi
57 | }
58 | var displayData: TableOfContentsDisplayData {
59 | return _displayData as! TableOfContentsDisplayData
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/ExampleTests/HomeTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViperitTests.swift
3 | // ViperitTests
4 | //
5 | // Created by Ferran on 17/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | @testable import Example
10 | import Viperit
11 | import XCTest
12 |
13 | class HomeMockView: UserInterface, HomeViewInterface {
14 |
15 | //TEST PROPERTIES
16 | var expectation: XCTestExpectation!
17 | var expectedMessage: String!
18 |
19 | func showInfo(message: String) {
20 | print("EXPECTED MESSAGE : \(expectedMessage!)")
21 | XCTAssert(message == expectedMessage)
22 | expectation.fulfill()
23 | }
24 |
25 | func showLoading() {
26 | //
27 | }
28 | }
29 |
30 | class HomeTests: XCTestCase {
31 |
32 | var view: HomeMockView!
33 | var presenter: HomePresenter!
34 |
35 | override func setUp() {
36 | super.setUp()
37 | var mod = AppModules.home.build()
38 | view = HomeMockView()
39 | view.expectation = expectation(description: "Test expectation description")
40 | presenter = mod.presenter as? HomePresenter
41 | mod.injectMock(view: view)
42 | }
43 |
44 | func expect(timeout: TimeInterval = 5, errorMessage: String = "TIMEOUT") {
45 | waitForExpectations(timeout: 5) { error in
46 | guard error != nil else { return }
47 | XCTFail(errorMessage)
48 | }
49 | }
50 |
51 | func testShowInfo1() {
52 | print("---RUNNING TEST1---")
53 | view.expectedMessage = "CONTENT_LOADED"
54 | presenter.loadContent()
55 | expect()
56 | }
57 |
58 | func testShowInfo2() {
59 | print("---RUNNING TEST2---")
60 | view.expectedMessage = "CONTENT_LOADED"
61 | presenter.loadContent()
62 | expect()
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/ExampleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIRequiredDeviceCapabilities
26 |
27 | armv7
28 |
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Ferran Abelló
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.0
2 | import PackageDescription
3 |
4 | let package = Package(
5 | name: "Viperit",
6 | platforms: [.iOS("11.0")],
7 | products: [
8 | .library(name: "Viperit", targets: ["Viperit"])
9 | ],
10 | targets: [
11 | .target(name: "Viperit", path: "Viperit"),
12 | .testTarget(name: "ViperitTests", dependencies: ["Viperit"], path: "ViperitTests")
13 | ]
14 | )
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://swift.org)
4 | [](https://travis-ci.org/ferranabello/Viperit)
5 | [](https://developer.apple.com/iphone/index.action)
6 | [](http://mit-license.org)
7 | [](https://codecov.io/gh/ferranabello/Viperit)
8 | [](https://codebeat.co/projects/github-com-ferranabello-viperit-master)
9 | [](http://github.com/ferranabello/Viperit)
10 | [](https://github.com/Carthage/Carthage)
11 | [](https://github.com/JamitLabs/Accio)
12 | [](https://github.com/apple/swift-package-manager)
13 | [](https://developer.apple.com/xcode/swiftui/)
14 |
15 |
16 | Write an iOS app following VIPER architecture. But in an **easy way**.
17 |
18 |
19 | ## Viper the easy way
20 | We all know Viper is cool. But we also know that it's hard to setup. This library intends to simplify all that boilerplate process. If you don't know yet what Viper is, check this out: [Architecting iOS Apps with VIPER (objc.io)](https://www.objc.io/issues/13-architecture/viper/)
21 |
22 | ## Installation
23 |
24 | ### Requirements
25 |
26 | - iOS 12.0+
27 | - Swift 5.1+
28 | - Xcode 12.0+
29 |
30 | ### Swift Package Manager (SPM)
31 | You can easily install this framework using SPM on Xcode. Go to `File | Swift Packages | Add Package Dependency...` in Xcode and search for "http://github.com/ferranabello/Viperit"
32 |
33 | ### CocoaPods
34 |
35 | [CocoaPods](https://cocoapods.org/) is a dependency manager for Cocoa projects.
36 |
37 | Specify Viperit into your project's Podfile:
38 |
39 | ```ruby
40 | source 'https://github.com/CocoaPods/Specs.git'
41 | platform :ios, '12.0'
42 | use_frameworks!
43 |
44 | pod 'Viperit'
45 | ```
46 |
47 | ### Carthage
48 |
49 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
50 |
51 | To integrate Viperit into your Xcode project using Carthage, specify it in your `Cartfile`:
52 |
53 | ```ogdl
54 | github "ferranabello/Viperit"
55 | ```
56 |
57 | Run `carthage update` to build the framework and drag the built `Viperit.framework` into your Xcode project.
58 |
59 | ## Features
60 |
61 | ### Create modules easily using Xcode templates
62 | Viperit Xcode templates can be downloaded from the [latest release](https://github.com/ferranabello/Viperit/releases) page. Download the **Templates.zip** file.
63 |
64 | To install them, unzip the file, open your terminal and run:
65 |
66 | ```bash
67 | cd PATH/TO/UNZIPPED/FOLDER
68 | mkdir -p ~/Library/Developer/Xcode/Templates/
69 | cp -R Viperit ~/Library/Developer/Xcode/Templates/
70 | ```
71 |
72 | 
73 |
74 | You can check "Also create a Storyboard file for module" if you want the storyboard file to be automatically created for you.
75 | Choose between "Universal" to use just one view for phones and tablets, and "Dedicated Tablet View" if you want to have a separated view for tablet devices.
76 |
77 | ### Use storyboard, xib, programmatic views or SwiftUI
78 | Any Viperit module will assume its view is loaded from a Storyboard by default. But you can use **storyboards**, **nibs**, **code** or even **SwiftUI** views! All you need to do is to override the variable *viewType* in your modules enum:
79 |
80 | ```swift
81 | enum MySuperCoolModules: String, ViperitModule {
82 | case theStoryboardThing
83 | case oldSchool
84 | case xibModule
85 | case whatTheSwift
86 |
87 | var viewType: ViperitViewType {
88 | switch self {
89 | case .theStoryboardThing: return .storyboard
90 | case .oldSchool: return .code
91 | case .xibModule: return .nib
92 | case .whatTheSwift: return .swiftUI
93 | }
94 | }
95 | }
96 | ```
97 |
98 | ### Built-in router functions
99 | This framework is very flexible, it's meant to be used in any way you want, but it has some useful built-in functionalities in the router that you could use:
100 | ```swift
101 | //Show your module as the root view controller of the given window
102 | func show(inWindow window: UIWindow?, embedInNavController: Bool, setupData: Any?, makeKeyAndVisible: Bool)
103 |
104 | //Show your module from any given view controller
105 | func show(from: UIViewController, embedInNavController: Bool, setupData: Any?)
106 |
107 | //Show your module inside another view
108 | func show(from containerView: UIViewController, insideView targetView: UIView, setupData: Any?)
109 |
110 | //Present your module modally
111 | func present(from: UIViewController, embedInNavController: Bool, presentationStyle: UIModalPresentationStyle, transitionStyle: UIModalTransitionStyle, setupData: Any?, completion: (() -> Void)?)
112 | ```
113 |
114 | ### Easy to test
115 | You can test your module injecting mock layers like so:
116 | ```swift
117 | var module = AppModules.home.build()
118 | view = HomeMockView()
119 | view.expectation = expectation(description: "Test expectation")
120 | module.injectMock(view: view)
121 | ...
122 | ```
123 |
124 | ## Usage
125 | Now, let's create our first Viperit module called "myFirstModule"!
126 |
127 | ### 0. Create your module files
128 | Let's use the provided Xcode template to easily create all the needed classes for the module. Just click **New file** in the document panel and choose between **Protocol-oriented module**, **Object-oriented module** or **SwiftUI module** under the "Viperit" section.
129 |
130 | ### 1. Create a modules enum
131 | You need at least one (you can use as many as you like, maybe to group modules by functionality) enum that implements the ViperitModule protocol to enumerate your application modules.
132 | ```swift
133 | import Viperit
134 |
135 | //MARK: - Application modules
136 | enum AppModules: String, ViperitModule {
137 | case myFirstModule
138 | }
139 | ```
140 |
141 | ### 2. Build the module and perform navigation
142 | Imagine this is a new app and we want to load our "myFirstModule" module as the app's startup module
143 | ```swift
144 | import Viperit
145 |
146 | @UIApplicationMain
147 | class AppDelegate: UIResponder, UIApplicationDelegate {
148 | var window: UIWindow?
149 |
150 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
151 | window = UIWindow(frame: UIScreen.main.bounds)
152 | let module = AppModules.myFirstModule.build()
153 | let router = module.router as! MyFirstModuleRouter
154 | router.show(inWindow: window)
155 | return true
156 | }
157 | }
158 | ```
159 | This is just an example, you could of course use your own router functions instead of the provided show(inWindow:):
160 | ```swift
161 | window = UIWindow(frame: UIScreen.main.bounds)
162 | let module = AppModules.myFirstModule.build()
163 | let router = module.router as! MyFirstModuleRouter
164 | router.mySuperCoolShowFunction(inWindow: window)
165 | ```
166 |
167 | ### 2.1. Are you using SwiftUI?
168 | Let's say you created a module based on SwiftUI called 'Cool'.
169 | All you need to do is to use the new Viperit SwiftUI module builder:
170 |
171 | ```swift
172 | import SwiftUI
173 | import Viperit
174 |
175 | //A sample function that could be implemented in some Router to show your Cool SwiftUI module
176 | //You can even inject an @EnvironmentObject view model to your SwiftUI view.
177 | func showTheCoolModule() {
178 | let module = AppModules.cool.build { presenter -> (CoolView, UserSettings) in
179 | let p = presenter as! CoolPresenterApi
180 | let settings = p.settings()
181 | return (CoolView(presenter: p), settings)
182 | }
183 | let router = module.router as! CoolRouter
184 | router.show(from: viewController)
185 | }
186 | ```
187 |
188 | Check the example app to have a better understanding of how it works, and how to manage data flow on SwiftUI with the presenter.
189 |
190 | ### 3. Follow the Viper flow
191 | Everything is ready for you to make great things the Viper way!
192 | Clone the repo and run the 'Example' target to see it in action! Or just try it with Cocoapods:
193 | ```ruby
194 | pod try Viperit
195 | ```
196 |
197 | ## Author
198 |
199 | [Ferran Abelló](https://www.github.com/ferranabello "Ferran Abelló Github")
200 |
201 | ## License
202 |
203 | Viperit is released under [MIT license](https://raw.githubusercontent.com/ferranabello/viperit/master/LICENSE) and copyrighted by Ferran Abelló.
204 |
--------------------------------------------------------------------------------
/Viperit.podspec:
--------------------------------------------------------------------------------
1 | # Viperit podspec
2 | #
3 | Pod::Spec.new do |s|
4 | s.name = 'Viperit'
5 | s.version = '1.5.0'
6 | s.summary = 'Viper Framework for iOS written in Swift'
7 |
8 | s.description = <<-DESC
9 | Viper Framework for iOS to implement VIPER architecture in an easy way
10 | DESC
11 |
12 | s.homepage = 'https://github.com/ferranabello/Viperit'
13 | s.license = { :type => 'MIT', :file => 'LICENSE' }
14 | s.author = { 'Ferran Abelló' => 'ferran.abello@gmail.com' }
15 | s.source = { :git => 'https://github.com/ferranabello/Viperit.git', :tag => s.version.to_s }
16 | s.weak_framework = 'SwiftUI'
17 | s.social_media_url = 'https://twitter.com/acferran'
18 | s.swift_version = '5'
19 |
20 | s.ios.deployment_target = '12.0'
21 | s.source_files = 'Viperit/**/*.swift'
22 | end
23 |
--------------------------------------------------------------------------------
/Viperit.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 53023EDC225257E8004ACC5C /* UserInterface+Table.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53023EDB225257E8004ACC5C /* UserInterface+Table.swift */; };
11 | 53023EDE22525814004ACC5C /* UserInterface+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53023EDD22525814004ACC5C /* UserInterface+Collection.swift */; };
12 | 53023EE02252582C004ACC5C /* UserInterface+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53023EDF2252582C004ACC5C /* UserInterface+Split.swift */; };
13 | 53077FE6232F8CAB0078D855 /* SimpleDisplayData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53077FE0232F8CAB0078D855 /* SimpleDisplayData.swift */; };
14 | 53077FE7232F8CAB0078D855 /* SimplePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53077FE1232F8CAB0078D855 /* SimplePresenter.swift */; };
15 | 53077FE8232F8CAB0078D855 /* SimpleRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53077FE2232F8CAB0078D855 /* SimpleRouter.swift */; };
16 | 53077FE9232F8CAB0078D855 /* SimpleModuleApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53077FE3232F8CAB0078D855 /* SimpleModuleApi.swift */; };
17 | 53077FEA232F8CAB0078D855 /* SimpleInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53077FE4232F8CAB0078D855 /* SimpleInteractor.swift */; };
18 | 53077FEB232F8CAB0078D855 /* SimpleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53077FE5232F8CAB0078D855 /* SimpleView.swift */; };
19 | 5327EAD922526175006D5FCC /* TableOfContentsDisplayData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5327EAD322526175006D5FCC /* TableOfContentsDisplayData.swift */; };
20 | 5327EADA22526175006D5FCC /* TableOfContentsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5327EAD422526175006D5FCC /* TableOfContentsPresenter.swift */; };
21 | 5327EADB22526175006D5FCC /* TableOfContentsRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5327EAD522526175006D5FCC /* TableOfContentsRouter.swift */; };
22 | 5327EADC22526175006D5FCC /* TableOfContentsModuleApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5327EAD622526175006D5FCC /* TableOfContentsModuleApi.swift */; };
23 | 5327EADD22526175006D5FCC /* TableOfContentsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5327EAD722526175006D5FCC /* TableOfContentsInteractor.swift */; };
24 | 5327EADE22526175006D5FCC /* TableOfContentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5327EAD822526175006D5FCC /* TableOfContentsView.swift */; };
25 | 53388C291DD9FFDF00EA4CEC /* Viperit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5367C0CF1DD7C68D005B6676 /* Viperit.framework */; };
26 | 53388C2A1DD9FFDF00EA4CEC /* Viperit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5367C0CF1DD7C68D005B6676 /* Viperit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
27 | 5355BECB232FC65600BC7CF9 /* CoolPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355BEC6232FC65600BC7CF9 /* CoolPresenter.swift */; };
28 | 5355BECC232FC65600BC7CF9 /* CoolRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355BEC7232FC65600BC7CF9 /* CoolRouter.swift */; };
29 | 5355BECD232FC65600BC7CF9 /* CoolModuleApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355BEC8232FC65600BC7CF9 /* CoolModuleApi.swift */; };
30 | 5355BECE232FC65600BC7CF9 /* CoolInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355BEC9232FC65600BC7CF9 /* CoolInteractor.swift */; };
31 | 5355BECF232FC65600BC7CF9 /* CoolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355BECA232FC65600BC7CF9 /* CoolView.swift */; };
32 | 5355BED6232FC76900BC7CF9 /* PerfectPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355BED1232FC76900BC7CF9 /* PerfectPresenter.swift */; };
33 | 5355BED7232FC76900BC7CF9 /* PerfectRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355BED2232FC76900BC7CF9 /* PerfectRouter.swift */; };
34 | 5355BED8232FC76900BC7CF9 /* PerfectModuleApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355BED3232FC76900BC7CF9 /* PerfectModuleApi.swift */; };
35 | 5355BED9232FC76900BC7CF9 /* PerfectInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355BED4232FC76900BC7CF9 /* PerfectInteractor.swift */; };
36 | 5355BEDA232FC76900BC7CF9 /* PerfectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355BED5232FC76900BC7CF9 /* PerfectView.swift */; };
37 | 5360ADF31E81280100C1AACB /* PresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5360ADF21E81280100C1AACB /* PresenterTests.swift */; };
38 | 5367C0DC1DD7C69E005B6676 /* ViperitError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53532C641DD50DDF00088AAC /* ViperitError.swift */; };
39 | 5367C0DD1DD7C69E005B6676 /* ViperComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53532C631DD50DDF00088AAC /* ViperComponent.swift */; };
40 | 5367C0DE1DD7C69E005B6676 /* Module.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53532C5F1DD50DDF00088AAC /* Module.swift */; };
41 | 5367C0DF1DD7C69E005B6676 /* UserInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53532C621DD50DDF00088AAC /* UserInterface.swift */; };
42 | 5367C0E01DD7C69E005B6676 /* DisplayData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53532C5D1DD50DDF00088AAC /* DisplayData.swift */; };
43 | 5367C0E11DD7C69E005B6676 /* Interactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53532C5E1DD50DDF00088AAC /* Interactor.swift */; };
44 | 5367C0E21DD7C69E005B6676 /* Presenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53532C601DD50DDF00088AAC /* Presenter.swift */; };
45 | 5367C0E31DD7C69E005B6676 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53532C611DD50DDF00088AAC /* Router.swift */; };
46 | 5368EDC71E7FF91F0028D8CA /* RouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5368EDC61E7FF91F0028D8CA /* RouterTests.swift */; };
47 | 5368EDC91E7FF9A40028D8CA /* TestModules.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5368EDC81E7FF9A40028D8CA /* TestModules.swift */; };
48 | 5368EDD11E801EDB0028D8CA /* Sample.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5368EDCA1E801EDB0028D8CA /* Sample.storyboard */; };
49 | 5368EDD21E801EDB0028D8CA /* SampleDisplayData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5368EDCB1E801EDB0028D8CA /* SampleDisplayData.swift */; };
50 | 5368EDD31E801EDB0028D8CA /* SampleInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5368EDCC1E801EDB0028D8CA /* SampleInteractor.swift */; };
51 | 5368EDD41E801EDB0028D8CA /* SamplePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5368EDCD1E801EDB0028D8CA /* SamplePresenter.swift */; };
52 | 5368EDD51E801EDB0028D8CA /* SampleRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5368EDCE1E801EDB0028D8CA /* SampleRouter.swift */; };
53 | 5368EDD61E801EDB0028D8CA /* SampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5368EDCF1E801EDB0028D8CA /* SampleView.swift */; };
54 | 5368EDD71E801EDB0028D8CA /* SampleViewPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5368EDD01E801EDB0028D8CA /* SampleViewPad.swift */; };
55 | 5372505E1EF13500007F3A10 /* ViperitModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5372505D1EF13500007F3A10 /* ViperitModule.swift */; };
56 | 537250691EF13835007F3A10 /* CodeModuleDisplayData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537250631EF13835007F3A10 /* CodeModuleDisplayData.swift */; };
57 | 5372506A1EF13835007F3A10 /* CodeModuleInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537250641EF13835007F3A10 /* CodeModuleInteractor.swift */; };
58 | 5372506B1EF13835007F3A10 /* CodeModuleModuleApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537250651EF13835007F3A10 /* CodeModuleModuleApi.swift */; };
59 | 5372506C1EF13835007F3A10 /* CodeModulePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537250661EF13835007F3A10 /* CodeModulePresenter.swift */; };
60 | 5372506D1EF13835007F3A10 /* CodeModuleRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537250671EF13835007F3A10 /* CodeModuleRouter.swift */; };
61 | 5372506E1EF13835007F3A10 /* CodeModuleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537250681EF13835007F3A10 /* CodeModuleView.swift */; };
62 | 537250751EF13988007F3A10 /* XibModuleDisplayData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5372506F1EF13988007F3A10 /* XibModuleDisplayData.swift */; };
63 | 537250761EF13988007F3A10 /* XibModuleInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537250701EF13988007F3A10 /* XibModuleInteractor.swift */; };
64 | 537250771EF13988007F3A10 /* XibModuleModuleApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537250711EF13988007F3A10 /* XibModuleModuleApi.swift */; };
65 | 537250781EF13988007F3A10 /* XibModulePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537250721EF13988007F3A10 /* XibModulePresenter.swift */; };
66 | 537250791EF13988007F3A10 /* XibModuleRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537250731EF13988007F3A10 /* XibModuleRouter.swift */; };
67 | 5372507A1EF13988007F3A10 /* XibModuleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537250741EF13988007F3A10 /* XibModuleView.swift */; };
68 | 5372507C1EF1399E007F3A10 /* XibModule.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5372507B1EF1399E007F3A10 /* XibModule.xib */; };
69 | 5372507E1EF1503A007F3A10 /* TestCleanModules.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5372507D1EF1503A007F3A10 /* TestCleanModules.swift */; };
70 | 537B7E9B232FDB25002681C6 /* SwiftUIPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537B7E96232FDB25002681C6 /* SwiftUIPresenter.swift */; };
71 | 537B7E9C232FDB25002681C6 /* SwiftUIRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537B7E97232FDB25002681C6 /* SwiftUIRouter.swift */; };
72 | 537B7E9D232FDB25002681C6 /* SwiftUIModuleApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537B7E98232FDB25002681C6 /* SwiftUIModuleApi.swift */; };
73 | 537B7E9E232FDB25002681C6 /* SwiftUIInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537B7E99232FDB25002681C6 /* SwiftUIInteractor.swift */; };
74 | 537B7E9F232FDB25002681C6 /* SwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537B7E9A232FDB25002681C6 /* SwiftUIView.swift */; };
75 | 537B7EA6232FE287002681C6 /* SwiftUIWithEnvPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537B7EA1232FE287002681C6 /* SwiftUIWithEnvPresenter.swift */; };
76 | 537B7EA7232FE287002681C6 /* SwiftUIWithEnvRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537B7EA2232FE287002681C6 /* SwiftUIWithEnvRouter.swift */; };
77 | 537B7EA8232FE287002681C6 /* SwiftUIWithEnvModuleApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537B7EA3232FE287002681C6 /* SwiftUIWithEnvModuleApi.swift */; };
78 | 537B7EA9232FE287002681C6 /* SwiftUIWithEnvInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537B7EA4232FE287002681C6 /* SwiftUIWithEnvInteractor.swift */; };
79 | 537B7EAA232FE287002681C6 /* SwiftUIWithEnvView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537B7EA5232FE287002681C6 /* SwiftUIWithEnvView.swift */; };
80 | 537D19E41E7BE9B100A758FF /* ModuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537D19E31E7BE9B100A758FF /* ModuleTests.swift */; };
81 | 539580A6207378C3009A66BC /* ExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 539580A5207378C3009A66BC /* ExtensionsTests.swift */; };
82 | 53A2851722C22EDF005CC4C8 /* UserInterface+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A2851622C22EDF005CC4C8 /* UserInterface+SwiftUI.swift */; };
83 | 53A86CBF2315D2EE00E6B88C /* UserSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A86CBE2315D2EE00E6B88C /* UserSettings.swift */; };
84 | 53D3AEF61E0709A80099A464 /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AEF51E0709A80099A464 /* String+.swift */; };
85 | 53D3AF111E07109D0099A464 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AEFC1E07109D0099A464 /* AppDelegate.swift */; };
86 | 53D3AF121E07109D0099A464 /* AppModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AEFD1E07109D0099A464 /* AppModule.swift */; };
87 | 53D3AF131E07109D0099A464 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53D3AEFE1E07109D0099A464 /* LaunchScreen.storyboard */; };
88 | 53D3AF151E07109D0099A464 /* Home.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53D3AF031E07109D0099A464 /* Home.storyboard */; };
89 | 53D3AF161E07109D0099A464 /* HomeDisplayData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AF041E07109D0099A464 /* HomeDisplayData.swift */; };
90 | 53D3AF171E07109D0099A464 /* HomeInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AF051E07109D0099A464 /* HomeInteractor.swift */; };
91 | 53D3AF181E07109D0099A464 /* HomePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AF061E07109D0099A464 /* HomePresenter.swift */; };
92 | 53D3AF191E07109D0099A464 /* HomeRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AF071E07109D0099A464 /* HomeRouter.swift */; };
93 | 53D3AF1A1E07109D0099A464 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AF081E07109D0099A464 /* HomeView.swift */; };
94 | 53D3AF1B1E07109D0099A464 /* Second.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53D3AF0A1E07109D0099A464 /* Second.storyboard */; };
95 | 53D3AF1C1E07109D0099A464 /* SecondDisplayData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AF0B1E07109D0099A464 /* SecondDisplayData.swift */; };
96 | 53D3AF1D1E07109D0099A464 /* SecondInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AF0C1E07109D0099A464 /* SecondInteractor.swift */; };
97 | 53D3AF1E1E07109D0099A464 /* SecondPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AF0D1E07109D0099A464 /* SecondPresenter.swift */; };
98 | 53D3AF1F1E07109D0099A464 /* SecondRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AF0E1E07109D0099A464 /* SecondRouter.swift */; };
99 | 53D3AF201E07109D0099A464 /* SecondView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AF0F1E07109D0099A464 /* SecondView.swift */; };
100 | 53D3AF211E07109D0099A464 /* SecondViewPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AF101E07109D0099A464 /* SecondViewPad.swift */; };
101 | 53D3AF321E0711000099A464 /* HomeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D3AF311E0711000099A464 /* HomeTests.swift */; };
102 | 53F14AA41E7BF3D400E61452 /* Viperit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5367C0CF1DD7C68D005B6676 /* Viperit.framework */; };
103 | /* End PBXBuildFile section */
104 |
105 | /* Begin PBXContainerItemProxy section */
106 | 53388C2B1DD9FFDF00EA4CEC /* PBXContainerItemProxy */ = {
107 | isa = PBXContainerItemProxy;
108 | containerPortal = 53532C301DD50BD200088AAC /* Project object */;
109 | proxyType = 1;
110 | remoteGlobalIDString = 5367C0CE1DD7C68D005B6676;
111 | remoteInfo = Viperit;
112 | };
113 | 53D3AF2B1E0710E90099A464 /* PBXContainerItemProxy */ = {
114 | isa = PBXContainerItemProxy;
115 | containerPortal = 53532C301DD50BD200088AAC /* Project object */;
116 | proxyType = 1;
117 | remoteGlobalIDString = 53532C371DD50BD200088AAC;
118 | remoteInfo = Example;
119 | };
120 | /* End PBXContainerItemProxy section */
121 |
122 | /* Begin PBXCopyFilesBuildPhase section */
123 | 53388C2D1DD9FFDF00EA4CEC /* Embed Frameworks */ = {
124 | isa = PBXCopyFilesBuildPhase;
125 | buildActionMask = 2147483647;
126 | dstPath = "";
127 | dstSubfolderSpec = 10;
128 | files = (
129 | 53388C2A1DD9FFDF00EA4CEC /* Viperit.framework in Embed Frameworks */,
130 | );
131 | name = "Embed Frameworks";
132 | runOnlyForDeploymentPostprocessing = 0;
133 | };
134 | /* End PBXCopyFilesBuildPhase section */
135 |
136 | /* Begin PBXFileReference section */
137 | 53023EDB225257E8004ACC5C /* UserInterface+Table.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserInterface+Table.swift"; sourceTree = ""; };
138 | 53023EDD22525814004ACC5C /* UserInterface+Collection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserInterface+Collection.swift"; sourceTree = ""; };
139 | 53023EDF2252582C004ACC5C /* UserInterface+Split.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserInterface+Split.swift"; sourceTree = ""; };
140 | 53077FE0232F8CAB0078D855 /* SimpleDisplayData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleDisplayData.swift; sourceTree = ""; };
141 | 53077FE1232F8CAB0078D855 /* SimplePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimplePresenter.swift; sourceTree = ""; };
142 | 53077FE2232F8CAB0078D855 /* SimpleRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleRouter.swift; sourceTree = ""; };
143 | 53077FE3232F8CAB0078D855 /* SimpleModuleApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleModuleApi.swift; sourceTree = ""; };
144 | 53077FE4232F8CAB0078D855 /* SimpleInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleInteractor.swift; sourceTree = ""; };
145 | 53077FE5232F8CAB0078D855 /* SimpleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleView.swift; sourceTree = ""; };
146 | 5327EAD322526175006D5FCC /* TableOfContentsDisplayData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableOfContentsDisplayData.swift; sourceTree = ""; };
147 | 5327EAD422526175006D5FCC /* TableOfContentsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableOfContentsPresenter.swift; sourceTree = ""; };
148 | 5327EAD522526175006D5FCC /* TableOfContentsRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableOfContentsRouter.swift; sourceTree = ""; };
149 | 5327EAD622526175006D5FCC /* TableOfContentsModuleApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableOfContentsModuleApi.swift; sourceTree = ""; };
150 | 5327EAD722526175006D5FCC /* TableOfContentsInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableOfContentsInteractor.swift; sourceTree = ""; };
151 | 5327EAD822526175006D5FCC /* TableOfContentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableOfContentsView.swift; sourceTree = ""; };
152 | 53532C381DD50BD200088AAC /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
153 | 53532C4C1DD50BD200088AAC /* ViperitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ViperitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
154 | 53532C521DD50BD200088AAC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
155 | 53532C5D1DD50DDF00088AAC /* DisplayData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayData.swift; sourceTree = ""; };
156 | 53532C5E1DD50DDF00088AAC /* Interactor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Interactor.swift; sourceTree = ""; };
157 | 53532C5F1DD50DDF00088AAC /* Module.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Module.swift; sourceTree = ""; };
158 | 53532C601DD50DDF00088AAC /* Presenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Presenter.swift; sourceTree = ""; };
159 | 53532C611DD50DDF00088AAC /* Router.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; };
160 | 53532C621DD50DDF00088AAC /* UserInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInterface.swift; sourceTree = ""; };
161 | 53532C631DD50DDF00088AAC /* ViperComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViperComponent.swift; sourceTree = ""; };
162 | 53532C641DD50DDF00088AAC /* ViperitError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViperitError.swift; sourceTree = ""; };
163 | 5355BEC6232FC65600BC7CF9 /* CoolPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoolPresenter.swift; sourceTree = ""; };
164 | 5355BEC7232FC65600BC7CF9 /* CoolRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoolRouter.swift; sourceTree = ""; };
165 | 5355BEC8232FC65600BC7CF9 /* CoolModuleApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoolModuleApi.swift; sourceTree = ""; };
166 | 5355BEC9232FC65600BC7CF9 /* CoolInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoolInteractor.swift; sourceTree = ""; };
167 | 5355BECA232FC65600BC7CF9 /* CoolView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoolView.swift; sourceTree = ""; };
168 | 5355BED1232FC76900BC7CF9 /* PerfectPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerfectPresenter.swift; sourceTree = ""; };
169 | 5355BED2232FC76900BC7CF9 /* PerfectRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerfectRouter.swift; sourceTree = ""; };
170 | 5355BED3232FC76900BC7CF9 /* PerfectModuleApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerfectModuleApi.swift; sourceTree = ""; };
171 | 5355BED4232FC76900BC7CF9 /* PerfectInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerfectInteractor.swift; sourceTree = ""; };
172 | 5355BED5232FC76900BC7CF9 /* PerfectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerfectView.swift; sourceTree = ""; };
173 | 5360ADF21E81280100C1AACB /* PresenterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresenterTests.swift; sourceTree = ""; };
174 | 5367C0CF1DD7C68D005B6676 /* Viperit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Viperit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
175 | 5368EDC61E7FF91F0028D8CA /* RouterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouterTests.swift; sourceTree = ""; };
176 | 5368EDC81E7FF9A40028D8CA /* TestModules.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestModules.swift; sourceTree = ""; };
177 | 5368EDCA1E801EDB0028D8CA /* Sample.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Sample.storyboard; sourceTree = ""; };
178 | 5368EDCB1E801EDB0028D8CA /* SampleDisplayData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleDisplayData.swift; sourceTree = ""; };
179 | 5368EDCC1E801EDB0028D8CA /* SampleInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleInteractor.swift; sourceTree = ""; };
180 | 5368EDCD1E801EDB0028D8CA /* SamplePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SamplePresenter.swift; sourceTree = ""; };
181 | 5368EDCE1E801EDB0028D8CA /* SampleRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleRouter.swift; sourceTree = ""; };
182 | 5368EDCF1E801EDB0028D8CA /* SampleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleView.swift; sourceTree = ""; };
183 | 5368EDD01E801EDB0028D8CA /* SampleViewPad.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleViewPad.swift; sourceTree = ""; };
184 | 5372505D1EF13500007F3A10 /* ViperitModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViperitModule.swift; sourceTree = ""; };
185 | 537250631EF13835007F3A10 /* CodeModuleDisplayData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeModuleDisplayData.swift; sourceTree = ""; };
186 | 537250641EF13835007F3A10 /* CodeModuleInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeModuleInteractor.swift; sourceTree = ""; };
187 | 537250651EF13835007F3A10 /* CodeModuleModuleApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeModuleModuleApi.swift; sourceTree = ""; };
188 | 537250661EF13835007F3A10 /* CodeModulePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeModulePresenter.swift; sourceTree = ""; };
189 | 537250671EF13835007F3A10 /* CodeModuleRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeModuleRouter.swift; sourceTree = ""; };
190 | 537250681EF13835007F3A10 /* CodeModuleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeModuleView.swift; sourceTree = ""; };
191 | 5372506F1EF13988007F3A10 /* XibModuleDisplayData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibModuleDisplayData.swift; sourceTree = ""; };
192 | 537250701EF13988007F3A10 /* XibModuleInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibModuleInteractor.swift; sourceTree = ""; };
193 | 537250711EF13988007F3A10 /* XibModuleModuleApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibModuleModuleApi.swift; sourceTree = ""; };
194 | 537250721EF13988007F3A10 /* XibModulePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibModulePresenter.swift; sourceTree = ""; };
195 | 537250731EF13988007F3A10 /* XibModuleRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibModuleRouter.swift; sourceTree = ""; };
196 | 537250741EF13988007F3A10 /* XibModuleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibModuleView.swift; sourceTree = ""; };
197 | 5372507B1EF1399E007F3A10 /* XibModule.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = XibModule.xib; sourceTree = ""; };
198 | 5372507D1EF1503A007F3A10 /* TestCleanModules.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestCleanModules.swift; sourceTree = ""; };
199 | 537B7E96232FDB25002681C6 /* SwiftUIPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIPresenter.swift; sourceTree = ""; };
200 | 537B7E97232FDB25002681C6 /* SwiftUIRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIRouter.swift; sourceTree = ""; };
201 | 537B7E98232FDB25002681C6 /* SwiftUIModuleApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIModuleApi.swift; sourceTree = ""; };
202 | 537B7E99232FDB25002681C6 /* SwiftUIInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIInteractor.swift; sourceTree = ""; };
203 | 537B7E9A232FDB25002681C6 /* SwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIView.swift; sourceTree = ""; };
204 | 537B7EA1232FE287002681C6 /* SwiftUIWithEnvPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIWithEnvPresenter.swift; sourceTree = ""; };
205 | 537B7EA2232FE287002681C6 /* SwiftUIWithEnvRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIWithEnvRouter.swift; sourceTree = ""; };
206 | 537B7EA3232FE287002681C6 /* SwiftUIWithEnvModuleApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIWithEnvModuleApi.swift; sourceTree = ""; };
207 | 537B7EA4232FE287002681C6 /* SwiftUIWithEnvInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIWithEnvInteractor.swift; sourceTree = ""; };
208 | 537B7EA5232FE287002681C6 /* SwiftUIWithEnvView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIWithEnvView.swift; sourceTree = ""; };
209 | 537D19E31E7BE9B100A758FF /* ModuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModuleTests.swift; sourceTree = ""; };
210 | 539580A5207378C3009A66BC /* ExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionsTests.swift; sourceTree = ""; };
211 | 53A2851622C22EDF005CC4C8 /* UserInterface+SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserInterface+SwiftUI.swift"; sourceTree = ""; };
212 | 53A86CBE2315D2EE00E6B88C /* UserSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettings.swift; sourceTree = ""; };
213 | 53D3AEF51E0709A80099A464 /* String+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; };
214 | 53D3AEFA1E070AC60099A464 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
215 | 53D3AEFC1E07109D0099A464 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
216 | 53D3AEFD1E07109D0099A464 /* AppModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppModule.swift; sourceTree = ""; };
217 | 53D3AEFF1E07109D0099A464 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
218 | 53D3AF001E07109D0099A464 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
219 | 53D3AF031E07109D0099A464 /* Home.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Home.storyboard; sourceTree = ""; };
220 | 53D3AF041E07109D0099A464 /* HomeDisplayData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeDisplayData.swift; sourceTree = ""; };
221 | 53D3AF051E07109D0099A464 /* HomeInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeInteractor.swift; sourceTree = ""; };
222 | 53D3AF061E07109D0099A464 /* HomePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomePresenter.swift; sourceTree = ""; };
223 | 53D3AF071E07109D0099A464 /* HomeRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeRouter.swift; sourceTree = ""; };
224 | 53D3AF081E07109D0099A464 /* HomeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; };
225 | 53D3AF0A1E07109D0099A464 /* Second.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Second.storyboard; sourceTree = ""; };
226 | 53D3AF0B1E07109D0099A464 /* SecondDisplayData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondDisplayData.swift; sourceTree = ""; };
227 | 53D3AF0C1E07109D0099A464 /* SecondInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondInteractor.swift; sourceTree = ""; };
228 | 53D3AF0D1E07109D0099A464 /* SecondPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondPresenter.swift; sourceTree = ""; };
229 | 53D3AF0E1E07109D0099A464 /* SecondRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondRouter.swift; sourceTree = ""; };
230 | 53D3AF0F1E07109D0099A464 /* SecondView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondView.swift; sourceTree = ""; };
231 | 53D3AF101E07109D0099A464 /* SecondViewPad.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondViewPad.swift; sourceTree = ""; };
232 | 53D3AF261E0710E90099A464 /* ExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
233 | 53D3AF311E0711000099A464 /* HomeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeTests.swift; sourceTree = ""; };
234 | 53D3AF331E0711AE0099A464 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
235 | /* End PBXFileReference section */
236 |
237 | /* Begin PBXFrameworksBuildPhase section */
238 | 53532C351DD50BD200088AAC /* Frameworks */ = {
239 | isa = PBXFrameworksBuildPhase;
240 | buildActionMask = 2147483647;
241 | files = (
242 | 53388C291DD9FFDF00EA4CEC /* Viperit.framework in Frameworks */,
243 | );
244 | runOnlyForDeploymentPostprocessing = 0;
245 | };
246 | 53532C491DD50BD200088AAC /* Frameworks */ = {
247 | isa = PBXFrameworksBuildPhase;
248 | buildActionMask = 2147483647;
249 | files = (
250 | 53F14AA41E7BF3D400E61452 /* Viperit.framework in Frameworks */,
251 | );
252 | runOnlyForDeploymentPostprocessing = 0;
253 | };
254 | 5367C0CB1DD7C68D005B6676 /* Frameworks */ = {
255 | isa = PBXFrameworksBuildPhase;
256 | buildActionMask = 2147483647;
257 | files = (
258 | );
259 | runOnlyForDeploymentPostprocessing = 0;
260 | };
261 | 53D3AF231E0710E90099A464 /* Frameworks */ = {
262 | isa = PBXFrameworksBuildPhase;
263 | buildActionMask = 2147483647;
264 | files = (
265 | );
266 | runOnlyForDeploymentPostprocessing = 0;
267 | };
268 | /* End PBXFrameworksBuildPhase section */
269 |
270 | /* Begin PBXGroup section */
271 | 53077FDF232F8C6D0078D855 /* Simple */ = {
272 | isa = PBXGroup;
273 | children = (
274 | 53077FE0232F8CAB0078D855 /* SimpleDisplayData.swift */,
275 | 53077FE1232F8CAB0078D855 /* SimplePresenter.swift */,
276 | 53077FE2232F8CAB0078D855 /* SimpleRouter.swift */,
277 | 53077FE3232F8CAB0078D855 /* SimpleModuleApi.swift */,
278 | 53077FE4232F8CAB0078D855 /* SimpleInteractor.swift */,
279 | 53077FE5232F8CAB0078D855 /* SimpleView.swift */,
280 | );
281 | path = Simple;
282 | sourceTree = "";
283 | };
284 | 5327EAD222526155006D5FCC /* TableOfContents */ = {
285 | isa = PBXGroup;
286 | children = (
287 | 5327EAD322526175006D5FCC /* TableOfContentsDisplayData.swift */,
288 | 5327EAD422526175006D5FCC /* TableOfContentsPresenter.swift */,
289 | 5327EAD522526175006D5FCC /* TableOfContentsRouter.swift */,
290 | 5327EAD622526175006D5FCC /* TableOfContentsModuleApi.swift */,
291 | 5327EAD722526175006D5FCC /* TableOfContentsInteractor.swift */,
292 | 5327EAD822526175006D5FCC /* TableOfContentsView.swift */,
293 | );
294 | path = TableOfContents;
295 | sourceTree = "";
296 | };
297 | 534AF1EF1E7BED06009D2D61 /* Sample */ = {
298 | isa = PBXGroup;
299 | children = (
300 | 5368EDC81E7FF9A40028D8CA /* TestModules.swift */,
301 | 5372507D1EF1503A007F3A10 /* TestCleanModules.swift */,
302 | 5372505F1EF137A1007F3A10 /* Modules */,
303 | );
304 | path = Sample;
305 | sourceTree = "";
306 | };
307 | 53532C2F1DD50BD200088AAC = {
308 | isa = PBXGroup;
309 | children = (
310 | 53532C3A1DD50BD200088AAC /* Viperit */,
311 | 53532C4F1DD50BD200088AAC /* ViperitTests */,
312 | 53D3AEFB1E07109D0099A464 /* Example */,
313 | 53D3AF301E0711000099A464 /* ExampleTests */,
314 | 53532C391DD50BD200088AAC /* Products */,
315 | );
316 | sourceTree = "";
317 | };
318 | 53532C391DD50BD200088AAC /* Products */ = {
319 | isa = PBXGroup;
320 | children = (
321 | 53532C381DD50BD200088AAC /* Example.app */,
322 | 53532C4C1DD50BD200088AAC /* ViperitTests.xctest */,
323 | 5367C0CF1DD7C68D005B6676 /* Viperit.framework */,
324 | 53D3AF261E0710E90099A464 /* ExampleTests.xctest */,
325 | );
326 | name = Products;
327 | sourceTree = "";
328 | };
329 | 53532C3A1DD50BD200088AAC /* Viperit */ = {
330 | isa = PBXGroup;
331 | children = (
332 | 53D3AEFA1E070AC60099A464 /* Info.plist */,
333 | 53532C5C1DD50DDF00088AAC /* Core */,
334 | 53D3AEF41E0709A80099A464 /* Extensions */,
335 | );
336 | path = Viperit;
337 | sourceTree = "";
338 | };
339 | 53532C4F1DD50BD200088AAC /* ViperitTests */ = {
340 | isa = PBXGroup;
341 | children = (
342 | 534AF1EF1E7BED06009D2D61 /* Sample */,
343 | 537D19E31E7BE9B100A758FF /* ModuleTests.swift */,
344 | 5360ADF21E81280100C1AACB /* PresenterTests.swift */,
345 | 5368EDC61E7FF91F0028D8CA /* RouterTests.swift */,
346 | 53532C521DD50BD200088AAC /* Info.plist */,
347 | 539580A5207378C3009A66BC /* ExtensionsTests.swift */,
348 | );
349 | path = ViperitTests;
350 | sourceTree = "";
351 | };
352 | 53532C5C1DD50DDF00088AAC /* Core */ = {
353 | isa = PBXGroup;
354 | children = (
355 | 53532C641DD50DDF00088AAC /* ViperitError.swift */,
356 | 53532C631DD50DDF00088AAC /* ViperComponent.swift */,
357 | 5372505D1EF13500007F3A10 /* ViperitModule.swift */,
358 | 53532C5F1DD50DDF00088AAC /* Module.swift */,
359 | 53532C621DD50DDF00088AAC /* UserInterface.swift */,
360 | 53532C5D1DD50DDF00088AAC /* DisplayData.swift */,
361 | 53532C5E1DD50DDF00088AAC /* Interactor.swift */,
362 | 53532C601DD50DDF00088AAC /* Presenter.swift */,
363 | 53532C611DD50DDF00088AAC /* Router.swift */,
364 | );
365 | path = Core;
366 | sourceTree = "";
367 | };
368 | 5355BEC5232FC64A00BC7CF9 /* Cool */ = {
369 | isa = PBXGroup;
370 | children = (
371 | 53A86CBD2315D2DD00E6B88C /* viewModels */,
372 | 5355BEC6232FC65600BC7CF9 /* CoolPresenter.swift */,
373 | 5355BEC7232FC65600BC7CF9 /* CoolRouter.swift */,
374 | 5355BEC8232FC65600BC7CF9 /* CoolModuleApi.swift */,
375 | 5355BEC9232FC65600BC7CF9 /* CoolInteractor.swift */,
376 | 5355BECA232FC65600BC7CF9 /* CoolView.swift */,
377 | );
378 | path = Cool;
379 | sourceTree = "";
380 | };
381 | 5355BED0232FC75C00BC7CF9 /* Perfect */ = {
382 | isa = PBXGroup;
383 | children = (
384 | 5355BED3232FC76900BC7CF9 /* PerfectModuleApi.swift */,
385 | 5355BED1232FC76900BC7CF9 /* PerfectPresenter.swift */,
386 | 5355BED2232FC76900BC7CF9 /* PerfectRouter.swift */,
387 | 5355BED4232FC76900BC7CF9 /* PerfectInteractor.swift */,
388 | 5355BED5232FC76900BC7CF9 /* PerfectView.swift */,
389 | );
390 | path = Perfect;
391 | sourceTree = "";
392 | };
393 | 5372505F1EF137A1007F3A10 /* Modules */ = {
394 | isa = PBXGroup;
395 | children = (
396 | 537B7EA0232FE277002681C6 /* SwiftUIWithEnv */,
397 | 537B7E95232FDAE9002681C6 /* SwiftUI */,
398 | 537250601EF137B4007F3A10 /* Sample */,
399 | 537250611EF137BD007F3A10 /* XibModule */,
400 | 537250621EF137C7007F3A10 /* CodeModule */,
401 | );
402 | path = Modules;
403 | sourceTree = "";
404 | };
405 | 537250601EF137B4007F3A10 /* Sample */ = {
406 | isa = PBXGroup;
407 | children = (
408 | 5368EDCA1E801EDB0028D8CA /* Sample.storyboard */,
409 | 5368EDCB1E801EDB0028D8CA /* SampleDisplayData.swift */,
410 | 5368EDCC1E801EDB0028D8CA /* SampleInteractor.swift */,
411 | 5368EDCD1E801EDB0028D8CA /* SamplePresenter.swift */,
412 | 5368EDCE1E801EDB0028D8CA /* SampleRouter.swift */,
413 | 5368EDCF1E801EDB0028D8CA /* SampleView.swift */,
414 | 5368EDD01E801EDB0028D8CA /* SampleViewPad.swift */,
415 | );
416 | path = Sample;
417 | sourceTree = "";
418 | };
419 | 537250611EF137BD007F3A10 /* XibModule */ = {
420 | isa = PBXGroup;
421 | children = (
422 | 5372507B1EF1399E007F3A10 /* XibModule.xib */,
423 | 5372506F1EF13988007F3A10 /* XibModuleDisplayData.swift */,
424 | 537250701EF13988007F3A10 /* XibModuleInteractor.swift */,
425 | 537250711EF13988007F3A10 /* XibModuleModuleApi.swift */,
426 | 537250721EF13988007F3A10 /* XibModulePresenter.swift */,
427 | 537250731EF13988007F3A10 /* XibModuleRouter.swift */,
428 | 537250741EF13988007F3A10 /* XibModuleView.swift */,
429 | );
430 | path = XibModule;
431 | sourceTree = "";
432 | };
433 | 537250621EF137C7007F3A10 /* CodeModule */ = {
434 | isa = PBXGroup;
435 | children = (
436 | 537250631EF13835007F3A10 /* CodeModuleDisplayData.swift */,
437 | 537250641EF13835007F3A10 /* CodeModuleInteractor.swift */,
438 | 537250651EF13835007F3A10 /* CodeModuleModuleApi.swift */,
439 | 537250661EF13835007F3A10 /* CodeModulePresenter.swift */,
440 | 537250671EF13835007F3A10 /* CodeModuleRouter.swift */,
441 | 537250681EF13835007F3A10 /* CodeModuleView.swift */,
442 | );
443 | path = CodeModule;
444 | sourceTree = "";
445 | };
446 | 537B7E95232FDAE9002681C6 /* SwiftUI */ = {
447 | isa = PBXGroup;
448 | children = (
449 | 537B7E96232FDB25002681C6 /* SwiftUIPresenter.swift */,
450 | 537B7E97232FDB25002681C6 /* SwiftUIRouter.swift */,
451 | 537B7E98232FDB25002681C6 /* SwiftUIModuleApi.swift */,
452 | 537B7E99232FDB25002681C6 /* SwiftUIInteractor.swift */,
453 | 537B7E9A232FDB25002681C6 /* SwiftUIView.swift */,
454 | );
455 | path = SwiftUI;
456 | sourceTree = "";
457 | };
458 | 537B7EA0232FE277002681C6 /* SwiftUIWithEnv */ = {
459 | isa = PBXGroup;
460 | children = (
461 | 537B7EA1232FE287002681C6 /* SwiftUIWithEnvPresenter.swift */,
462 | 537B7EA2232FE287002681C6 /* SwiftUIWithEnvRouter.swift */,
463 | 537B7EA3232FE287002681C6 /* SwiftUIWithEnvModuleApi.swift */,
464 | 537B7EA4232FE287002681C6 /* SwiftUIWithEnvInteractor.swift */,
465 | 537B7EA5232FE287002681C6 /* SwiftUIWithEnvView.swift */,
466 | );
467 | path = SwiftUIWithEnv;
468 | sourceTree = "";
469 | };
470 | 53A86CBD2315D2DD00E6B88C /* viewModels */ = {
471 | isa = PBXGroup;
472 | children = (
473 | 53A86CBE2315D2EE00E6B88C /* UserSettings.swift */,
474 | );
475 | path = viewModels;
476 | sourceTree = "";
477 | };
478 | 53D3AEF41E0709A80099A464 /* Extensions */ = {
479 | isa = PBXGroup;
480 | children = (
481 | 53D3AEF51E0709A80099A464 /* String+.swift */,
482 | 53023EDF2252582C004ACC5C /* UserInterface+Split.swift */,
483 | 53023EDB225257E8004ACC5C /* UserInterface+Table.swift */,
484 | 53023EDD22525814004ACC5C /* UserInterface+Collection.swift */,
485 | 53A2851622C22EDF005CC4C8 /* UserInterface+SwiftUI.swift */,
486 | );
487 | path = Extensions;
488 | sourceTree = "";
489 | };
490 | 53D3AEFB1E07109D0099A464 /* Example */ = {
491 | isa = PBXGroup;
492 | children = (
493 | 53D3AEFC1E07109D0099A464 /* AppDelegate.swift */,
494 | 53D3AEFD1E07109D0099A464 /* AppModule.swift */,
495 | 53D3AEFE1E07109D0099A464 /* LaunchScreen.storyboard */,
496 | 53D3AF001E07109D0099A464 /* Info.plist */,
497 | 53D3AF011E07109D0099A464 /* modules */,
498 | );
499 | path = Example;
500 | sourceTree = "";
501 | };
502 | 53D3AF011E07109D0099A464 /* modules */ = {
503 | isa = PBXGroup;
504 | children = (
505 | 5355BED0232FC75C00BC7CF9 /* Perfect */,
506 | 5355BEC5232FC64A00BC7CF9 /* Cool */,
507 | 53077FDF232F8C6D0078D855 /* Simple */,
508 | 5327EAD222526155006D5FCC /* TableOfContents */,
509 | 53D3AF021E07109D0099A464 /* Home */,
510 | 53D3AF091E07109D0099A464 /* Second */,
511 | );
512 | path = modules;
513 | sourceTree = "";
514 | };
515 | 53D3AF021E07109D0099A464 /* Home */ = {
516 | isa = PBXGroup;
517 | children = (
518 | 53D3AF031E07109D0099A464 /* Home.storyboard */,
519 | 53D3AF041E07109D0099A464 /* HomeDisplayData.swift */,
520 | 53D3AF051E07109D0099A464 /* HomeInteractor.swift */,
521 | 53D3AF061E07109D0099A464 /* HomePresenter.swift */,
522 | 53D3AF071E07109D0099A464 /* HomeRouter.swift */,
523 | 53D3AF081E07109D0099A464 /* HomeView.swift */,
524 | );
525 | path = Home;
526 | sourceTree = "";
527 | };
528 | 53D3AF091E07109D0099A464 /* Second */ = {
529 | isa = PBXGroup;
530 | children = (
531 | 53D3AF0A1E07109D0099A464 /* Second.storyboard */,
532 | 53D3AF0B1E07109D0099A464 /* SecondDisplayData.swift */,
533 | 53D3AF0C1E07109D0099A464 /* SecondInteractor.swift */,
534 | 53D3AF0D1E07109D0099A464 /* SecondPresenter.swift */,
535 | 53D3AF0E1E07109D0099A464 /* SecondRouter.swift */,
536 | 53D3AF0F1E07109D0099A464 /* SecondView.swift */,
537 | 53D3AF101E07109D0099A464 /* SecondViewPad.swift */,
538 | );
539 | path = Second;
540 | sourceTree = "";
541 | };
542 | 53D3AF301E0711000099A464 /* ExampleTests */ = {
543 | isa = PBXGroup;
544 | children = (
545 | 53D3AF311E0711000099A464 /* HomeTests.swift */,
546 | 53D3AF331E0711AE0099A464 /* Info.plist */,
547 | );
548 | path = ExampleTests;
549 | sourceTree = "";
550 | };
551 | /* End PBXGroup section */
552 |
553 | /* Begin PBXHeadersBuildPhase section */
554 | 5367C0CC1DD7C68D005B6676 /* Headers */ = {
555 | isa = PBXHeadersBuildPhase;
556 | buildActionMask = 2147483647;
557 | files = (
558 | );
559 | runOnlyForDeploymentPostprocessing = 0;
560 | };
561 | /* End PBXHeadersBuildPhase section */
562 |
563 | /* Begin PBXNativeTarget section */
564 | 53532C371DD50BD200088AAC /* Example */ = {
565 | isa = PBXNativeTarget;
566 | buildConfigurationList = 53532C551DD50BD200088AAC /* Build configuration list for PBXNativeTarget "Example" */;
567 | buildPhases = (
568 | 53532C341DD50BD200088AAC /* Sources */,
569 | 53532C351DD50BD200088AAC /* Frameworks */,
570 | 53532C361DD50BD200088AAC /* Resources */,
571 | 53388C2D1DD9FFDF00EA4CEC /* Embed Frameworks */,
572 | );
573 | buildRules = (
574 | );
575 | dependencies = (
576 | 53388C2C1DD9FFDF00EA4CEC /* PBXTargetDependency */,
577 | );
578 | name = Example;
579 | productName = Viperit;
580 | productReference = 53532C381DD50BD200088AAC /* Example.app */;
581 | productType = "com.apple.product-type.application";
582 | };
583 | 53532C4B1DD50BD200088AAC /* ViperitTests */ = {
584 | isa = PBXNativeTarget;
585 | buildConfigurationList = 53532C581DD50BD200088AAC /* Build configuration list for PBXNativeTarget "ViperitTests" */;
586 | buildPhases = (
587 | 53532C481DD50BD200088AAC /* Sources */,
588 | 53532C491DD50BD200088AAC /* Frameworks */,
589 | 53532C4A1DD50BD200088AAC /* Resources */,
590 | );
591 | buildRules = (
592 | );
593 | dependencies = (
594 | );
595 | name = ViperitTests;
596 | productName = ViperitTests;
597 | productReference = 53532C4C1DD50BD200088AAC /* ViperitTests.xctest */;
598 | productType = "com.apple.product-type.bundle.unit-test";
599 | };
600 | 5367C0CE1DD7C68D005B6676 /* Viperit */ = {
601 | isa = PBXNativeTarget;
602 | buildConfigurationList = 5367C0D91DD7C68D005B6676 /* Build configuration list for PBXNativeTarget "Viperit" */;
603 | buildPhases = (
604 | 5367C0CA1DD7C68D005B6676 /* Sources */,
605 | 5367C0CB1DD7C68D005B6676 /* Frameworks */,
606 | 5367C0CC1DD7C68D005B6676 /* Headers */,
607 | 5367C0CD1DD7C68D005B6676 /* Resources */,
608 | );
609 | buildRules = (
610 | );
611 | dependencies = (
612 | );
613 | name = Viperit;
614 | productName = Viperit;
615 | productReference = 5367C0CF1DD7C68D005B6676 /* Viperit.framework */;
616 | productType = "com.apple.product-type.framework";
617 | };
618 | 53D3AF251E0710E90099A464 /* ExampleTests */ = {
619 | isa = PBXNativeTarget;
620 | buildConfigurationList = 53D3AF2D1E0710E90099A464 /* Build configuration list for PBXNativeTarget "ExampleTests" */;
621 | buildPhases = (
622 | 53D3AF221E0710E90099A464 /* Sources */,
623 | 53D3AF231E0710E90099A464 /* Frameworks */,
624 | 53D3AF241E0710E90099A464 /* Resources */,
625 | );
626 | buildRules = (
627 | );
628 | dependencies = (
629 | 53D3AF2C1E0710E90099A464 /* PBXTargetDependency */,
630 | );
631 | name = ExampleTests;
632 | productName = ExampleTests;
633 | productReference = 53D3AF261E0710E90099A464 /* ExampleTests.xctest */;
634 | productType = "com.apple.product-type.bundle.unit-test";
635 | };
636 | /* End PBXNativeTarget section */
637 |
638 | /* Begin PBXProject section */
639 | 53532C301DD50BD200088AAC /* Project object */ = {
640 | isa = PBXProject;
641 | attributes = {
642 | LastSwiftUpdateCheck = 0820;
643 | LastUpgradeCheck = 1300;
644 | ORGANIZATIONNAME = "Ferran Abelló";
645 | TargetAttributes = {
646 | 53532C371DD50BD200088AAC = {
647 | CreatedOnToolsVersion = 8.1;
648 | LastSwiftMigration = 1020;
649 | ProvisioningStyle = Automatic;
650 | };
651 | 53532C4B1DD50BD200088AAC = {
652 | CreatedOnToolsVersion = 8.1;
653 | LastSwiftMigration = 1020;
654 | ProvisioningStyle = Automatic;
655 | };
656 | 5367C0CE1DD7C68D005B6676 = {
657 | CreatedOnToolsVersion = 8.1;
658 | LastSwiftMigration = 1020;
659 | ProvisioningStyle = Manual;
660 | };
661 | 53D3AF251E0710E90099A464 = {
662 | CreatedOnToolsVersion = 8.2;
663 | LastSwiftMigration = 1020;
664 | ProvisioningStyle = Automatic;
665 | TestTargetID = 53532C371DD50BD200088AAC;
666 | };
667 | };
668 | };
669 | buildConfigurationList = 53532C331DD50BD200088AAC /* Build configuration list for PBXProject "Viperit" */;
670 | compatibilityVersion = "Xcode 3.2";
671 | developmentRegion = en;
672 | hasScannedForEncodings = 0;
673 | knownRegions = (
674 | en,
675 | Base,
676 | );
677 | mainGroup = 53532C2F1DD50BD200088AAC;
678 | productRefGroup = 53532C391DD50BD200088AAC /* Products */;
679 | projectDirPath = "";
680 | projectRoot = "";
681 | targets = (
682 | 5367C0CE1DD7C68D005B6676 /* Viperit */,
683 | 53532C4B1DD50BD200088AAC /* ViperitTests */,
684 | 53532C371DD50BD200088AAC /* Example */,
685 | 53D3AF251E0710E90099A464 /* ExampleTests */,
686 | );
687 | };
688 | /* End PBXProject section */
689 |
690 | /* Begin PBXResourcesBuildPhase section */
691 | 53532C361DD50BD200088AAC /* Resources */ = {
692 | isa = PBXResourcesBuildPhase;
693 | buildActionMask = 2147483647;
694 | files = (
695 | 53D3AF1B1E07109D0099A464 /* Second.storyboard in Resources */,
696 | 53D3AF151E07109D0099A464 /* Home.storyboard in Resources */,
697 | 53D3AF131E07109D0099A464 /* LaunchScreen.storyboard in Resources */,
698 | );
699 | runOnlyForDeploymentPostprocessing = 0;
700 | };
701 | 53532C4A1DD50BD200088AAC /* Resources */ = {
702 | isa = PBXResourcesBuildPhase;
703 | buildActionMask = 2147483647;
704 | files = (
705 | 5368EDD11E801EDB0028D8CA /* Sample.storyboard in Resources */,
706 | 5372507C1EF1399E007F3A10 /* XibModule.xib in Resources */,
707 | );
708 | runOnlyForDeploymentPostprocessing = 0;
709 | };
710 | 5367C0CD1DD7C68D005B6676 /* Resources */ = {
711 | isa = PBXResourcesBuildPhase;
712 | buildActionMask = 2147483647;
713 | files = (
714 | );
715 | runOnlyForDeploymentPostprocessing = 0;
716 | };
717 | 53D3AF241E0710E90099A464 /* Resources */ = {
718 | isa = PBXResourcesBuildPhase;
719 | buildActionMask = 2147483647;
720 | files = (
721 | );
722 | runOnlyForDeploymentPostprocessing = 0;
723 | };
724 | /* End PBXResourcesBuildPhase section */
725 |
726 | /* Begin PBXSourcesBuildPhase section */
727 | 53532C341DD50BD200088AAC /* Sources */ = {
728 | isa = PBXSourcesBuildPhase;
729 | buildActionMask = 2147483647;
730 | files = (
731 | 5355BECE232FC65600BC7CF9 /* CoolInteractor.swift in Sources */,
732 | 5355BECF232FC65600BC7CF9 /* CoolView.swift in Sources */,
733 | 53077FEB232F8CAB0078D855 /* SimpleView.swift in Sources */,
734 | 5327EADD22526175006D5FCC /* TableOfContentsInteractor.swift in Sources */,
735 | 53D3AF111E07109D0099A464 /* AppDelegate.swift in Sources */,
736 | 53D3AF211E07109D0099A464 /* SecondViewPad.swift in Sources */,
737 | 5327EADB22526175006D5FCC /* TableOfContentsRouter.swift in Sources */,
738 | 5327EADE22526175006D5FCC /* TableOfContentsView.swift in Sources */,
739 | 5327EADA22526175006D5FCC /* TableOfContentsPresenter.swift in Sources */,
740 | 53D3AF1A1E07109D0099A464 /* HomeView.swift in Sources */,
741 | 53D3AF1F1E07109D0099A464 /* SecondRouter.swift in Sources */,
742 | 53077FE7232F8CAB0078D855 /* SimplePresenter.swift in Sources */,
743 | 5327EAD922526175006D5FCC /* TableOfContentsDisplayData.swift in Sources */,
744 | 53077FEA232F8CAB0078D855 /* SimpleInteractor.swift in Sources */,
745 | 53D3AF191E07109D0099A464 /* HomeRouter.swift in Sources */,
746 | 53D3AF121E07109D0099A464 /* AppModule.swift in Sources */,
747 | 5355BED8232FC76900BC7CF9 /* PerfectModuleApi.swift in Sources */,
748 | 53D3AF161E07109D0099A464 /* HomeDisplayData.swift in Sources */,
749 | 5355BED7232FC76900BC7CF9 /* PerfectRouter.swift in Sources */,
750 | 53D3AF201E07109D0099A464 /* SecondView.swift in Sources */,
751 | 5355BED6232FC76900BC7CF9 /* PerfectPresenter.swift in Sources */,
752 | 53D3AF181E07109D0099A464 /* HomePresenter.swift in Sources */,
753 | 5355BECB232FC65600BC7CF9 /* CoolPresenter.swift in Sources */,
754 | 5355BECC232FC65600BC7CF9 /* CoolRouter.swift in Sources */,
755 | 5355BED9232FC76900BC7CF9 /* PerfectInteractor.swift in Sources */,
756 | 53D3AF1C1E07109D0099A464 /* SecondDisplayData.swift in Sources */,
757 | 53077FE8232F8CAB0078D855 /* SimpleRouter.swift in Sources */,
758 | 53D3AF171E07109D0099A464 /* HomeInteractor.swift in Sources */,
759 | 53077FE6232F8CAB0078D855 /* SimpleDisplayData.swift in Sources */,
760 | 5355BECD232FC65600BC7CF9 /* CoolModuleApi.swift in Sources */,
761 | 53D3AF1E1E07109D0099A464 /* SecondPresenter.swift in Sources */,
762 | 5327EADC22526175006D5FCC /* TableOfContentsModuleApi.swift in Sources */,
763 | 5355BEDA232FC76900BC7CF9 /* PerfectView.swift in Sources */,
764 | 53D3AF1D1E07109D0099A464 /* SecondInteractor.swift in Sources */,
765 | 53A86CBF2315D2EE00E6B88C /* UserSettings.swift in Sources */,
766 | 53077FE9232F8CAB0078D855 /* SimpleModuleApi.swift in Sources */,
767 | );
768 | runOnlyForDeploymentPostprocessing = 0;
769 | };
770 | 53532C481DD50BD200088AAC /* Sources */ = {
771 | isa = PBXSourcesBuildPhase;
772 | buildActionMask = 2147483647;
773 | files = (
774 | 5372507A1EF13988007F3A10 /* XibModuleView.swift in Sources */,
775 | 5360ADF31E81280100C1AACB /* PresenterTests.swift in Sources */,
776 | 537250771EF13988007F3A10 /* XibModuleModuleApi.swift in Sources */,
777 | 5372506A1EF13835007F3A10 /* CodeModuleInteractor.swift in Sources */,
778 | 5368EDD61E801EDB0028D8CA /* SampleView.swift in Sources */,
779 | 5368EDD21E801EDB0028D8CA /* SampleDisplayData.swift in Sources */,
780 | 5368EDC91E7FF9A40028D8CA /* TestModules.swift in Sources */,
781 | 5368EDD41E801EDB0028D8CA /* SamplePresenter.swift in Sources */,
782 | 537B7EA8232FE287002681C6 /* SwiftUIWithEnvModuleApi.swift in Sources */,
783 | 5368EDD71E801EDB0028D8CA /* SampleViewPad.swift in Sources */,
784 | 537250691EF13835007F3A10 /* CodeModuleDisplayData.swift in Sources */,
785 | 5368EDD51E801EDB0028D8CA /* SampleRouter.swift in Sources */,
786 | 537250791EF13988007F3A10 /* XibModuleRouter.swift in Sources */,
787 | 537B7EA6232FE287002681C6 /* SwiftUIWithEnvPresenter.swift in Sources */,
788 | 5372506B1EF13835007F3A10 /* CodeModuleModuleApi.swift in Sources */,
789 | 537250751EF13988007F3A10 /* XibModuleDisplayData.swift in Sources */,
790 | 537B7EAA232FE287002681C6 /* SwiftUIWithEnvView.swift in Sources */,
791 | 537B7EA9232FE287002681C6 /* SwiftUIWithEnvInteractor.swift in Sources */,
792 | 537250781EF13988007F3A10 /* XibModulePresenter.swift in Sources */,
793 | 5368EDD31E801EDB0028D8CA /* SampleInteractor.swift in Sources */,
794 | 537B7EA7232FE287002681C6 /* SwiftUIWithEnvRouter.swift in Sources */,
795 | 537250761EF13988007F3A10 /* XibModuleInteractor.swift in Sources */,
796 | 537B7E9B232FDB25002681C6 /* SwiftUIPresenter.swift in Sources */,
797 | 5372507E1EF1503A007F3A10 /* TestCleanModules.swift in Sources */,
798 | 5372506E1EF13835007F3A10 /* CodeModuleView.swift in Sources */,
799 | 537B7E9E232FDB25002681C6 /* SwiftUIInteractor.swift in Sources */,
800 | 539580A6207378C3009A66BC /* ExtensionsTests.swift in Sources */,
801 | 5368EDC71E7FF91F0028D8CA /* RouterTests.swift in Sources */,
802 | 537B7E9C232FDB25002681C6 /* SwiftUIRouter.swift in Sources */,
803 | 537D19E41E7BE9B100A758FF /* ModuleTests.swift in Sources */,
804 | 5372506D1EF13835007F3A10 /* CodeModuleRouter.swift in Sources */,
805 | 537B7E9F232FDB25002681C6 /* SwiftUIView.swift in Sources */,
806 | 5372506C1EF13835007F3A10 /* CodeModulePresenter.swift in Sources */,
807 | 537B7E9D232FDB25002681C6 /* SwiftUIModuleApi.swift in Sources */,
808 | );
809 | runOnlyForDeploymentPostprocessing = 0;
810 | };
811 | 5367C0CA1DD7C68D005B6676 /* Sources */ = {
812 | isa = PBXSourcesBuildPhase;
813 | buildActionMask = 2147483647;
814 | files = (
815 | 5367C0DC1DD7C69E005B6676 /* ViperitError.swift in Sources */,
816 | 53D3AEF61E0709A80099A464 /* String+.swift in Sources */,
817 | 5367C0DD1DD7C69E005B6676 /* ViperComponent.swift in Sources */,
818 | 5367C0DE1DD7C69E005B6676 /* Module.swift in Sources */,
819 | 5367C0DF1DD7C69E005B6676 /* UserInterface.swift in Sources */,
820 | 53023EDC225257E8004ACC5C /* UserInterface+Table.swift in Sources */,
821 | 5367C0E01DD7C69E005B6676 /* DisplayData.swift in Sources */,
822 | 5367C0E11DD7C69E005B6676 /* Interactor.swift in Sources */,
823 | 53023EDE22525814004ACC5C /* UserInterface+Collection.swift in Sources */,
824 | 5367C0E21DD7C69E005B6676 /* Presenter.swift in Sources */,
825 | 53A2851722C22EDF005CC4C8 /* UserInterface+SwiftUI.swift in Sources */,
826 | 5367C0E31DD7C69E005B6676 /* Router.swift in Sources */,
827 | 53023EE02252582C004ACC5C /* UserInterface+Split.swift in Sources */,
828 | 5372505E1EF13500007F3A10 /* ViperitModule.swift in Sources */,
829 | );
830 | runOnlyForDeploymentPostprocessing = 0;
831 | };
832 | 53D3AF221E0710E90099A464 /* Sources */ = {
833 | isa = PBXSourcesBuildPhase;
834 | buildActionMask = 2147483647;
835 | files = (
836 | 53D3AF321E0711000099A464 /* HomeTests.swift in Sources */,
837 | );
838 | runOnlyForDeploymentPostprocessing = 0;
839 | };
840 | /* End PBXSourcesBuildPhase section */
841 |
842 | /* Begin PBXTargetDependency section */
843 | 53388C2C1DD9FFDF00EA4CEC /* PBXTargetDependency */ = {
844 | isa = PBXTargetDependency;
845 | target = 5367C0CE1DD7C68D005B6676 /* Viperit */;
846 | targetProxy = 53388C2B1DD9FFDF00EA4CEC /* PBXContainerItemProxy */;
847 | };
848 | 53D3AF2C1E0710E90099A464 /* PBXTargetDependency */ = {
849 | isa = PBXTargetDependency;
850 | target = 53532C371DD50BD200088AAC /* Example */;
851 | targetProxy = 53D3AF2B1E0710E90099A464 /* PBXContainerItemProxy */;
852 | };
853 | /* End PBXTargetDependency section */
854 |
855 | /* Begin PBXVariantGroup section */
856 | 53D3AEFE1E07109D0099A464 /* LaunchScreen.storyboard */ = {
857 | isa = PBXVariantGroup;
858 | children = (
859 | 53D3AEFF1E07109D0099A464 /* Base */,
860 | );
861 | name = LaunchScreen.storyboard;
862 | sourceTree = "";
863 | };
864 | /* End PBXVariantGroup section */
865 |
866 | /* Begin XCBuildConfiguration section */
867 | 53532C531DD50BD200088AAC /* Debug */ = {
868 | isa = XCBuildConfiguration;
869 | buildSettings = {
870 | ALWAYS_SEARCH_USER_PATHS = NO;
871 | CLANG_ANALYZER_NONNULL = YES;
872 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
873 | CLANG_CXX_LIBRARY = "libc++";
874 | CLANG_ENABLE_MODULES = YES;
875 | CLANG_ENABLE_OBJC_ARC = YES;
876 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
877 | CLANG_WARN_BOOL_CONVERSION = YES;
878 | CLANG_WARN_COMMA = YES;
879 | CLANG_WARN_CONSTANT_CONVERSION = YES;
880 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
881 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
882 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
883 | CLANG_WARN_EMPTY_BODY = YES;
884 | CLANG_WARN_ENUM_CONVERSION = YES;
885 | CLANG_WARN_INFINITE_RECURSION = YES;
886 | CLANG_WARN_INT_CONVERSION = YES;
887 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
888 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
889 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
890 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
891 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
892 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
893 | CLANG_WARN_STRICT_PROTOTYPES = YES;
894 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
895 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
896 | CLANG_WARN_UNREACHABLE_CODE = YES;
897 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
898 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
899 | COPY_PHASE_STRIP = NO;
900 | DEBUG_INFORMATION_FORMAT = dwarf;
901 | ENABLE_STRICT_OBJC_MSGSEND = YES;
902 | ENABLE_TESTABILITY = YES;
903 | GCC_C_LANGUAGE_STANDARD = gnu99;
904 | GCC_DYNAMIC_NO_PIC = NO;
905 | GCC_NO_COMMON_BLOCKS = YES;
906 | GCC_OPTIMIZATION_LEVEL = 0;
907 | GCC_PREPROCESSOR_DEFINITIONS = (
908 | "DEBUG=1",
909 | "$(inherited)",
910 | );
911 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
912 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
913 | GCC_WARN_UNDECLARED_SELECTOR = YES;
914 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
915 | GCC_WARN_UNUSED_FUNCTION = YES;
916 | GCC_WARN_UNUSED_VARIABLE = YES;
917 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
918 | MTL_ENABLE_DEBUG_INFO = YES;
919 | ONLY_ACTIVE_ARCH = YES;
920 | SDKROOT = iphoneos;
921 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
922 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
923 | SWIFT_SWIFT3_OBJC_INFERENCE = Off;
924 | TARGETED_DEVICE_FAMILY = "1,2";
925 | };
926 | name = Debug;
927 | };
928 | 53532C541DD50BD200088AAC /* Release */ = {
929 | isa = XCBuildConfiguration;
930 | buildSettings = {
931 | ALWAYS_SEARCH_USER_PATHS = NO;
932 | CLANG_ANALYZER_NONNULL = YES;
933 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
934 | CLANG_CXX_LIBRARY = "libc++";
935 | CLANG_ENABLE_MODULES = YES;
936 | CLANG_ENABLE_OBJC_ARC = YES;
937 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
938 | CLANG_WARN_BOOL_CONVERSION = YES;
939 | CLANG_WARN_COMMA = YES;
940 | CLANG_WARN_CONSTANT_CONVERSION = YES;
941 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
942 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
943 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
944 | CLANG_WARN_EMPTY_BODY = YES;
945 | CLANG_WARN_ENUM_CONVERSION = YES;
946 | CLANG_WARN_INFINITE_RECURSION = YES;
947 | CLANG_WARN_INT_CONVERSION = YES;
948 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
949 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
950 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
951 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
952 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
953 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
954 | CLANG_WARN_STRICT_PROTOTYPES = YES;
955 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
956 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
957 | CLANG_WARN_UNREACHABLE_CODE = YES;
958 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
959 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
960 | COPY_PHASE_STRIP = NO;
961 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
962 | ENABLE_NS_ASSERTIONS = NO;
963 | ENABLE_STRICT_OBJC_MSGSEND = YES;
964 | GCC_C_LANGUAGE_STANDARD = gnu99;
965 | GCC_NO_COMMON_BLOCKS = YES;
966 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
967 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
968 | GCC_WARN_UNDECLARED_SELECTOR = YES;
969 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
970 | GCC_WARN_UNUSED_FUNCTION = YES;
971 | GCC_WARN_UNUSED_VARIABLE = YES;
972 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
973 | MTL_ENABLE_DEBUG_INFO = NO;
974 | SDKROOT = iphoneos;
975 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
976 | SWIFT_SWIFT3_OBJC_INFERENCE = Off;
977 | TARGETED_DEVICE_FAMILY = "1,2";
978 | VALIDATE_PRODUCT = YES;
979 | };
980 | name = Release;
981 | };
982 | 53532C561DD50BD200088AAC /* Debug */ = {
983 | isa = XCBuildConfiguration;
984 | buildSettings = {
985 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
986 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
987 | CLANG_ENABLE_MODULES = YES;
988 | DEVELOPMENT_TEAM = "";
989 | INFOPLIST_FILE = Example/Info.plist;
990 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
991 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
992 | PRODUCT_BUNDLE_IDENTIFIER = org.acferran.Example;
993 | PRODUCT_NAME = "$(TARGET_NAME)";
994 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
995 | SWIFT_VERSION = 5.0;
996 | };
997 | name = Debug;
998 | };
999 | 53532C571DD50BD200088AAC /* Release */ = {
1000 | isa = XCBuildConfiguration;
1001 | buildSettings = {
1002 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
1003 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
1004 | CLANG_ENABLE_MODULES = YES;
1005 | DEVELOPMENT_TEAM = "";
1006 | INFOPLIST_FILE = Example/Info.plist;
1007 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
1008 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
1009 | PRODUCT_BUNDLE_IDENTIFIER = org.acferran.Example;
1010 | PRODUCT_NAME = "$(TARGET_NAME)";
1011 | SWIFT_VERSION = 5.0;
1012 | };
1013 | name = Release;
1014 | };
1015 | 53532C591DD50BD200088AAC /* Debug */ = {
1016 | isa = XCBuildConfiguration;
1017 | buildSettings = {
1018 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
1019 | CLANG_ENABLE_MODULES = YES;
1020 | DEVELOPMENT_TEAM = "";
1021 | EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = NO;
1022 | INFOPLIST_FILE = ViperitTests/Info.plist;
1023 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
1024 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1025 | PRODUCT_BUNDLE_IDENTIFIER = org.acferran.ViperitTests;
1026 | PRODUCT_NAME = "$(TARGET_NAME)";
1027 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
1028 | SWIFT_VERSION = 5.0;
1029 | };
1030 | name = Debug;
1031 | };
1032 | 53532C5A1DD50BD200088AAC /* Release */ = {
1033 | isa = XCBuildConfiguration;
1034 | buildSettings = {
1035 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
1036 | CLANG_ENABLE_MODULES = YES;
1037 | DEVELOPMENT_TEAM = "";
1038 | EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = NO;
1039 | INFOPLIST_FILE = ViperitTests/Info.plist;
1040 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
1041 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1042 | PRODUCT_BUNDLE_IDENTIFIER = org.acferran.ViperitTests;
1043 | PRODUCT_NAME = "$(TARGET_NAME)";
1044 | SWIFT_VERSION = 5.0;
1045 | };
1046 | name = Release;
1047 | };
1048 | 5367C0DA1DD7C68D005B6676 /* Debug */ = {
1049 | isa = XCBuildConfiguration;
1050 | buildSettings = {
1051 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
1052 | CODE_SIGN_IDENTITY = "";
1053 | CODE_SIGN_STYLE = Manual;
1054 | CURRENT_PROJECT_VERSION = 1;
1055 | DEFINES_MODULE = YES;
1056 | DEVELOPMENT_TEAM = "";
1057 | DYLIB_COMPATIBILITY_VERSION = 1;
1058 | DYLIB_CURRENT_VERSION = 1;
1059 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1060 | INFOPLIST_FILE = Viperit/Info.plist;
1061 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1062 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
1063 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1064 | MARKETING_VERSION = 1.5.0;
1065 | OTHER_LDFLAGS = (
1066 | "-weak_framework",
1067 | SwiftUI,
1068 | );
1069 | PRODUCT_BUNDLE_IDENTIFIER = org.acferran.Viperit;
1070 | PRODUCT_NAME = "$(TARGET_NAME)";
1071 | PROVISIONING_PROFILE_SPECIFIER = "";
1072 | SKIP_INSTALL = NO;
1073 | SWIFT_VERSION = 5.0;
1074 | VERSIONING_SYSTEM = "apple-generic";
1075 | VERSION_INFO_PREFIX = "";
1076 | };
1077 | name = Debug;
1078 | };
1079 | 5367C0DB1DD7C68D005B6676 /* Release */ = {
1080 | isa = XCBuildConfiguration;
1081 | buildSettings = {
1082 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
1083 | CODE_SIGN_IDENTITY = "";
1084 | CODE_SIGN_STYLE = Manual;
1085 | CURRENT_PROJECT_VERSION = 1;
1086 | DEFINES_MODULE = YES;
1087 | DEVELOPMENT_TEAM = "";
1088 | DYLIB_COMPATIBILITY_VERSION = 1;
1089 | DYLIB_CURRENT_VERSION = 1;
1090 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1091 | INFOPLIST_FILE = Viperit/Info.plist;
1092 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1093 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
1094 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1095 | MARKETING_VERSION = 1.5.0;
1096 | OTHER_LDFLAGS = (
1097 | "-weak_framework",
1098 | SwiftUI,
1099 | );
1100 | PRODUCT_BUNDLE_IDENTIFIER = org.acferran.Viperit;
1101 | PRODUCT_NAME = "$(TARGET_NAME)";
1102 | PROVISIONING_PROFILE_SPECIFIER = "";
1103 | SKIP_INSTALL = NO;
1104 | SWIFT_VERSION = 5.0;
1105 | VERSIONING_SYSTEM = "apple-generic";
1106 | VERSION_INFO_PREFIX = "";
1107 | };
1108 | name = Release;
1109 | };
1110 | 53D3AF2E1E0710E90099A464 /* Debug */ = {
1111 | isa = XCBuildConfiguration;
1112 | buildSettings = {
1113 | BUNDLE_LOADER = "$(TEST_HOST)";
1114 | DEVELOPMENT_TEAM = "";
1115 | INFOPLIST_FILE = ExampleTests/Info.plist;
1116 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
1117 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1118 | PRODUCT_BUNDLE_IDENTIFIER = org.acferran.ExampleTests;
1119 | PRODUCT_NAME = "$(TARGET_NAME)";
1120 | SWIFT_VERSION = 5.0;
1121 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example";
1122 | };
1123 | name = Debug;
1124 | };
1125 | 53D3AF2F1E0710E90099A464 /* Release */ = {
1126 | isa = XCBuildConfiguration;
1127 | buildSettings = {
1128 | BUNDLE_LOADER = "$(TEST_HOST)";
1129 | DEVELOPMENT_TEAM = "";
1130 | INFOPLIST_FILE = ExampleTests/Info.plist;
1131 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
1132 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1133 | PRODUCT_BUNDLE_IDENTIFIER = org.acferran.ExampleTests;
1134 | PRODUCT_NAME = "$(TARGET_NAME)";
1135 | SWIFT_VERSION = 5.0;
1136 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example";
1137 | };
1138 | name = Release;
1139 | };
1140 | /* End XCBuildConfiguration section */
1141 |
1142 | /* Begin XCConfigurationList section */
1143 | 53532C331DD50BD200088AAC /* Build configuration list for PBXProject "Viperit" */ = {
1144 | isa = XCConfigurationList;
1145 | buildConfigurations = (
1146 | 53532C531DD50BD200088AAC /* Debug */,
1147 | 53532C541DD50BD200088AAC /* Release */,
1148 | );
1149 | defaultConfigurationIsVisible = 0;
1150 | defaultConfigurationName = Release;
1151 | };
1152 | 53532C551DD50BD200088AAC /* Build configuration list for PBXNativeTarget "Example" */ = {
1153 | isa = XCConfigurationList;
1154 | buildConfigurations = (
1155 | 53532C561DD50BD200088AAC /* Debug */,
1156 | 53532C571DD50BD200088AAC /* Release */,
1157 | );
1158 | defaultConfigurationIsVisible = 0;
1159 | defaultConfigurationName = Release;
1160 | };
1161 | 53532C581DD50BD200088AAC /* Build configuration list for PBXNativeTarget "ViperitTests" */ = {
1162 | isa = XCConfigurationList;
1163 | buildConfigurations = (
1164 | 53532C591DD50BD200088AAC /* Debug */,
1165 | 53532C5A1DD50BD200088AAC /* Release */,
1166 | );
1167 | defaultConfigurationIsVisible = 0;
1168 | defaultConfigurationName = Release;
1169 | };
1170 | 5367C0D91DD7C68D005B6676 /* Build configuration list for PBXNativeTarget "Viperit" */ = {
1171 | isa = XCConfigurationList;
1172 | buildConfigurations = (
1173 | 5367C0DA1DD7C68D005B6676 /* Debug */,
1174 | 5367C0DB1DD7C68D005B6676 /* Release */,
1175 | );
1176 | defaultConfigurationIsVisible = 0;
1177 | defaultConfigurationName = Release;
1178 | };
1179 | 53D3AF2D1E0710E90099A464 /* Build configuration list for PBXNativeTarget "ExampleTests" */ = {
1180 | isa = XCConfigurationList;
1181 | buildConfigurations = (
1182 | 53D3AF2E1E0710E90099A464 /* Debug */,
1183 | 53D3AF2F1E0710E90099A464 /* Release */,
1184 | );
1185 | defaultConfigurationIsVisible = 0;
1186 | defaultConfigurationName = Release;
1187 | };
1188 | /* End XCConfigurationList section */
1189 | };
1190 | rootObject = 53532C301DD50BD200088AAC /* Project object */;
1191 | }
1192 |
--------------------------------------------------------------------------------
/Viperit.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Viperit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Viperit.xcodeproj/xcshareddata/xcschemes/Viperit.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
52 |
53 |
59 |
60 |
66 |
67 |
68 |
69 |
71 |
72 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/Viperit.xcodeproj/xcshareddata/xcschemes/ViperitTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
16 |
18 |
24 |
25 |
26 |
27 |
28 |
38 |
39 |
45 |
46 |
48 |
49 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/Viperit/Core/DisplayData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DisplayData.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 18/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class DisplayData {
12 | required public init() { }
13 | }
14 |
--------------------------------------------------------------------------------
/Viperit/Core/Interactor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Interactor.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 11/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | public protocol InteractorProtocol: ViperitComponent {
10 | var _presenter: PresenterProtocol! { get set }
11 | }
12 |
13 | open class Interactor: InteractorProtocol {
14 | public weak var _presenter: PresenterProtocol!
15 |
16 | required public init() { }
17 | }
18 |
--------------------------------------------------------------------------------
/Viperit/Core/Module.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Module.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 11/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | private let kTabletSuffix = "Pad"
13 |
14 | public protocol ViperitComponent {
15 | init()
16 | }
17 |
18 | //MARK: - Module
19 | public struct Module {
20 | public private(set) var view: UserInterfaceProtocol
21 | public private(set) var interactor: InteractorProtocol
22 | public private(set) var presenter: PresenterProtocol
23 | public private(set) var router: RouterProtocol
24 | public private(set) var displayData: DisplayData?
25 |
26 | static func build(_ module: T, bundle: Bundle = Bundle.main, deviceType: UIUserInterfaceIdiom? = nil) -> Module where T.RawValue == String {
27 | //Get class types
28 | let interactorClass = module.classForViperComponent(.interactor, bundle: bundle) as! InteractorProtocol.Type
29 | let presenterClass = module.classForViperComponent(.presenter, bundle: bundle) as! PresenterProtocol.Type
30 | let routerClass = module.classForViperComponent(.router, bundle: bundle) as! RouterProtocol.Type
31 | let displayDataClass = module.classForViperComponent(.displayData, bundle: bundle) as? DisplayData.Type
32 |
33 | //Allocate VIPER components
34 | let V = loadView(forModule: module, bundle: bundle, deviceType: deviceType)
35 | let I = interactorClass.init()
36 | let P = presenterClass.init()
37 | let R = routerClass.init()
38 | let D = displayDataClass?.init()
39 |
40 | return build(view: V, interactor: I, presenter: P, router: R, displayData: D)
41 | }
42 | }
43 |
44 | //MARK: - Inject Mock Components for Testing
45 | public extension Module {
46 |
47 | mutating func injectMock(view mockView: UserInterfaceProtocol) {
48 | view = mockView
49 | view._presenter = presenter
50 | view._displayData = displayData
51 | presenter._view = view
52 | }
53 |
54 | mutating func injectMock(interactor mockInteractor: InteractorProtocol) {
55 | interactor = mockInteractor
56 | interactor._presenter = presenter
57 | presenter._interactor = interactor
58 | }
59 |
60 | mutating func injectMock(presenter mockPresenter: PresenterProtocol) {
61 | presenter = mockPresenter
62 | presenter._view = view
63 | presenter._interactor = interactor
64 | presenter._router = router
65 | view._presenter = presenter
66 | interactor._presenter = presenter
67 | router._presenter = presenter
68 | }
69 |
70 | mutating func injectMock(router mockRouter: RouterProtocol) {
71 | router = mockRouter
72 | router._presenter = presenter
73 | presenter._router = router
74 | }
75 | }
76 |
77 |
78 | //MARK: - Helper Methods
79 | extension Module {
80 |
81 | static func loadView(forModule module: T, bundle: Bundle, deviceType: UIUserInterfaceIdiom? = nil) -> UserInterfaceProtocol where T.RawValue == String {
82 |
83 | let viewClass = module.classForViperComponent(.view, bundle: bundle, deviceType: deviceType) as! UIViewController.Type
84 | let viewIdentifier = safeString(NSStringFromClass(viewClass).components(separatedBy: ".").last)
85 | let viewName = module.viewName.uppercasedFirst
86 |
87 | switch module.viewType {
88 | case .nib:
89 | return viewClass.init(nibName: viewName, bundle: bundle) as! UserInterfaceProtocol
90 | case .code:
91 | return viewClass.init() as! UserInterfaceProtocol
92 | default:
93 | let sb = UIStoryboard(name: viewName, bundle: bundle)
94 | return sb.instantiateViewController(withIdentifier: viewIdentifier) as! UserInterfaceProtocol
95 | }
96 | }
97 |
98 | static func build(view: UserInterfaceProtocol, interactor: InteractorProtocol, presenter: PresenterProtocol, router: RouterProtocol, displayData: DisplayData?) -> Module {
99 | //View connections
100 | view._presenter = presenter
101 | view._displayData = displayData
102 |
103 | //Interactor connections
104 | var interactor = interactor
105 | interactor._presenter = presenter
106 |
107 | //Presenter connections
108 | presenter._router = router
109 | presenter._interactor = interactor
110 | presenter._view = view
111 |
112 | //Router connections
113 | var router = router
114 | router._presenter = presenter
115 |
116 | return Module(view: view, interactor: interactor, presenter: presenter, router: router, displayData: displayData)
117 | }
118 | }
119 |
120 |
121 | //MARK: - Private Extension for Application Module generic enum
122 | extension RawRepresentable where RawValue == String {
123 |
124 | func classForViperComponent(_ component: ViperComponent, bundle: Bundle, deviceType: UIUserInterfaceIdiom? = nil) -> Swift.AnyClass? {
125 | let className = rawValue.uppercasedFirst + component.rawValue.uppercasedFirst
126 | let bundleName = safeString(bundle.infoDictionary?["CFBundleName"])
127 | let classInBundle = (bundleName + "." + className).replacingOccurrences(of: "[ -]", with: "_", options: .regularExpression)
128 |
129 | if component == .view {
130 | let deviceType = deviceType ?? UIScreen.main.traitCollection.userInterfaceIdiom
131 | let isPad = deviceType == .pad
132 | if isPad, let tabletView = NSClassFromString(classInBundle + kTabletSuffix) {
133 | return tabletView
134 | }
135 | }
136 |
137 | return NSClassFromString(classInBundle)
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/Viperit/Core/Presenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Presenter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 11/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | public protocol PresenterProtocol: AnyObject, ViperitComponent {
10 | var _interactor: InteractorProtocol! { get set }
11 | var _view: UserInterfaceProtocol! { get set }
12 | var _router: RouterProtocol! { get set }
13 |
14 | func setupView(data: Any)
15 | func viewHasLoaded()
16 | func viewIsAboutToAppear()
17 | func viewHasAppeared()
18 | func viewIsAboutToDisappear()
19 | func viewHasDisappeared()
20 | }
21 |
22 | open class Presenter: PresenterProtocol {
23 | public var _interactor: InteractorProtocol!
24 | public weak var _view: UserInterfaceProtocol!
25 | public var _router: RouterProtocol!
26 |
27 | required public init() { }
28 |
29 | open func setupView(data: Any) {
30 | print(ViperitError.methodNotImplemented.description)
31 | }
32 |
33 | open func viewHasLoaded() {}
34 | open func viewIsAboutToAppear() {}
35 | open func viewHasAppeared() {}
36 | open func viewIsAboutToDisappear() {}
37 | open func viewHasDisappeared() {}
38 | }
39 |
--------------------------------------------------------------------------------
/Viperit/Core/Router.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Router.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 11/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public protocol RouterProtocol: ViperitComponent {
12 | var _presenter: PresenterProtocol! { get set }
13 | var _view: UserInterfaceProtocol! { get }
14 |
15 | func embedInNavigationController() -> UINavigationController
16 | func show(inWindow window: UIWindow?, embedInNavController: Bool, setupData: Any?, makeKeyAndVisible: Bool)
17 | func show(from: UIViewController, embedInNavController: Bool, setupData: Any?)
18 | func show(from containerView: UIViewController, insideView targetView: UIView, setupData: Any?)
19 | func present(from: UIViewController, embedInNavController: Bool, presentationStyle: UIModalPresentationStyle, transitionStyle: UIModalTransitionStyle, setupData: Any?, completion: (() -> Void)?)
20 | func dismiss(animated flag: Bool, completion: (() -> Void)?)
21 | }
22 |
23 | public extension RouterProtocol {
24 | var viewController: UIViewController {
25 | return _view as! UIViewController
26 | }
27 | }
28 |
29 | open class Router: RouterProtocol {
30 | public weak var _presenter: PresenterProtocol!
31 | public var _view: UserInterfaceProtocol! {
32 | return _presenter._view
33 | }
34 |
35 | open func embedInNavigationController() -> UINavigationController {
36 | return getNavigationController() ?? UINavigationController(rootViewController: viewController)
37 | }
38 |
39 | open func show(inWindow window: UIWindow?, embedInNavController: Bool = false, setupData: Any? = nil, makeKeyAndVisible: Bool = true) {
40 | process(setupData: setupData)
41 | let view = embedInNavController ? embedInNavigationController() : viewController
42 | window?.rootViewController = view
43 | if makeKeyAndVisible {
44 | window?.makeKeyAndVisible()
45 | }
46 | }
47 |
48 | open func show(from: UIViewController, embedInNavController: Bool = false, setupData: Any? = nil) {
49 | process(setupData: setupData)
50 | let view: UIViewController = embedInNavController ? embedInNavigationController() : viewController
51 | from.show(view, sender: nil)
52 | }
53 |
54 | public func show(from containerView: UIViewController, insideView targetView: UIView, setupData: Any? = nil) {
55 | process(setupData: setupData)
56 | addAsChildView(ofView: containerView, insideContainer: targetView)
57 | }
58 |
59 | public func present(from: UIViewController, embedInNavController: Bool = false, presentationStyle: UIModalPresentationStyle = .fullScreen, transitionStyle: UIModalTransitionStyle = .coverVertical, setupData: Any? = nil, completion: (() -> Void)? = nil) {
60 | let view: UIViewController = embedInNavController ? embedInNavigationController() : viewController
61 | view.modalTransitionStyle = transitionStyle
62 | view.modalPresentationStyle = presentationStyle
63 |
64 | process(setupData: setupData)
65 | from.present(view, animated: true, completion: completion)
66 | }
67 |
68 | public func dismiss(animated flag: Bool = true, completion: (() -> Void)? = nil) {
69 | viewController.dismiss(animated: flag, completion: completion)
70 | }
71 |
72 | required public init() { }
73 | }
74 |
75 | //MARK: - Process possible setup data
76 | private extension Router {
77 | func process(setupData: Any?) {
78 | if let data = setupData {
79 | _presenter.setupView(data: data)
80 | }
81 | }
82 | }
83 |
84 | //MARK: - Get navigation controller helper
85 | public extension RouterProtocol {
86 | func getNavigationController() -> UINavigationController? {
87 | guard let view = _view as? UIViewController else { return nil }
88 | if let nav = view.navigationController {
89 | return nav
90 | } else if let parent = view.parent {
91 | if let parentNav = parent.navigationController {
92 | return parentNav
93 | }
94 | }
95 | return nil
96 | }
97 | }
98 |
99 | //MARK: - Embed view in a container view
100 | public extension RouterProtocol {
101 | func addAsChildView(ofView parentView: UIViewController, insideContainer containerView: UIView) {
102 | guard let view = _view as? UIViewController else { return }
103 | parentView.addChild(view)
104 | containerView.addSubview(view.view)
105 | stretchToBounds(containerView, view: view.view)
106 | view.didMove(toParent: parentView)
107 | }
108 |
109 | private func stretchToBounds(_ holderView: UIView, view: UIView) {
110 | view.translatesAutoresizingMaskIntoConstraints = false
111 | let pinDirections: [NSLayoutConstraint.Attribute] = [.top, .bottom, .left, .right]
112 | let pinConstraints = pinDirections.map { direction -> NSLayoutConstraint in
113 | return NSLayoutConstraint(item: view, attribute: direction, relatedBy: .equal,
114 | toItem: holderView, attribute: direction, multiplier: 1.0, constant: 0)
115 | }
116 | holderView.addConstraints(pinConstraints)
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Viperit/Core/UserInterface.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserInterface.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 11/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public protocol UserInterfaceProtocol: AnyObject {
12 | var _presenter: PresenterProtocol! { get set }
13 | var _displayData: DisplayData? { get set }
14 | var viewController: UIViewController { get }
15 | func viewDidLoad()
16 | func viewWillAppear(_ animated: Bool)
17 | func viewDidAppear(_ animated: Bool)
18 | func viewWillDisappear(_ animated: Bool)
19 | func viewDidDisappear(_ animated: Bool)
20 | }
21 |
22 | public extension UserInterfaceProtocol {
23 | var viewController: UIViewController {
24 | return self as! UIViewController
25 | }
26 | }
27 |
28 | //MARK: - Default implementation for UIViewController
29 | open class UserInterface: UIViewController, UserInterfaceProtocol {
30 | public var _presenter: PresenterProtocol!
31 | public var _displayData: DisplayData?
32 |
33 | open override func viewDidLoad() {
34 | super.viewDidLoad()
35 | _presenter.viewHasLoaded()
36 | }
37 |
38 | open override func viewWillAppear(_ animated: Bool) {
39 | super.viewWillAppear(animated)
40 | _presenter.viewIsAboutToAppear()
41 | }
42 |
43 | open override func viewDidAppear(_ animated: Bool) {
44 | super.viewDidAppear(animated)
45 | _presenter.viewHasAppeared()
46 | }
47 |
48 | open override func viewWillDisappear(_ animated: Bool) {
49 | super.viewWillDisappear(animated)
50 | _presenter.viewIsAboutToDisappear()
51 | }
52 |
53 | open override func viewDidDisappear(_ animated: Bool) {
54 | super.viewDidDisappear(animated)
55 | _presenter.viewHasDisappeared()
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Viperit/Core/ViperComponent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViperComponents.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 11/09/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | internal enum ViperComponent: String {
12 | case view
13 | case interactor
14 | case presenter
15 | case router
16 | case displayData
17 | }
18 |
--------------------------------------------------------------------------------
/Viperit/Core/ViperitError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViperitError.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 29/10/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum ViperitError : Error {
12 | case methodNotImplemented
13 |
14 | var description: String {
15 | var message = ""
16 | switch self {
17 | case .methodNotImplemented: message = "Method not implemented"
18 | }
19 | return "[VIPERIT WARNING]: \(message)"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Viperit/Core/ViperitModule.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViperitModule.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | // Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | #if canImport(SwiftUI)
12 | import SwiftUI
13 | #endif
14 |
15 |
16 | //MARK: - Module View Types
17 | public enum ViperitViewType {
18 | case swiftUI
19 | case storyboard
20 | case nib
21 | case code
22 | }
23 |
24 | //MARK: - Viperit Module Protocol
25 | public protocol ViperitModule {
26 | var viewType: ViperitViewType { get }
27 | var viewName: String { get }
28 | func build(bundle: Bundle, deviceType: UIUserInterfaceIdiom?) -> Module
29 |
30 | @available(iOS 13.0, *)
31 | func build(bundle: Bundle, deviceType: UIUserInterfaceIdiom?, setupUI: ((PresenterProtocol) -> (T, E))) -> Module
32 |
33 | @available(iOS 13.0, *)
34 | func build(bundle: Bundle, deviceType: UIUserInterfaceIdiom?, setupUI: ((PresenterProtocol) -> T)) -> Module
35 | }
36 |
37 | public extension ViperitModule where Self: RawRepresentable, Self.RawValue == String {
38 | var viewType: ViperitViewType {
39 | return .storyboard
40 | }
41 |
42 | var viewName: String {
43 | return rawValue
44 | }
45 |
46 | func build(bundle: Bundle = Bundle.main, deviceType: UIUserInterfaceIdiom? = nil) -> Module {
47 | return Module.build(self, bundle: bundle, deviceType: deviceType)
48 | }
49 |
50 | @available(iOS 13.0, *)
51 | func build(bundle: Bundle = Bundle.main, deviceType: UIUserInterfaceIdiom? = nil, setupUI: ((PresenterProtocol) -> (T, E))) -> Module {
52 | let components = allocateViperComponents(bundle: bundle)
53 | let set = setupUI(components.presenter)
54 | let viewUI = set.0
55 |
56 | let viewHost: UserInterfaceProtocol
57 | let envObject = set.1
58 | viewHost = HostingUserInterface(rootView: viewUI.environmentObject(envObject))
59 |
60 | return Module.build(view: viewHost, interactor: components.interactor,
61 | presenter: components.presenter, router: components.router, displayData: components.displayData)
62 | }
63 |
64 |
65 | @available(iOS 13.0, *)
66 | func build(bundle: Bundle = Bundle.main, deviceType: UIUserInterfaceIdiom? = nil, setupUI: ((PresenterProtocol) -> T)) -> Module {
67 | let components = allocateViperComponents(bundle: bundle)
68 | let viewUI = setupUI(components.presenter)
69 | let viewHost: UserInterfaceProtocol
70 | viewHost = HostingUserInterface(rootView: viewUI)
71 |
72 | return Module.build(view: viewHost, interactor: components.interactor,
73 | presenter: components.presenter, router: components.router, displayData: components.displayData)
74 | }
75 | }
76 |
77 | private extension ViperitModule where Self: RawRepresentable, Self.RawValue == String {
78 | func allocateViperComponents(bundle: Bundle) -> (interactor: InteractorProtocol, presenter: PresenterProtocol, router: RouterProtocol, displayData: DisplayData?) {
79 | //Get class types
80 | let interactorClass = classForViperComponent(.interactor, bundle: bundle) as! InteractorProtocol.Type
81 | let presenterClass = classForViperComponent(.presenter, bundle: bundle) as! PresenterProtocol.Type
82 | let routerClass = classForViperComponent(.router, bundle: bundle) as! RouterProtocol.Type
83 | let displayDataClass = classForViperComponent(.displayData, bundle: bundle) as? DisplayData.Type
84 |
85 | //Allocate VIPER components
86 | let interactor = interactorClass.init()
87 | let presenter = presenterClass.init()
88 | let router = routerClass.init()
89 | let displayData = displayDataClass?.init()
90 |
91 | return (interactor, presenter, router, displayData)
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Viperit/Extensions/String+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/11/2016.
6 | // Copyright © 2016 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension String {
12 | var first: String {
13 | return String(prefix(1))
14 | }
15 | var uppercasedFirst: String {
16 | return first.uppercased() + String(dropFirst())
17 | }
18 | }
19 |
20 | func safeString(_ value: Any?) -> String {
21 | guard let text = value else { return "" }
22 | return String(describing: text)
23 | }
24 |
--------------------------------------------------------------------------------
/Viperit/Extensions/UserInterface+Collection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserInterface+Collection.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 01/04/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | //MARK: - Default implementation for UICollectionViewController
12 | open class CollectionUserInterface: UICollectionViewController, UserInterfaceProtocol {
13 | public var _presenter: PresenterProtocol!
14 | public var _displayData: DisplayData?
15 |
16 | open override func viewDidLoad() {
17 | super.viewDidLoad()
18 | _presenter.viewHasLoaded()
19 | }
20 |
21 | open override func viewWillAppear(_ animated: Bool) {
22 | super.viewWillAppear(animated)
23 | _presenter.viewIsAboutToAppear()
24 | }
25 |
26 | open override func viewDidAppear(_ animated: Bool) {
27 | super.viewDidAppear(animated)
28 | _presenter.viewHasAppeared()
29 | }
30 |
31 | open override func viewWillDisappear(_ animated: Bool) {
32 | super.viewWillDisappear(animated)
33 | _presenter.viewIsAboutToDisappear()
34 | }
35 |
36 | open override func viewDidDisappear(_ animated: Bool) {
37 | super.viewDidDisappear(animated)
38 | _presenter.viewHasDisappeared()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Viperit/Extensions/UserInterface+Split.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserInterface+Split.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 01/04/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | //MARK: - Default implementation for UISplitViewController
12 | open class SplitUserInterface: UISplitViewController, UserInterfaceProtocol {
13 | public var _presenter: PresenterProtocol!
14 | public var _displayData: DisplayData?
15 |
16 | open override func viewDidLoad() {
17 | super.viewDidLoad()
18 | _presenter.viewHasLoaded()
19 | }
20 |
21 | open override func viewWillAppear(_ animated: Bool) {
22 | super.viewWillAppear(animated)
23 | _presenter.viewIsAboutToAppear()
24 | }
25 |
26 | open override func viewDidAppear(_ animated: Bool) {
27 | super.viewDidAppear(animated)
28 | _presenter.viewHasAppeared()
29 | }
30 |
31 | open override func viewWillDisappear(_ animated: Bool) {
32 | super.viewWillDisappear(animated)
33 | _presenter.viewIsAboutToDisappear()
34 | }
35 |
36 | open override func viewDidDisappear(_ animated: Bool) {
37 | super.viewDidDisappear(animated)
38 | _presenter.viewHasDisappeared()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Viperit/Extensions/UserInterface+SwiftUI.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserInterface+SwiftUI.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 25/06/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | #if canImport(SwiftUI)
10 | import SwiftUI
11 | #endif
12 |
13 | @available(iOS 13.0, *)
14 | public class HostingUserInterface: UIHostingController, UserInterfaceProtocol {
15 | public var _presenter: PresenterProtocol!
16 | public var _displayData: DisplayData?
17 |
18 |
19 | public override required init(rootView: Content) {
20 | super.init(rootView: rootView)
21 | }
22 |
23 | @objc required dynamic init?(coder aDecoder: NSCoder) {
24 | fatalError("init(coder:) has not been implemented")
25 | }
26 |
27 | open override func viewDidLoad() {
28 | super.viewDidLoad()
29 | _presenter.viewHasLoaded()
30 | }
31 |
32 | open override func viewWillAppear(_ animated: Bool) {
33 | super.viewWillAppear(animated)
34 | _presenter.viewIsAboutToAppear()
35 | }
36 |
37 | open override func viewDidAppear(_ animated: Bool) {
38 | super.viewDidAppear(animated)
39 | _presenter.viewHasAppeared()
40 | }
41 |
42 | open override func viewWillDisappear(_ animated: Bool) {
43 | super.viewWillDisappear(animated)
44 | _presenter.viewIsAboutToDisappear()
45 | }
46 |
47 | open override func viewDidDisappear(_ animated: Bool) {
48 | super.viewDidDisappear(animated)
49 | _presenter.viewHasDisappeared()
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Viperit/Extensions/UserInterface+Table.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserInterface+Table.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 01/04/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | //MARK: - Default implementation for UITableViewController
12 | open class TableUserInterface: UITableViewController, UserInterfaceProtocol {
13 | public var _presenter: PresenterProtocol!
14 | public var _displayData: DisplayData?
15 |
16 | open override func viewDidLoad() {
17 | super.viewDidLoad()
18 | _presenter.viewHasLoaded()
19 | }
20 |
21 | open override func viewWillAppear(_ animated: Bool) {
22 | super.viewWillAppear(animated)
23 | _presenter.viewIsAboutToAppear()
24 | }
25 |
26 | open override func viewDidAppear(_ animated: Bool) {
27 | super.viewDidAppear(animated)
28 | _presenter.viewHasAppeared()
29 | }
30 |
31 | open override func viewWillDisappear(_ animated: Bool) {
32 | super.viewWillDisappear(animated)
33 | _presenter.viewIsAboutToDisappear()
34 | }
35 |
36 | open override func viewDidDisappear(_ animated: Bool) {
37 | super.viewDidDisappear(animated)
38 | _presenter.viewHasDisappeared()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Viperit/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIRequiredDeviceCapabilities
26 |
27 | armv7
28 |
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/ViperitTests/ExtensionsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExtensionsTests.swift
3 | // ViperitTests
4 | //
5 | // Created by Ferran on 03/04/2018.
6 | // Copyright © 2018 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Viperit
11 |
12 | class ExtensionsTests: XCTestCase {
13 | func testSafeNilString() {
14 | let optionalObject: String? = nil
15 | let nonOptionalString = safeString(optionalObject)
16 | XCTAssertEqual(nonOptionalString, "")
17 | }
18 |
19 | func testSafeFilledString() {
20 | let optionalObject: String? = "Hey there"
21 | let nonOptionalString = safeString(optionalObject)
22 | XCTAssertEqual(nonOptionalString, "Hey there")
23 | }
24 |
25 | func testUppercasedFirst() {
26 | let noUpperCased = "hey there"
27 | let uppercasedFirst = noUpperCased.uppercasedFirst
28 | XCTAssertEqual(uppercasedFirst, "Hey there")
29 | }
30 |
31 | func testAlreadyUppercasedFirst() {
32 | let alreadyUppercasedFirst = "Hey there"
33 | let uppercasedFirst = alreadyUppercasedFirst.uppercasedFirst
34 | XCTAssertEqual(uppercasedFirst, "Hey there")
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ViperitTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ViperitTests/ModuleTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ModuleTests.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 17/03/2017.
6 | // Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import Viperit
11 |
12 | private class MockView: UserInterface {}
13 | private class MockPresenter: Presenter {}
14 | private class MockInteractor: Interactor {}
15 | private class MockRouter: Router {}
16 |
17 | class ModuleTests: XCTestCase {
18 | private lazy var testBundle = Bundle(for: SampleRouter.self)
19 |
20 | private func createTestModule(module: TestModules = .sample, forTablet: Bool = false) -> Module {
21 | let deviceType: UIUserInterfaceIdiom = forTablet ? .pad : .phone
22 | return module.build(bundle: testBundle, deviceType: deviceType)
23 | }
24 |
25 | func testModuleBuilderPerformance() {
26 | self.measure {
27 | _ = self.createTestModule()
28 | }
29 | }
30 |
31 | func testModuleBuildCorrectComponents() {
32 | let module = createTestModule()
33 |
34 | XCTAssert(module.view is SampleView)
35 | XCTAssert(module.interactor is SampleInteractor)
36 | XCTAssert(module.presenter is SamplePresenter)
37 | XCTAssert(module.router is SampleRouter)
38 | XCTAssert(module.displayData is SampleDisplayData)
39 | }
40 |
41 | func testModuleBuildForTabletComponents() {
42 | let module = createTestModule(forTablet: true)
43 |
44 | XCTAssert(module.view is SampleViewPad)
45 | XCTAssert(module.interactor is SampleInteractor)
46 | XCTAssert(module.presenter is SamplePresenter)
47 | XCTAssert(module.router is SampleRouter)
48 | XCTAssert(module.displayData is SampleDisplayData)
49 | }
50 |
51 | func testModuleDepencies() {
52 | let module = createTestModule()
53 |
54 | //Assert view dependencies
55 | let v = module.view
56 | let i = module.interactor
57 | let p = module.presenter
58 | let r = module.router
59 |
60 | XCTAssert(v._presenter is SamplePresenter)
61 | XCTAssert(v._displayData is SampleDisplayData)
62 | XCTAssert(i._presenter is SamplePresenter)
63 | XCTAssert(p._view is SampleView)
64 | XCTAssert(p._router is SampleRouter)
65 | XCTAssert(r._presenter is SamplePresenter)
66 | XCTAssert(r._view is SampleView)
67 | }
68 |
69 | func testMockViewInjection() {
70 | var module = createTestModule()
71 | let mockView = MockView()
72 | module.injectMock(view: mockView)
73 |
74 | //Assert new injected dependencies
75 | let v = module.view
76 | let p = module.presenter
77 | let r = module.router
78 |
79 | XCTAssert(v is MockView)
80 | XCTAssert(v._presenter is SamplePresenter)
81 | XCTAssert(v._displayData is SampleDisplayData)
82 | XCTAssert(p._view is MockView)
83 | XCTAssert(r._view is MockView)
84 | }
85 |
86 | func testMockPresenterInjection() {
87 | var module = createTestModule()
88 | let mockPresenter = MockPresenter()
89 | module.injectMock(presenter: mockPresenter)
90 |
91 | //Assert new injected dependencies
92 | let v = module.view
93 | let i = module.interactor
94 | let p = module.presenter
95 | let r = module.router
96 |
97 | XCTAssert(p is MockPresenter)
98 | XCTAssert(p._view is SampleView)
99 | XCTAssert(p._interactor is SampleInteractor)
100 | XCTAssert(p._router is SampleRouter)
101 | XCTAssert(r._presenter is MockPresenter)
102 | XCTAssert(v._presenter is MockPresenter)
103 | XCTAssert(i._presenter is MockPresenter)
104 | }
105 |
106 | func testMockInteractorInjection() {
107 | var module = createTestModule()
108 | let mockInteractor = MockInteractor()
109 | module.injectMock(interactor: mockInteractor)
110 |
111 | //Assert new injected dependencies
112 | let i = module.interactor
113 | let p = module.presenter
114 |
115 | XCTAssert(i is MockInteractor)
116 | XCTAssert(i._presenter is SamplePresenter)
117 | XCTAssert(p._interactor is MockInteractor)
118 | }
119 |
120 | func testMockRouterInjection() {
121 | var module = createTestModule()
122 | let mockRouter = MockRouter()
123 | module.injectMock(router: mockRouter)
124 |
125 | //Assert new injected dependencies
126 | let p = module.presenter
127 | let r = module.router
128 |
129 | XCTAssert(r is MockRouter)
130 | XCTAssert(r._presenter is SamplePresenter)
131 | XCTAssert(p._router is MockRouter)
132 | }
133 |
134 | func testModuleFromNib() {
135 | let module = createTestModule(module: .xibModule)
136 | XCTAssert(module.view is XibModuleView)
137 | }
138 |
139 | func testModuleByCode() {
140 | let module = createTestModule(module: .codeModule)
141 | XCTAssert(module.view is CodeModuleView)
142 | }
143 |
144 | func testModuleWithoutOverridingProperties() {
145 | let module = TestCleanModules.sample.build(bundle: testBundle, deviceType: .phone)
146 | XCTAssert(module.view is SampleView)
147 | }
148 | }
149 |
150 | //MARK: - SwiftUI tests
151 | @available(iOS 13.0, *)
152 | extension ModuleTests {
153 | private func createSwiftUIModule() -> Module {
154 | return TestModules.swiftUI.build(bundle: testBundle, deviceType: nil) { presenter in
155 | SwiftUIView(presenter: presenter as? SwiftUIPresenterApi)
156 | }
157 | }
158 |
159 | private func createSwiftUIModuleWithEnvironmentObject() -> Module {
160 | return TestModules.swiftUIWithEnv.build(bundle: testBundle, deviceType: nil) { presenter -> (SwiftUIWithEnvView, Settings) in
161 | let view = SwiftUIWithEnvView(presenter: presenter as? SwiftUIWithEnvPresenterApi)
162 | let settings = Settings()
163 | return (view, settings)
164 | }
165 | }
166 |
167 | func testSwiftUIModuleBuilderPerformance() {
168 | self.measure {
169 | _ = self.createSwiftUIModule()
170 | }
171 | }
172 |
173 | func testSwiftUIWithEnvironmentObjectModuleBuilderPerformance() {
174 | self.measure {
175 | _ = self.createSwiftUIModuleWithEnvironmentObject()
176 | }
177 | }
178 |
179 | func testSwiftUIModuleBuildCorrectComponents() {
180 | let module = createSwiftUIModule()
181 |
182 | XCTAssert(module.view is HostingUserInterface)
183 | XCTAssert(module.interactor is SwiftUIInteractor)
184 | XCTAssert(module.presenter is SwiftUIPresenter)
185 | XCTAssert(module.router is SwiftUIRouter)
186 | XCTAssertNil(module.displayData)
187 | }
188 |
189 | func testSwiftUIWithEnvironmentObjectModuleBuildCorrectComponents() {
190 | let module = createSwiftUIModuleWithEnvironmentObject()
191 |
192 | XCTAssert(module.interactor is SwiftUIWithEnvInteractor)
193 | XCTAssert(module.presenter is SwiftUIWithEnvPresenter)
194 | XCTAssert(module.router is SwiftUIWithEnvRouter)
195 | XCTAssertNil(module.displayData)
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/ViperitTests/PresenterTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PresenterTests.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 21/03/2017.
6 | // Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import Viperit
11 |
12 | //MARK: - Timeout
13 | private let kTimeout: TimeInterval = 0.5
14 |
15 | //MARK: - View Lifecycle
16 | private let kViewHasLoaded = "viewHasLoaded"
17 | private let kViewIsAboutToAppear = "viewIsAboutToAppear"
18 | private let kViewHasAppeared = "viewHasAppeared"
19 | private let kViewIsAboutToDisappear = "viewIsAboutToDisappear"
20 | private let kViewHasDisappeared = "viewHasDisappeared"
21 |
22 | //MARK: - Setup methods
23 | private let kSetupView = "setupView"
24 |
25 | //MARK: - Mock Presenter
26 | private class MockPresenter: Presenter, SamplePresenterInterface {
27 | var expectation: XCTestExpectation!
28 | var methodExpected: String!
29 |
30 | private func assert(method: String) {
31 | if methodExpected == method {
32 | XCTAssert(true)
33 | expectation.fulfill()
34 | }
35 | }
36 |
37 | override func viewHasLoaded() {
38 | super.viewHasLoaded()
39 | assert(method: kViewHasLoaded)
40 | }
41 |
42 | override func viewIsAboutToAppear() {
43 | super.viewIsAboutToAppear()
44 | assert(method: kViewIsAboutToAppear)
45 | }
46 |
47 | override func viewHasAppeared() {
48 | super.viewHasAppeared()
49 | assert(method: kViewHasAppeared)
50 | }
51 |
52 | override func viewIsAboutToDisappear() {
53 | super.viewIsAboutToDisappear()
54 | assert(method: kViewIsAboutToDisappear)
55 | }
56 |
57 | override func viewHasDisappeared() {
58 | super.viewHasDisappeared()
59 | assert(method: kViewHasDisappeared)
60 | }
61 |
62 | override func setupView(data: Any) {
63 | super.setupView(data: data)
64 | assert(method: kSetupView)
65 | }
66 | }
67 |
68 | //MARK: - Presenter Tests
69 | class PresenterTests: XCTestCase {
70 | private func createTestModuleWithMockPresenter(methodToTest: String) -> Module {
71 | var module = TestModules.sample.build(bundle: Bundle(for: SampleRouter.self))
72 | let mockPresenter = MockPresenter()
73 | mockPresenter.expectation = expectation(description: "Expecting method: \(methodToTest)")
74 | mockPresenter.methodExpected = methodToTest
75 | module.injectMock(presenter: mockPresenter)
76 | return module
77 | }
78 |
79 | private func expectViewLifecycle(method: String) {
80 | let module = createTestModuleWithMockPresenter(methodToTest: method)
81 |
82 | //Simulate view lifecycle
83 | _ = module.view.viewController.view
84 | module.view.viewWillAppear(false)
85 | module.view.viewDidAppear(false)
86 | module.view.viewWillDisappear(false)
87 | module.view.viewDidDisappear(false)
88 |
89 | expect(timeout: kTimeout, errorMessage: "\(method) timed out (> \(kTimeout)s")
90 | }
91 |
92 |
93 | func testViewHasLoaded() {
94 | expectViewLifecycle(method: kViewHasLoaded)
95 | }
96 |
97 | func testViewIsAboutToAppear() {
98 | expectViewLifecycle(method: kViewIsAboutToAppear)
99 | }
100 |
101 | func testViewHasAppeared() {
102 | expectViewLifecycle(method: kViewHasAppeared)
103 | }
104 |
105 | func testSetupView() {
106 | let mockView = UIViewController()
107 | _ = mockView.view
108 | let module = createTestModuleWithMockPresenter(methodToTest: kSetupView)
109 | let router = module.router as! SampleRouter
110 |
111 | //Simulate show module with router function using setup data
112 | router.show(from: mockView, setupData: "randomData")
113 | expect(timeout: kTimeout, errorMessage: "\(kSetupView) timed out (> \(kTimeout)s")
114 | }
115 |
116 |
117 | func expect(timeout: TimeInterval, errorMessage: String) {
118 | waitForExpectations(timeout: timeout) { error in
119 | guard error != nil else { return }
120 | XCTFail(errorMessage)
121 | }
122 | }
123 |
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/ViperitTests/RouterTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RouterTests.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 20/03/2017.
6 | // Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import Viperit
11 |
12 | class RouterTests: XCTestCase {
13 | private func createTestModule(forTablet: Bool = false) -> Module {
14 | let deviceType: UIUserInterfaceIdiom = forTablet ? .pad : .phone
15 | return TestModules.sample.build(bundle: Bundle(for: SampleRouter.self), deviceType: deviceType)
16 | }
17 |
18 | func testEmbedInNavigationController() {
19 | let module = createTestModule()
20 | let router = module.router as! SampleRouter
21 | let navigationController = router.embedInNavigationController()
22 |
23 | //Check that there is one view in the stack and that is a SampleView
24 | assert(navigationController.viewControllers.count == 1)
25 | assert(navigationController.viewControllers[0] is SampleView)
26 | }
27 |
28 | func testEmbedInNavigationControllerWhenAlreadyExists() {
29 | let module = createTestModule()
30 | let router = module.router as! SampleRouter
31 | let navController = UINavigationController(rootViewController: UIViewController())
32 | navController.pushViewController(module.view.viewController, animated: false)
33 | let navigationController = router.embedInNavigationController()
34 |
35 | //Check that there is two views in the stack and that the second one is a SampleView
36 | assert(navigationController.viewControllers.count == 2)
37 | assert(navigationController.viewControllers[1] is SampleView)
38 | }
39 |
40 | func testShowInWindow() {
41 | let window = UIWindow()
42 | let module = createTestModule()
43 | module.router.show(inWindow: window, embedInNavController: false, setupData: nil, makeKeyAndVisible: false)
44 | XCTAssert(window.rootViewController is SampleView)
45 | }
46 |
47 | func testShowInWindowEmbeddedInNavigationController() {
48 | let window = UIWindow()
49 | let module = createTestModule()
50 | module.router.show(inWindow: window, embedInNavController: true, setupData: nil, makeKeyAndVisible: false)
51 |
52 | guard let rootNav = window.rootViewController as? UINavigationController,
53 | let _ = rootNav.viewControllers[0] as? SampleView else {
54 | XCTAssert(false)
55 | return
56 | }
57 | XCTAssert(true)
58 | }
59 |
60 | func testShowEmbeddedInNavigationController() {
61 | let mockView = UIViewController()
62 | let module = createTestModule()
63 | let router = module.router as! SampleRouter
64 |
65 | router.show(from: mockView, embedInNavController: true)
66 | let navigationController = module.view.viewController.navigationController
67 |
68 | //Check that there is one view in the stack and that is a SampleView
69 | assert(navigationController != nil)
70 | assert(navigationController?.viewControllers.count == 1)
71 | assert(navigationController?.viewControllers[0] is SampleView)
72 | }
73 |
74 | func testShowInsideView() {
75 | let mockView = UIViewController()
76 | let targetView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 10))
77 | mockView.view.addSubview(targetView)
78 |
79 | let module = createTestModule()
80 | let router = module.router as! SampleRouter
81 | module.view.viewController.view.tag = 150
82 | router.show(from: mockView, insideView: targetView)
83 |
84 | guard targetView.subviews.count == 1 else {
85 | XCTAssert(false)
86 | return
87 | }
88 |
89 | let extractedView = targetView.subviews[0]
90 | XCTAssert(extractedView.tag == 150)
91 | }
92 |
93 | func testPresentDefault() {
94 | let window = UIWindow()
95 | let mockRootModule = createTestModule()
96 | let rootRouter = mockRootModule.router as! SampleRouter
97 | let presentedModule = createTestModule()
98 | let presentedRouter = presentedModule.router as! SampleRouter
99 |
100 |
101 | //Setup window
102 | rootRouter.show(inWindow: window, embedInNavController: true, makeKeyAndVisible: false)
103 |
104 | //Setup presented view to check values afterwards
105 | let presentedTitle: String? = "MODALLY PRESENTED MODULE"
106 | presentedModule.view.viewController.title = presentedTitle
107 | presentedModule.view.viewController.view?.backgroundColor = .red
108 |
109 | presentedRouter.present(from: mockRootModule.view.viewController) {
110 | guard let topController = self.getTopViewController(window: window) else {
111 | XCTAssert(false, "No top ViewController found")
112 | return
113 | }
114 |
115 | XCTAssert(topController.view?.backgroundColor == .red)
116 | XCTAssertEqual(topController.title, presentedTitle)
117 | XCTAssertEqual(topController, presentedModule.view.viewController)
118 | }
119 | }
120 |
121 | func testPresentEmbeddedInNavController() {
122 | let window = UIWindow()
123 | let mockRootModule = createTestModule()
124 | let rootRouter = mockRootModule.router as! SampleRouter
125 | let presentedModule = createTestModule()
126 | let presentedRouter = presentedModule.router as! SampleRouter
127 |
128 | //Setup window
129 | rootRouter.show(inWindow: window, embedInNavController: true, setupData: nil, makeKeyAndVisible: false)
130 |
131 | presentedRouter.present(from: mockRootModule.view.viewController) {
132 | //Setup presented module view to check values afterwards
133 | let presentedTitle: String? = "MODALLY PRESENTED MODULE EMBEDDED IN NAVIGATION CONTROLLER"
134 | presentedModule.view.viewController.title = presentedTitle
135 | presentedModule.view.viewController.view?.backgroundColor = .blue
136 | presentedModule.view.viewController.navigationItem.title = presentedTitle
137 |
138 | guard let topNavController = self.getTopViewController(window: window) as? UINavigationController else {
139 | XCTAssert(false, "No top navigation controller found")
140 | return
141 | }
142 |
143 | let presentedRootController = topNavController.topViewController
144 | XCTAssertNotNil(presentedRootController, "No root view controller found")
145 | XCTAssert(presentedRootController?.view?.backgroundColor == .blue)
146 | XCTAssertEqual(presentedRootController?.title, presentedTitle)
147 | XCTAssertEqual(presentedRootController?.navigationItem.title, presentedTitle)
148 | XCTAssertEqual(topNavController.viewControllers.count, 1)
149 | XCTAssertEqual(presentedRootController, presentedModule.view.viewController)
150 | }
151 | }
152 |
153 | func testDismissModal() {
154 | let window = UIWindow()
155 | let mockRootModule = createTestModule()
156 | let rootRouter = mockRootModule.router as! SampleRouter
157 | let presentedModule = createTestModule()
158 | let presentedRouter = presentedModule.router as! SampleRouter
159 |
160 | //Setup window
161 | rootRouter.show(inWindow: window, embedInNavController: false, setupData: nil, makeKeyAndVisible: false)
162 |
163 | presentedRouter.present(from: mockRootModule.view.viewController) {
164 | XCTAssertEqual(mockRootModule.view.viewController.presentedViewController, presentedModule.view.viewController)
165 | }
166 |
167 | presentedRouter.dismiss(animated: false, completion: {
168 | XCTAssertNil(mockRootModule.view.viewController.presentedViewController)
169 | })
170 | }
171 |
172 | func testRouterViewControllerProperty() {
173 | let module = createTestModule()
174 | let viewControllerFromView = module.view.viewController
175 | let viewControllerFromRouter = module.router.viewController
176 | XCTAssert(viewControllerFromView is SampleView)
177 | XCTAssert(viewControllerFromRouter is SampleView)
178 | XCTAssertEqual(viewControllerFromView, viewControllerFromRouter)
179 | }
180 | }
181 |
182 | //MARK: - Helpers
183 | private extension RouterTests {
184 | func getTopViewController(window: UIWindow) -> UIViewController? {
185 | if var topController = window.rootViewController {
186 | while let presentedViewController = topController.presentedViewController {
187 | topController = presentedViewController
188 | }
189 |
190 | return topController
191 | }
192 | return nil
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/CodeModule/CodeModuleDisplayData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CodeModuleDisplayData.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | //Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - CodeModuleDisplayData class
13 | final class CodeModuleDisplayData: DisplayData {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/CodeModule/CodeModuleInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CodeModuleInteractor.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | //Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - CodeModuleInteractor Class
13 | final class CodeModuleInteractor: Interactor {
14 | }
15 |
16 | // MARK: - CodeModuleInteractor API
17 | extension CodeModuleInteractor: CodeModuleInteractorApi {
18 | }
19 |
20 | // MARK: - Interactor Viper Components Api
21 | private extension CodeModuleInteractor {
22 | var presenter: CodeModulePresenterApi {
23 | return _presenter as! CodeModulePresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/CodeModule/CodeModuleModuleApi.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CodeModuleModuleApi.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | //Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Viperit
10 |
11 | //MARK: - CodeModuleRouter API
12 | protocol CodeModuleRouterApi: RouterProtocol {
13 | }
14 |
15 | //MARK: - CodeModuleView API
16 | protocol CodeModuleViewApi: UserInterfaceProtocol {
17 | }
18 |
19 | //MARK: - CodeModulePresenter API
20 | protocol CodeModulePresenterApi: PresenterProtocol {
21 | }
22 |
23 | //MARK: - CodeModuleInteractor API
24 | protocol CodeModuleInteractorApi: InteractorProtocol {
25 | }
26 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/CodeModule/CodeModulePresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CodeModulePresenter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | //Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - CodeModulePresenter Class
13 | final class CodeModulePresenter: Presenter {
14 | }
15 |
16 | // MARK: - CodeModulePresenter API
17 | extension CodeModulePresenter: CodeModulePresenterApi {
18 | }
19 |
20 | // MARK: - CodeModule Viper Components
21 | private extension CodeModulePresenter {
22 | var view: CodeModuleViewApi {
23 | return _view as! CodeModuleViewApi
24 | }
25 | var interactor: CodeModuleInteractorApi {
26 | return _interactor as! CodeModuleInteractorApi
27 | }
28 | var router: CodeModuleRouterApi {
29 | return _router as! CodeModuleRouterApi
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/CodeModule/CodeModuleRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CodeModuleRouter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | //Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - CodeModuleRouter class
13 | final class CodeModuleRouter: Router {
14 | }
15 |
16 | // MARK: - CodeModuleRouter API
17 | extension CodeModuleRouter: CodeModuleRouterApi {
18 | }
19 |
20 | // MARK: - CodeModule Viper Components
21 | private extension CodeModuleRouter {
22 | var presenter: CodeModulePresenterApi {
23 | return _presenter as! CodeModulePresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/CodeModule/CodeModuleView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CodeModuleView.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | //Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Viperit
11 |
12 | //MARK: CodeModuleView Class
13 | final class CodeModuleView: UserInterface {
14 | }
15 |
16 | //MARK: - CodeModuleView API
17 | extension CodeModuleView: CodeModuleViewApi {
18 | }
19 |
20 | // MARK: - CodeModuleView Viper Components API
21 | private extension CodeModuleView {
22 | var presenter: CodeModulePresenterApi {
23 | return _presenter as! CodeModulePresenterApi
24 | }
25 | var displayData: CodeModuleDisplayData {
26 | return _displayData as! CodeModuleDisplayData
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/Sample/Sample.storyboard:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/Sample/SampleDisplayData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SampleDisplayData.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 17/03/2017.
6 | // Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | final class SampleDisplayData: DisplayData {
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/Sample/SampleInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SampleInteractor.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 17/03/2017.
6 | // Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | final class SampleInteractor: Interactor {
13 | }
14 |
15 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
16 | private extension SampleInteractor {
17 | var presenter: SamplePresenter {
18 | return _presenter as! SamplePresenter
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/Sample/SamplePresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SamplePresenter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 17/03/2017.
6 | // Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | //Public API for Presenter (these methods will be visible from the View)
13 | protocol SamplePresenterInterface {
14 | }
15 |
16 | final class SamplePresenter: Presenter {
17 | }
18 |
19 | extension SamplePresenter: SamplePresenterInterface {
20 |
21 | }
22 |
23 |
24 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
25 | private extension SamplePresenter {
26 | var view: SampleViewInterface {
27 | return _view as! SampleViewInterface
28 | }
29 | var interactor: SampleInteractor {
30 | return _interactor as! SampleInteractor
31 | }
32 | var router: SampleRouter {
33 | return _router as! SampleRouter
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/Sample/SampleRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SampleRouter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 17/03/2017.
6 | // Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | final class SampleRouter: Router {
13 | }
14 |
15 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
16 | private extension SampleRouter {
17 | var presenter: SamplePresenter {
18 | return _presenter as! SamplePresenter
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/Sample/SampleView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SampleView.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 17/03/2017.
6 | // Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Viperit
11 |
12 | //MARK: - Public Interface Protocol
13 | protocol SampleViewInterface {
14 | }
15 |
16 | //MARK: Sample View
17 | final class SampleView: UserInterface {
18 | }
19 |
20 | //MARK: - Public interface
21 | extension SampleView: SampleViewInterface {
22 | }
23 |
24 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
25 | private extension SampleView {
26 | var presenter: SamplePresenterInterface {
27 | return _presenter as! SamplePresenterInterface
28 | }
29 | var displayData: SampleDisplayData {
30 | return _displayData as! SampleDisplayData
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/Sample/SampleViewPad.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SampleViewPad.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 20/03/2017.
6 | // Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Viperit
11 |
12 | //MARK: Sample View for Tablet
13 | final class SampleViewPad: UserInterface {
14 | }
15 |
16 | //MARK: - Public interface implementation
17 | extension SampleViewPad: SampleViewInterface {
18 | }
19 |
20 | // MARK: - VIPER COMPONENTS API (Auto-generated code)
21 | private extension SampleViewPad {
22 | var presenter: SamplePresenter {
23 | return _presenter as! SamplePresenter
24 | }
25 | var displayData: SampleDisplayData {
26 | return _displayData as! SampleDisplayData
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/SwiftUI/SwiftUIInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIInteractor.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - SwiftUIInteractor Class
13 | final class SwiftUIInteractor: Interactor {
14 | }
15 |
16 | // MARK: - SwiftUIInteractor API
17 | extension SwiftUIInteractor: SwiftUIInteractorApi {
18 | }
19 |
20 | // MARK: - Interactor Viper Components Api
21 | private extension SwiftUIInteractor {
22 | var presenter: SwiftUIPresenterApi {
23 | return _presenter as! SwiftUIPresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/SwiftUI/SwiftUIModuleApi.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIModuleApi.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Viperit
10 |
11 | //MARK: - SwiftUIRouter API
12 | protocol SwiftUIRouterApi: RouterProtocol {
13 | }
14 |
15 | //MARK: - SwiftUIPresenter API
16 | protocol SwiftUIPresenterApi: PresenterProtocol {
17 | }
18 |
19 | //MARK: - SwiftUIInteractor API
20 | protocol SwiftUIInteractorApi: InteractorProtocol {
21 | }
22 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/SwiftUI/SwiftUIPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIPresenter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - SwiftUIPresenter Class
13 | final class SwiftUIPresenter: Presenter {
14 | }
15 |
16 | // MARK: - SwiftUIPresenter API
17 | extension SwiftUIPresenter: SwiftUIPresenterApi {
18 | }
19 |
20 | // MARK: - SwiftUI Viper Components
21 | private extension SwiftUIPresenter {
22 | var interactor: SwiftUIInteractorApi {
23 | return _interactor as! SwiftUIInteractorApi
24 | }
25 | var router: SwiftUIRouterApi {
26 | return _router as! SwiftUIRouterApi
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/SwiftUI/SwiftUIRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIRouter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - SwiftUIRouter class
13 | final class SwiftUIRouter: Router {
14 | }
15 |
16 | // MARK: - SwiftUIRouter API
17 | extension SwiftUIRouter: SwiftUIRouterApi {
18 | }
19 |
20 | // MARK: - SwiftUI Viper Components
21 | private extension SwiftUIRouter {
22 | var presenter: SwiftUIPresenterApi {
23 | return _presenter as! SwiftUIPresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/SwiftUI/SwiftUIView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIView.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import Viperit
11 |
12 | //MARK: SwiftUIView SwiftUI
13 | struct SwiftUIView : View {
14 | weak var presenter: SwiftUIPresenterApi?
15 |
16 | var body: some View {
17 | Text("SwiftUI SwiftUI View")
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/SwiftUIWithEnv/SwiftUIWithEnvInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIWithEnvInteractor.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - SwiftUIWithEnvInteractor Class
13 | final class SwiftUIWithEnvInteractor: Interactor {
14 | }
15 |
16 | // MARK: - SwiftUIWithEnvInteractor API
17 | extension SwiftUIWithEnvInteractor: SwiftUIWithEnvInteractorApi {
18 | }
19 |
20 | // MARK: - Interactor Viper Components Api
21 | private extension SwiftUIWithEnvInteractor {
22 | var presenter: SwiftUIWithEnvPresenterApi {
23 | return _presenter as! SwiftUIWithEnvPresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/SwiftUIWithEnv/SwiftUIWithEnvModuleApi.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIWithEnvModuleApi.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Viperit
10 |
11 | //MARK: - SwiftUIWithEnvRouter API
12 | protocol SwiftUIWithEnvRouterApi: RouterProtocol {
13 | }
14 |
15 | //MARK: - SwiftUIWithEnvPresenter API
16 | protocol SwiftUIWithEnvPresenterApi: PresenterProtocol {
17 | }
18 |
19 | //MARK: - SwiftUIWithEnvInteractor API
20 | protocol SwiftUIWithEnvInteractorApi: InteractorProtocol {
21 | }
22 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/SwiftUIWithEnv/SwiftUIWithEnvPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIWithEnvPresenter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - SwiftUIWithEnvPresenter Class
13 | final class SwiftUIWithEnvPresenter: Presenter {
14 | }
15 |
16 | // MARK: - SwiftUIWithEnvPresenter API
17 | extension SwiftUIWithEnvPresenter: SwiftUIWithEnvPresenterApi {
18 | }
19 |
20 | // MARK: - SwiftUIWithEnv Viper Components
21 | private extension SwiftUIWithEnvPresenter {
22 | var interactor: SwiftUIWithEnvInteractorApi {
23 | return _interactor as! SwiftUIWithEnvInteractorApi
24 | }
25 | var router: SwiftUIWithEnvRouterApi {
26 | return _router as! SwiftUIWithEnvRouterApi
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/SwiftUIWithEnv/SwiftUIWithEnvRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIWithEnvRouter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - SwiftUIWithEnvRouter class
13 | final class SwiftUIWithEnvRouter: Router {
14 | }
15 |
16 | // MARK: - SwiftUIWithEnvRouter API
17 | extension SwiftUIWithEnvRouter: SwiftUIWithEnvRouterApi {
18 | }
19 |
20 | // MARK: - SwiftUIWithEnv Viper Components
21 | private extension SwiftUIWithEnvRouter {
22 | var presenter: SwiftUIWithEnvPresenterApi {
23 | return _presenter as! SwiftUIWithEnvPresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/SwiftUIWithEnv/SwiftUIWithEnvView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIWithEnvView.swift
3 | // Viperit
4 | //
5 | // Created by Ferran on 16/09/2019.
6 | // Copyright © 2019 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import Viperit
11 |
12 | class Settings: ObservableObject {
13 | @Published var text = "hi"
14 | }
15 |
16 | //MARK: SwiftUIWithEnvView SwiftUI
17 | struct SwiftUIWithEnvView : View {
18 | weak var presenter: SwiftUIWithEnvPresenterApi?
19 | @EnvironmentObject var settings: Settings
20 |
21 | var body: some View {
22 | Text(settings.text)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/XibModule/XibModule.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/XibModule/XibModuleDisplayData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // XibModuleDisplayData.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | //Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - XibModuleDisplayData class
13 | final class XibModuleDisplayData: DisplayData {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/XibModule/XibModuleInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // XibModuleInteractor.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | //Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - XibModuleInteractor Class
13 | final class XibModuleInteractor: Interactor {
14 | }
15 |
16 | // MARK: - XibModuleInteractor API
17 | extension XibModuleInteractor: XibModuleInteractorApi {
18 | }
19 |
20 | // MARK: - Interactor Viper Components Api
21 | private extension XibModuleInteractor {
22 | var presenter: XibModulePresenterApi {
23 | return _presenter as! XibModulePresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/XibModule/XibModuleModuleApi.swift:
--------------------------------------------------------------------------------
1 | //
2 | // XibModuleModuleApi.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | //Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Viperit
10 |
11 | //MARK: - XibModuleRouter API
12 | protocol XibModuleRouterApi: RouterProtocol {
13 | }
14 |
15 | //MARK: - XibModuleView API
16 | protocol XibModuleViewApi: UserInterfaceProtocol {
17 | }
18 |
19 | //MARK: - XibModulePresenter API
20 | protocol XibModulePresenterApi: PresenterProtocol {
21 | }
22 |
23 | //MARK: - XibModuleInteractor API
24 | protocol XibModuleInteractorApi: InteractorProtocol {
25 | }
26 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/XibModule/XibModulePresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // XibModulePresenter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | //Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - XibModulePresenter Class
13 | final class XibModulePresenter: Presenter {
14 | }
15 |
16 | // MARK: - XibModulePresenter API
17 | extension XibModulePresenter: XibModulePresenterApi {
18 | }
19 |
20 | // MARK: - XibModule Viper Components
21 | private extension XibModulePresenter {
22 | var view: XibModuleViewApi {
23 | return _view as! XibModuleViewApi
24 | }
25 | var interactor: XibModuleInteractorApi {
26 | return _interactor as! XibModuleInteractorApi
27 | }
28 | var router: XibModuleRouterApi {
29 | return _router as! XibModuleRouterApi
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/XibModule/XibModuleRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // XibModuleRouter.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | //Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Viperit
11 |
12 | // MARK: - XibModuleRouter class
13 | final class XibModuleRouter: Router {
14 | }
15 |
16 | // MARK: - XibModuleRouter API
17 | extension XibModuleRouter: XibModuleRouterApi {
18 | }
19 |
20 | // MARK: - XibModule Viper Components
21 | private extension XibModuleRouter {
22 | var presenter: XibModulePresenterApi {
23 | return _presenter as! XibModulePresenterApi
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/Modules/XibModule/XibModuleView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // XibModuleView.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | //Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Viperit
11 |
12 | //MARK: XibModuleView Class
13 | final class XibModuleView: UserInterface {
14 | }
15 |
16 | //MARK: - XibModuleView API
17 | extension XibModuleView: XibModuleViewApi {
18 | }
19 |
20 | // MARK: - XibModuleView Viper Components API
21 | private extension XibModuleView {
22 | var presenter: XibModulePresenterApi {
23 | return _presenter as! XibModulePresenterApi
24 | }
25 | var displayData: XibModuleDisplayData {
26 | return _displayData as! XibModuleDisplayData
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/TestCleanModules.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestCleanModules.swift
3 | // ViperitTests
4 | //
5 | // Created by Ferran Abelló on 14/06/2017.
6 | // Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Viperit
10 |
11 | enum TestCleanModules: String, ViperitModule {
12 | case sample
13 | }
14 |
--------------------------------------------------------------------------------
/ViperitTests/Sample/TestModules.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestModules.swift
3 | // Viperit
4 | //
5 | // Created by Ferran Abelló on 20/03/2017.
6 | // Copyright © 2017 Ferran Abelló. All rights reserved.
7 | //
8 |
9 | import Viperit
10 |
11 | enum TestModules: String, ViperitModule {
12 | case sample
13 | case codeModule
14 | case xibModule
15 | case swiftUI
16 | case swiftUIWithEnv
17 |
18 | var viewType: ViperitViewType {
19 | switch self {
20 | case .swiftUI, .swiftUIWithEnv: return .swiftUI
21 | case .codeModule: return .code
22 | case .xibModule: return .nib
23 | default: return .storyboard
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | ignore:
2 | - Viperit/Core/ViperitError.swift
3 | - Viperit/Extensions/*
4 | - ViperitTests/*
5 | - ViperitTests/**/*
6 |
--------------------------------------------------------------------------------