├── image.png ├── image2.png ├── MVP Kit.xctemplate ├── TemplateIcon.icns ├── Default │ ├── ___FILEBASENAME___.swift │ ├── ___FILEBASENAME___Protocols.swift │ ├── ___FILEBASENAME___Presenter.swift │ ├── ___FILEBASENAME___ViewController.swift │ └── ___FILEBASENAME___ViewController.xib ├── InputProtocol │ ├── ___FILEBASENAME___.swift │ ├── ___FILEBASENAME___Protocols.swift │ ├── ___FILEBASENAME___Presenter.swift │ ├── ___FILEBASENAME___ViewController.swift │ └── ___FILEBASENAME___ViewController.xib └── TemplateInfo.plist ├── MVVM Kit.xctemplate ├── TemplateIcon.icns ├── Default │ ├── ___FILEBASENAME___.swift │ ├── ___FILEBASENAME___ViewModel.swift │ ├── ___FILEBASENAME___ViewController.swift │ ├── Observer.swift │ └── ___FILEBASENAME___ViewController.xib ├── withoutObserver │ ├── ___FILEBASENAME___.swift │ ├── ___FILEBASENAME___ViewModel.swift │ ├── ___FILEBASENAME___ViewController.swift │ └── ___FILEBASENAME___ViewController.xib └── TemplateInfo.plist ├── VIPER Kit.xctemplate ├── TemplateIcon.icns ├── Default │ ├── ___FILEBASENAME___.swift │ ├── ___FILEBASENAME___ViewController.swift │ ├── ___FILEBASENAME___Interactor.swift │ ├── ___FILEBASENAME___Router.swift │ ├── ___FILEBASENAME___Presenter.swift │ └── ___FILEBASENAME___ViewController.xib └── TemplateInfo.plist ├── .gitignore ├── LICENSE ├── README.md └── install.swift /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fauzisho/Architecture-Kit-Swift/HEAD/image.png -------------------------------------------------------------------------------- /image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fauzisho/Architecture-Kit-Swift/HEAD/image2.png -------------------------------------------------------------------------------- /MVP Kit.xctemplate/TemplateIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fauzisho/Architecture-Kit-Swift/HEAD/MVP Kit.xctemplate/TemplateIcon.icns -------------------------------------------------------------------------------- /MVVM Kit.xctemplate/TemplateIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fauzisho/Architecture-Kit-Swift/HEAD/MVVM Kit.xctemplate/TemplateIcon.icns -------------------------------------------------------------------------------- /VIPER Kit.xctemplate/TemplateIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fauzisho/Architecture-Kit-Swift/HEAD/VIPER Kit.xctemplate/TemplateIcon.icns -------------------------------------------------------------------------------- /MVP Kit.xctemplate/Default/___FILEBASENAME___.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: Model - 6 | 7 | import UIKit 8 | 9 | class ___VARIABLE_productName:identifier___{ 10 | 11 | init() { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /VIPER Kit.xctemplate/Default/___FILEBASENAME___.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: Entity - 6 | 7 | import UIKit 8 | 9 | class ___VARIABLE_productName:identifier___{ 10 | 11 | init() { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MVP Kit.xctemplate/InputProtocol/___FILEBASENAME___.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: Model - 6 | 7 | import UIKit 8 | 9 | class ___VARIABLE_productName:identifier___{ 10 | 11 | init() { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MVVM Kit.xctemplate/Default/___FILEBASENAME___.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: Model - 6 | 7 | import UIKit 8 | 9 | class ___VARIABLE_productName:identifier___{ 10 | 11 | init() { 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /MVVM Kit.xctemplate/withoutObserver/___FILEBASENAME___.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: Model - 6 | 7 | import UIKit 8 | 9 | class ___VARIABLE_productName:identifier___{ 10 | 11 | init() { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MVP Kit.xctemplate/Default/___FILEBASENAME___Protocols.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: Protocol - 6 | 7 | import Foundation 8 | 9 | 10 | //Component: View - 11 | protocol ___VARIABLE_productName:identifier___ViewProtocol: class { 12 | } 13 | -------------------------------------------------------------------------------- /MVP Kit.xctemplate/Default/___FILEBASENAME___Presenter.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: Presenter - 6 | 7 | import UIKit 8 | 9 | class ___VARIABLE_productName:identifier___Presenter{ 10 | 11 | weak private var view: ___VARIABLE_productName:identifier___ViewProtocol? 12 | 13 | init(interface: ___VARIABLE_productName:identifier___ViewProtocol) { 14 | self.view = interface 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /VIPER Kit.xctemplate/Default/___FILEBASENAME___ViewController.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: View - 6 | 7 | import UIKit 8 | 9 | class ___VARIABLE_productName:identifier___ViewController: UIViewController, ___VARIABLE_productName:identifier___ViewProtocol { 10 | 11 | var presenter: ___VARIABLE_productName:identifier___PresenterProtocol? 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /MVP Kit.xctemplate/Default/___FILEBASENAME___ViewController.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: View - 6 | 7 | import UIKit 8 | 9 | class ___VARIABLE_productName:identifier___ViewController: UIViewController, ___VARIABLE_productName:identifier___ViewProtocol { 10 | 11 | var presenter: ___VARIABLE_productName:identifier___Presenter? 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | presenter = ___VARIABLE_productName:identifier___Presenter(interface: self) 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /MVVM Kit.xctemplate/Default/___FILEBASENAME___ViewModel.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: ViewModel - 6 | 7 | import UIKit 8 | 9 | class ___VARIABLE_productName:identifier___ViewModel{ 10 | 11 | public var isError = Observer(value: "") 12 | public var isLoading = Observer(value: false) 13 | 14 | init() { 15 | } 16 | 17 | func test(){ 18 | self.isLoading.value = true 19 | self.isError.value = "Something Wrong" 20 | self.isLoading.value = false 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /MVVM Kit.xctemplate/withoutObserver/___FILEBASENAME___ViewModel.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: ViewModel - 6 | 7 | import UIKit 8 | 9 | class ___VARIABLE_productName:identifier___ViewModel{ 10 | 11 | public var isError = Observer(value: "") 12 | public var isLoading = Observer(value: false) 13 | 14 | init() { 15 | } 16 | 17 | func test(){ 18 | self.isLoading.value = true 19 | self.isError.value = "Something Wrong" 20 | self.isLoading.value = false 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /MVP Kit.xctemplate/InputProtocol/___FILEBASENAME___Protocols.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: Protocol - 6 | 7 | import Foundation 8 | 9 | //Component: Presenter - 10 | protocol ___VARIABLE_productName:identifier___PresenterProtocol: class { 11 | func test() 12 | } 13 | 14 | //Component: View - 15 | protocol ___VARIABLE_productName:identifier___ViewProtocol: class { 16 | var presenter: ___VARIABLE_productName:identifier___PresenterProtocol? { get set } 17 | func isLoading(loading : Bool) 18 | func isError(message : String) 19 | } 20 | -------------------------------------------------------------------------------- /MVP Kit.xctemplate/InputProtocol/___FILEBASENAME___Presenter.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: Presenter - 6 | 7 | 8 | import UIKit 9 | 10 | class ___VARIABLE_productName:identifier___Presenter: ___VARIABLE_productName:identifier___PresenterProtocol { 11 | 12 | weak private var view: ___VARIABLE_productName:identifier___ViewProtocol? 13 | 14 | init(interface: ___VARIABLE_productName:identifier___ViewProtocol) { 15 | self.view = interface 16 | } 17 | 18 | func test() { 19 | self.view?.isLoading(loading: true) 20 | self.view?.isError(message: "message") 21 | self.view?.isLoading(loading: false) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /MVVM Kit.xctemplate/Default/___FILEBASENAME___ViewController.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: View - 6 | 7 | import UIKit 8 | 9 | class ___VARIABLE_productName:identifier___ViewController: UIViewController{ 10 | 11 | var viewModel: ___VARIABLE_productName:identifier___ViewModel? 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | viewModel = ___VARIABLE_productName:identifier___ViewModel() 16 | 17 | viewModel?.isError.addObserver({ (error) in 18 | // display error 19 | }) 20 | 21 | viewModel?.isLoading.addObserver({ (loading) in 22 | // display loading 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /MVVM Kit.xctemplate/withoutObserver/___FILEBASENAME___ViewController.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: View - 6 | 7 | import UIKit 8 | 9 | class ___VARIABLE_productName:identifier___ViewController: UIViewController{ 10 | 11 | var viewModel: ___VARIABLE_productName:identifier___ViewModel? 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | viewModel = ___VARIABLE_productName:identifier___ViewModel() 16 | 17 | viewModel?.isError.addObserver({ (error) in 18 | // display error 19 | }) 20 | 21 | viewModel?.isLoading.addObserver({ (loading) in 22 | // display loading 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | DerivedData/ 3 | build/ 4 | *.pbxuser 5 | *.mode1v3 6 | *.mode2v3 7 | *.perspectivev3 8 | xcuserdata/ 9 | *.xcscmblueprint 10 | *.xccheckout 11 | *.moved-aside 12 | *.xcuserstate 13 | 14 | # Swift Package Manager 15 | /.swiftpm/ 16 | /Packages 17 | Package.resolved 18 | 19 | # CocoaPods 20 | Pods/ 21 | Podfile.lock 22 | 23 | # Carthage 24 | Carthage/Build/ 25 | 26 | # Fastlane 27 | fastlane/report.xml 28 | fastlane/Preview.html 29 | fastlane/screenshots 30 | fastlane/test_output 31 | 32 | # Archives 33 | *.xcarchive 34 | 35 | # macOS 36 | .DS_Store 37 | 38 | # Playgrounds 39 | timeline.xctimeline 40 | playground.xcworkspace 41 | 42 | # Other 43 | *.log 44 | *.tmp 45 | *.swp 46 | *.bak 47 | *.orig 48 | 49 | # Environment files 50 | .env 51 | 52 | # Xcode generated docs 53 | *.docset 54 | 55 | # AppCenter 56 | appcenter-pre-build.sh 57 | appcenter-post -------------------------------------------------------------------------------- /MVVM Kit.xctemplate/Default/Observer.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // 6 | 7 | import Foundation 8 | 9 | open class Observer { 10 | open var value: T { 11 | didSet { 12 | DispatchQueue.main.async { 13 | self.valueChanged?(self.value) 14 | } 15 | } 16 | } 17 | 18 | private var valueChanged: ((T) -> Void)? 19 | 20 | init(value: T) { 21 | self.value = value 22 | } 23 | 24 | open func addObserver(enable: Bool = true, _ onChange: ((T) -> Void)?) { 25 | valueChanged = onChange 26 | if enable { 27 | onChange?(value) 28 | } 29 | } 30 | 31 | func removeObserver() { 32 | valueChanged = nil 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /VIPER Kit.xctemplate/Default/___FILEBASENAME___Interactor.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: Interactor - 6 | 7 | import UIKit 8 | 9 | //MARK: Interactor - 10 | protocol ___VARIABLE_productName:identifier___InteractorOutputProtocol: class { 11 | 12 | /* Interactor -> Presenter */ 13 | } 14 | 15 | protocol ___VARIABLE_productName:identifier___InteractorInputProtocol: class { 16 | 17 | var presenter: ___VARIABLE_productName:identifier___InteractorOutputProtocol? { get set } 18 | 19 | /* Presenter -> Interactor */ 20 | } 21 | 22 | class ___VARIABLE_productName:identifier___Interactor: ___VARIABLE_productName:identifier___InteractorInputProtocol { 23 | 24 | weak var presenter: ___VARIABLE_productName:identifier___InteractorOutputProtocol? 25 | } 26 | -------------------------------------------------------------------------------- /MVP Kit.xctemplate/InputProtocol/___FILEBASENAME___ViewController.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: View - 6 | 7 | import UIKit 8 | 9 | class ___VARIABLE_productName:identifier___ViewController: UIViewController, ___VARIABLE_productName:identifier___ViewProtocol { 10 | 11 | var presenter: ___VARIABLE_productName:identifier___PresenterProtocol? 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | presenter = ___VARIABLE_productName:identifier___Presenter(interface: self) 16 | presenter?.test() 17 | } 18 | 19 | func isLoading(loading: Bool) { 20 | // show loading 21 | print("check loading \(loading)") 22 | } 23 | 24 | func isError(message: String) { 25 | // show error 26 | print("check message \(message)") 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /VIPER Kit.xctemplate/Default/___FILEBASENAME___Router.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: Router - 6 | 7 | import UIKit 8 | 9 | //MARK: Router - 10 | protocol ___VARIABLE_productName:identifier___RouterProtocol: class { 11 | 12 | } 13 | 14 | class ___VARIABLE_productName:identifier___Router: ___VARIABLE_productName:identifier___RouterProtocol { 15 | 16 | weak var viewController: UIViewController? 17 | 18 | static func createModule() -> UIViewController { 19 | // Change to get view from storyboard if not using progammatic UI 20 | let view = ___VARIABLE_productName:identifier___ViewController(nibName: nil, bundle: nil) 21 | let interactor = ___VARIABLE_productName:identifier___Interactor() 22 | let router = ___VARIABLE_productName:identifier___Router() 23 | let presenter = ___VARIABLE_productName:identifier___Presenter(interface: view, interactor: interactor, router: router) 24 | 25 | view.presenter = presenter 26 | interactor.presenter = presenter 27 | router.viewController = view 28 | 29 | return view 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Architecture Kit Swift 3 | 4 | ![Architecture Kit](https://raw.githubusercontent.com/fauzisho/Architecture-Kit-Swift/master/image.png) 5 | 6 | Template to help developer create new component architecture are MVP, MVVM, VIPER, VIVMER, Redux, and many more 7 | 8 | ## How to install 9 | 10 | ### Using script 11 | Only need execute this command in terminal: 12 | ```swift 13 | sudo swift install.swift 14 | ``` 15 | 16 | ## How to use 17 | 18 | ![Architecture Kit](https://raw.githubusercontent.com/fauzisho/Architecture-Kit-Swift/master/image2.png) 19 | 20 | Click Right Folder -> Click New File -> Choose Architecture 21 | 22 | 23 | ### Architecture Template 24 | 25 | - [x] MVP 26 | - [x] MVVM 27 | - [ ] MVVM-C 28 | - [x] VIPER 29 | - [x] [VIVMER](https://github.com/fauzisho/VIVMER-Architecture) 30 | - [ ] ELM 31 | - [ ] REDUX 32 | 33 | ### App Example 34 | 35 | 36 | ### Reference 37 | 38 | - iOS Application Design Patterns in Swift book [App Architecture](https://www.objc.io/books/app-architecture/) book! 39 | - [Swift + MVVM + Two Way Binding = Win!](https://codeburst.io/swift-mvvm-two-way-binding-win-b447edc55ff5) 40 | - [Architecting iOS Apps with VIPER](https://www.objc.io/issues/13-architecture/viper/) 41 | 42 | ### Contribution 43 | Architecture Kit Swift is fully open-source. All suggestions and contributions are welcome! 44 | 45 | 46 | -------------------------------------------------------------------------------- /VIPER Kit.xctemplate/Default/___FILEBASENAME___Presenter.swift: -------------------------------------------------------------------------------- 1 | // Created ___FULLUSERNAME___ on ___DATE___. 2 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 3 | // 4 | // Template generated by Fauzi Sholichin @fauzisho 5 | // Component: Presenter - 6 | 7 | import UIKit 8 | 9 | //MARK: View - 10 | protocol ___VARIABLE_productName:identifier___ViewProtocol: class { 11 | 12 | var presenter: ___VARIABLE_productName:identifier___PresenterProtocol? { get set } 13 | 14 | /* Presenter -> ViewController */ 15 | } 16 | 17 | //MARK: Presenter - 18 | protocol ___VARIABLE_productName:identifier___PresenterProtocol: class { 19 | 20 | var interactor: ___VARIABLE_productName:identifier___InteractorInputProtocol? { get set } 21 | } 22 | 23 | class ___VARIABLE_productName:identifier___Presenter: ___VARIABLE_productName:identifier___PresenterProtocol, ___VARIABLE_productName:identifier___InteractorOutputProtocol { 24 | 25 | weak private var view: ___VARIABLE_productName:identifier___ViewProtocol? 26 | var interactor: ___VARIABLE_productName:identifier___InteractorInputProtocol? 27 | private let router: ___VARIABLE_productName:identifier___RouterProtocol 28 | 29 | init(interface: ___VARIABLE_productName:identifier___ViewProtocol, interactor: ___VARIABLE_productName:identifier___InteractorInputProtocol?, router: ___VARIABLE_productName:identifier___RouterProtocol) { 30 | self.view = interface 31 | self.interactor = interactor 32 | self.router = router 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /VIPER Kit.xctemplate/Default/___FILEBASENAME___ViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /MVP Kit.xctemplate/Default/___FILEBASENAME___ViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /MVVM Kit.xctemplate/Default/___FILEBASENAME___ViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /MVP Kit.xctemplate/InputProtocol/___FILEBASENAME___ViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /MVVM Kit.xctemplate/withoutObserver/___FILEBASENAME___ViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /MVP Kit.xctemplate/TemplateInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AllowedTypes 6 | 7 | public.swift-source 8 | 9 | Platforms 10 | 11 | com.apple.platform.iphoneos 12 | 13 | DefaultCompletionName 14 | SwiftMVPModule 15 | Description 16 | Basic MVP module template. Creates View,Presenter, Protocols. 17 | Kind 18 | Xcode.IDEKit.TextSubstitutionFileTemplateKind 19 | MainTemplateFile 20 | ___FILEBASENAME___ 21 | SortOrder 22 | 1 23 | Options 24 | 25 | 26 | Description 27 | Name of module that you need create 28 | Default 29 | 30 | Identifier 31 | mvpModuleName 32 | Name 33 | Module name 34 | Required 35 | YES 36 | Type 37 | text 38 | 39 | 40 | Default 41 | ___VARIABLE_mvpModuleName___ 42 | Identifier 43 | productName 44 | Type 45 | static 46 | 47 | 48 | Identifier 49 | InputProtocol 50 | Name 51 | add protocol presenter 52 | Description 53 | add protocol presenter 54 | Type 55 | checkbox 56 | Default 57 | false 58 | NotPersisted 59 | 60 | 61 | 62 | Template Author 63 | Fauzi Sholichin - @fauzisho 64 | 65 | 66 | -------------------------------------------------------------------------------- /MVVM Kit.xctemplate/TemplateInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AllowedTypes 6 | 7 | public.swift-source 8 | 9 | Platforms 10 | 11 | com.apple.platform.iphoneos 12 | 13 | DefaultCompletionName 14 | SwiftMVVMKit 15 | Description 16 | Basic MVVM Kit template. Creates View, View Model, Model, Observer. 17 | Kind 18 | Xcode.IDEKit.TextSubstitutionFileTemplateKind 19 | MainTemplateFile 20 | ___FILEBASENAME___ 21 | SortOrder 22 | 1 23 | Options 24 | 25 | 26 | Description 27 | Name of module that you need create 28 | Default 29 | 30 | Identifier 31 | mvvmModuleName 32 | Name 33 | Module name 34 | Required 35 | YES 36 | Type 37 | text 38 | 39 | 40 | Default 41 | ___VARIABLE_mvvmModuleName___ 42 | Identifier 43 | productName 44 | Type 45 | static 46 | 47 | 48 | Identifier 49 | withoutObserver 50 | Name 51 | without observer class 52 | Description 53 | without observer class 54 | Type 55 | checkbox 56 | Default 57 | false 58 | NotPersisted 59 | 60 | 61 | 62 | Template Author 63 | Fauzi Sholichin - @fauzisho 64 | 65 | 66 | -------------------------------------------------------------------------------- /VIPER Kit.xctemplate/TemplateInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AllowedTypes 6 | 7 | public.swift-source 8 | 9 | Platforms 10 | 11 | com.apple.platform.iphoneos 12 | 13 | DefaultCompletionName 14 | SwiftVIPERKit 15 | Description 16 | Basic VIPER Kit template. Creates View, Presenter, Entity, Router, and Interactor 17 | Kind 18 | Xcode.IDEKit.TextSubstitutionFileTemplateKind 19 | MainTemplateFile 20 | ___FILEBASENAME___ 21 | SortOrder 22 | 1 23 | Options 24 | 25 | 26 | Description 27 | Name of module that you need create 28 | Default 29 | 30 | Identifier 31 | mvvmModuleName 32 | Name 33 | Module name 34 | Required 35 | YES 36 | Type 37 | text 38 | 39 | 40 | Default 41 | ___VARIABLE_mvvmModuleName___ 42 | Identifier 43 | productName 44 | Type 45 | static 46 | 47 | 48 | Identifier 49 | Default 50 | Name 51 | without entity class 52 | Description 53 | without entity class 54 | Type 55 | checkbox 56 | Default 57 | false 58 | NotPersisted 59 | 60 | 61 | 62 | Template Author 63 | Fauzi Sholichin - @fauzisho 64 | 65 | 66 | -------------------------------------------------------------------------------- /install.swift: -------------------------------------------------------------------------------- 1 | // Created by Fauzi Sholichin on 23/02/2019. 2 | // Copyright © 2019 Fauzi Sholichin. All rights reserved. 3 | // 4 | 5 | import Foundation 6 | 7 | // Get the actual logged-in user's home directory (e.g., /Users/yourname) 8 | func getUserHomePath() -> String? { 9 | // Option 1: Use environment variable $HOME 10 | if let home = ProcessInfo.processInfo.environment["HOME"], !home.isEmpty { 11 | return home 12 | } 13 | 14 | // Fallback: Use NSHomeDirectory() 15 | return NSHomeDirectory() 16 | } 17 | 18 | // Template names 19 | let templateMVP = "MVP Kit.xctemplate" 20 | let templateMVVM = "MVVM Kit.xctemplate" 21 | let templateVIPER = "VIPER Kit.xctemplate" 22 | 23 | // ✅ Use real user's home, not root's 24 | guard let userHome = getUserHomePath() else { 25 | print("❌ Could not determine user home directory.") 26 | exit(1) 27 | } 28 | 29 | let xcodeTemplatesPath = "\(userHome)/Library/Developer/Xcode/Templates/Project Templates/Application" 30 | 31 | func printInConsole(_ message: Any) { 32 | print("====================================") 33 | print("\(message)") 34 | print("====================================") 35 | } 36 | 37 | func moveTemplate() { 38 | let fileManager = FileManager.default 39 | let currentDir = fileManager.currentDirectoryPath 40 | 41 | // Ensure destination directory exists 42 | do { 43 | if !fileManager.fileExists(atPath: xcodeTemplatesPath) { 44 | try fileManager.createDirectory( 45 | atPath: xcodeTemplatesPath, 46 | withIntermediateDirectories: true, 47 | attributes: nil 48 | ) 49 | printInConsole("✅ Created: \(xcodeTemplatesPath)") 50 | } 51 | } catch let error { 52 | printInConsole("❌ Failed to create path: \(error.localizedDescription)") 53 | return 54 | } 55 | 56 | let templates = [templateMVP, templateMVVM, templateVIPER] 57 | 58 | for template in templates { 59 | let src = "\(currentDir)/\(template)" 60 | let dst = "\(xcodeTemplatesPath)/\(template)" 61 | 62 | guard fileManager.fileExists(atPath: src) else { 63 | printInConsole("⚠️ Not found: \(src)") 64 | continue 65 | } 66 | 67 | do { 68 | if fileManager.fileExists(atPath: dst) { 69 | try fileManager.removeItem(atPath: dst) 70 | } 71 | try fileManager.copyItem(atPath: src, toPath: dst) 72 | printInConsole("✅ Installed: \(template) in \(dst)") 73 | } catch let error { 74 | printInConsole("❌ Copy failed \(template): \(error.localizedDescription)") 75 | } 76 | } 77 | 78 | printInConsole("🎉 Done! Check Xcode → New Project") 79 | } 80 | 81 | moveTemplate() 82 | --------------------------------------------------------------------------------