├── .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 | ![Viperit](/Assets/logo_light.png) 2 | 3 | [![Language](https://img.shields.io/badge/swift-5.1-green.svg)](https://swift.org) 4 | [![Build Status](https://travis-ci.org/ferranabello/Viperit.svg?branch=master)](https://travis-ci.org/ferranabello/Viperit) 5 | [![Platform](http://img.shields.io/badge/platform-ios-blue.svg)](https://developer.apple.com/iphone/index.action) 6 | [![License](http://img.shields.io/badge/license-MIT-orange.svg)](http://mit-license.org) 7 | [![Codecov](https://img.shields.io/codecov/c/github/ferranabello/Viperit.svg)](https://codecov.io/gh/ferranabello/Viperit) 8 | [![codebeat badge](https://codebeat.co/badges/17d36823-4e6c-4b45-bad3-746611689636)](https://codebeat.co/projects/github-com-ferranabello-viperit-master) 9 | [![CocoaPods](https://img.shields.io/cocoapods/v/Viperit.svg)](http://github.com/ferranabello/Viperit) 10 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 11 | [![Accio](https://img.shields.io/badge/Accio-supported-0A7CF5.svg?style=flat)](https://github.com/JamitLabs/Accio) 12 | [![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) 13 | [![SwiftUI compatible](https://img.shields.io/badge/SwiftUI-compatible-green.svg)](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 | ![Module Creation](/Assets/Instructions/module_creation.gif) 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 | --------------------------------------------------------------------------------