Open Terminal and run:
$ pip3 install igen
$ pip3 install -U igen
or
$ pip3 uninstall igen
$ pip3 install igen --no-cache-dir
$ pip3 uninstall igen
pip3 is already installed if you are using Python 3 (>=3.4)
In order to install Python 3, you need to install Homebrew, run the following command in Terminal:
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Then install Python 3:
$ brew install python
The Base Template contains necessary files for a screen using the Clean Architecture pattern.
Open Terminal, navigate to the folder you want to save the files and run:
$ igen template base <Scene_Name> [--window]
--window
: use UIWindow instead of UINavigationController in the Navigator.
The first time you use the template
command, you need to enter your project information:
Enter project name: Your Project
Enter developer name: Your Name
Enter company name: Your Company
Later, if you want to update the information you can run:
$ igen config project
$ igen template base Login
Output:
Successfully created files:
Login/LoginViewModel.swift
Login/LoginNavigator.swift
Login/LoginUseCase.swift
Login/LoginViewController.swift
Login/LoginAssembler.swift
Login/Test/LoginViewModelTests.swift
Login/Test/LoginUseCaseMock.swift
Login/Test/LoginNavigatorMock.swift
Login/Test/LoginViewControllerTests.swift
To create a Clean Architecture skeleton project, run:
$ igen template skeleton <Folder_Name>
Run the following command in Terminal:
$ igen template skeleton DemoApp
Output:
Successfully created files:
DemoApp/Podfile
DemoApp/gitignore
DemoApp/Localizable.strings
DemoApp/pull_request_template.md
DemoApp/swiftlint.yml
DemoApp/DemoApp-Bridging-Header.h
DemoApp/Sources/UnitTestViewController.swift
DemoApp/Sources/AppDelegate.swift
DemoApp/Sources/Assembler.swift
DemoApp/Sources/Support/Utils.swift
DemoApp/Sources/Support/Extensions/UIViewController+.swift
DemoApp/Sources/Support/Extensions/UIViewController+Rx.swift
DemoApp/Sources/Data/Gateways/GatewaysAssembler.swift
DemoApp/Sources/Data/API/APIError.swift
DemoApp/Sources/Data/API/APIService.swift
DemoApp/Sources/Data/API/APIInput.swift
DemoApp/Sources/Data/API/APIOutput.swift
DemoApp/Sources/Config/APIUrls.swift
DemoApp/Sources/Scenes/App/AppAssembler.swift
DemoApp/Sources/Scenes/App/AppNavigator.swift
DemoApp/Sources/Scenes/App/AppUseCase.swift
DemoApp/Sources/Scenes/App/AppViewModel.swift
DemoApp/Sources/Scenes/Storyboards/Storyboards.swift
The List Template shows a list of objects in a UITableView or a UICollectionView.
Copy the model to the pasteboard (clipboard) then run:
$ igen template list <Scene_Name> [--section] [--collection] [--window] [--paging]
--paging
: use pagination.
--section
: show the list with header sections.
--collection
: use UICollectionView instead of UITableView.
--window
: use UIWindow instead of UINavigationController in the Navigator.
Copy the model:
struct Product {
let id: Int
let name: String
let price: Double
}
then run:
$ igen template list ProductList
Output:
Successfully created files:
ProductList/ProductListViewModel.swift
ProductList/ProductItemViewModel.swift
ProductList/ProductListNavigator.swift
ProductList/ProductListUseCase.swift
ProductList/ProductListViewController.swift
ProductList/ProductCell.swift
ProductList/ProductListAssembler.swift
ProductList/Test/ProductListViewModelTests.swift
ProductList/Test/ProductListUseCaseMock.swift
ProductList/Test/ProductListNavigatorMock.swift
ProductList/Test/ProductListViewControllerTests.swift
ProductList/Test/ProductCellTests.swift
The Detail Template shows details of an object in a UITableView.
Copy the model then run:
$ igen template detail <Scene_Name> [--static] [--window]
--static
: display details of the object in a static UITableViewController.
--window
: use UIWindow instead of UINavigationController in the Navigator.
Copy the model:
struct Product {
let id: Int
let name: String
let price: Double
}
then run:
$ igen template detail ProductDetail
Output:
Successfully created files:
ProductDetail/ProductDetailViewModel.swift
ProductDetail/ProductDetailNavigator.swift
ProductDetail/ProductDetailUseCase.swift
ProductDetail/ProductDetailViewController.swift
ProductDetail/ProductIdCell.swift
ProductDetail/ProductNameCell.swift
ProductDetail/ProductPriceCell.swift
ProductDetail/ProductDetailAssembler.swift
ProductDetail/Test/ProductDetailViewModelTests.swift
ProductDetail/Test/ProductDetailUseCaseMock.swift
ProductDetail/Test/ProductDetailNavigatorMock.swift
ProductDetail/Test/ProductDetailViewControllerTests.swift
ProductDetail/Test/ProductDetailCellsTests.swift
To create a form input template for a model, copy the model then run:
$ igen template form <Scene_Name> [--submit SUBMIT] [--dynamic] [--window]
--submit
: set the name of the submit action.
--dynamic
: use the dynamic form instead of the static form.
--window
: use UIWindow instead of UINavigationController in the Navigator.
Copy the model:
struct Product {
var id: Int
var name: String
var price: Double
}
then run:
$ igen template form CreateProduct --submit Create
Output:
Successfully created files:
CreateProduct/CreateProductAssembler.swift
CreateProduct/CreateProductNavigator.swift
CreateProduct/CreateProductViewModel.swift
CreateProduct/CreateProductUseCase.swift
CreateProduct/CreateProductViewController.swift
CreateProduct/Test/CreateProductUseCaseMock.swift
CreateProduct/Test/CreateProductNavigatorMock.swift
CreateProduct/Test/CreateProductViewModelTests.swift
CreateProduct/Test/CreateProductViewControllerTests.swift
To create a setting template, copy the setting enum then run:
$ igen template setting <Scene_Name> [--section] [--window]
--section
: show the list with header sections.
--window
: use UIWindow instead of UINavigationController in the Navigator.
Copy the enum:
enum SettingMenu {
case about
case support
case facebook
case email
case rating
}
then run:
$ igen template setting Setting
Output:
Successfully created files:
Setting/SettingAssembler.swift
Setting/SettingNavigator.swift
Setting/SettingViewModel.swift
Setting/SettingUseCase.swift
Setting/SettingViewController.swift
Setting/SettingMenuCell.swift
Setting/Test/SettingUseCaseMock.swift
Setting/Test/SettingNavigatorMock.swift
Setting/Test/SettingViewModelTests.swift
Setting/Test/SettingViewControllerTests.swift
Setting/Test/SettingMenuCellTests.swift
To create a login template, run:
$ igen template login <Scene_Name> [--window]
--window
: use UIWindow instead of UINavigationController in the Navigator.
Run:
$ igen template login Login
Output:
Successfully created files:
Login/LoginAssembler.swift
Login/LoginNavigator.swift
Login/LoginViewModel.swift
Login/LoginUseCase.swift
Login/LoginViewController.swift
Login/LoginDto.swift
Login/Test/LoginUseCaseMock.swift
Login/Test/LoginNavigatorMock.swift
Login/Test/LoginViewModelTests.swift
Login/Test/LoginViewControllerTests.swift
Copy the protocol/function then run:
$ igen mock [-p]
-p
, --print
: print the result.
Copy the protocol:
protocol ProductsNavigatorType {
func toProducts()
func toProductDetail(product: Product)
func toEditProduct(_ product: Product) -> Driver<EditProductDelegate>
}
then run:
$ igen mock
Output:
The result has been copied to the pasteboard.
Content in the pasteboard:
final class ProductsNavigatorMock: ProductsNavigatorType {
// MARK: - toProducts
var toProductsCalled = false
func toProducts() {
toProductsCalled = true
}
// MARK: - toProductDetail
var toProductDetailCalled = false
func toProductDetail(product: Product) {
toProductDetailCalled = true
}
// MARK: - toEditProduct
var toEditProductCalled = false
var toEditProductReturnValue = Driver<EditProductDelegate>.empty()
func toEditProduct(_ product: Product) -> Driver<EditProductDelegate> {
toEditProductCalled = true
return toEditProductReturnValue
}
}
Copy the view model then run:
$ igen test [-p]
-p
, --print
: print the result.
Copy the view model:
struct LoginViewModel: ViewModel {
struct Input {
let usernameTrigger: Driver<String>
let passwordTrigger: Driver<String>
let loginTrigger: Driver<Void>
}
struct Output {
@Property var usernameValidationMessage = ""
@Property var passwordValidationMessage = ""
@Property var isLoginEnabled = true
@Property var isLoading = false
@Property var error: Error?
}
then run:
$ igen test
Output:
The result has been copied to the pasteboard.
Content in the pasteboard:
final class LoginViewModelTests: XCTestCase {
private var viewModel: LoginViewModel!
private var navigator: LoginNavigatorMock!
private var useCase: LoginUseCaseMock!
private var input: LoginViewModel.Input!
private var output: LoginViewModel.Output!
private var disposeBag: DisposeBag!
// Triggers
private let usernameTriggerTrigger = PublishSubject<String>()
private let passwordTriggerTrigger = PublishSubject<String>()
private let loginTriggerTrigger = PublishSubject<Void>()
override func setUp() {
super.setUp()
navigator = LoginNavigatorMock()
useCase = LoginUseCaseMock()
viewModel = LoginViewModel(navigator: navigator, useCase: useCase)
input = LoginViewModel.Input(
usernameTrigger: usernameTrigger.asDriverOnErrorJustComplete(),
passwordTrigger: passwordTrigger.asDriverOnErrorJustComplete(),
loginTrigger: loginTrigger.asDriverOnErrorJustComplete()
)
disposeBag = DisposeBag()
output = viewModel.transform(input, disposeBag: disposeBag)
}
func test_usernameTriggerTrigger_() {
// arrange
// act
// assert
XCTAssert(true)
}
func test_passwordTriggerTrigger_() {
// arrange
// act
// assert
XCTAssert(true)
}
func test_loginTriggerTrigger_() {
// arrange
// act
// assert
XCTAssert(true)
}
}
Copy the class/struct then run the command:
$ igen init [-p]
-p
, --print
: print the result.
Copy the model:
struct Product {
var id: Int
var name: String
var price: Double
}
then run:
$ igen init
Output:
The result has been copied to the pasteboard.
Content in the pasteboard:
extension Product {
init() {
self.init(
id: 0,
name: "",
price: 0.0
)
}
}
Copy the json then run the command:
$ igen json <Model_Name> [-p]
—-return-classes
: return classes instead of structs.
-p
, --print
: print the result.
Copy the json:
{
"id": 989,
"content": "Hello world!",
"is_read": false,
"created_at": "2018-06-29T17:15:36+09:00"
}
then run:
$ igen json Notice
Output:
The result has been copied to the pasteboard.
Content in the pasteboard:
struct Notice {
var id = 0
var content = ""
var isRead = false
var createdAt = Date()
}
extension Notice: Then { }
extension Notice: Mappable {
init?(map: Map) {
self.init()
}
mutating func mapping(map: Map) {
id <- map["id"]
content <- map["content"]
isRead <- map["is_read"]
createdAt <- (map["created_at"], DateTransform())
}
}
To update files’ headers, run:
$ igen header [--file-name] [--project] [--developer] [--created-date] [--copyright-year] [--company] <File_Paths>
--file-name
: update file name.
--project
: update project.
--developer
: update developer.
--created-date
: update created date.
--copyright-year
: update copyright year.
--company
: update company.
If you don’t set any options, the tool will update all header information base on its configuration file.
Update the company and the developer in AppDelegate’s header.
$ igen header AppDelegate.swift --company --developer
You can use wildcard as well:
Update all Swift files:
$ igen header *.swift
Update all Swift files in the Domain folder and its child folders (recursive) :
$ igen header Domain/**/*.swift
To update the project information, run:
$ igen config project [--global]
--global
: global configuration.
To view the configuration, run:
$ igen config [--global]
--global
: global configuration.
$ igen config key value [--global] [--unset]
--global
: global configuration.
--unset
: delete a configuration.
$ igen config project.id <Project_ID>
Use the special value @project
if you want to use the MD5 encoded project name as the project id.
$ igen config project.id @project
The project id will be used in file headers.
//
// AppDelegate.swift
// MGiGen (d18ea2a2902863a858af4f0e0911ed35)
//
// Created by Tuan Truong on 3/27/19.
// Copyright © 2019 Sun Asterisk. All rights reserved.
//
$ igen config output.path <Path>
Example:
Set the current working directory as the output path (relative path):
$ igen config output.path .
Set the desktop as the output path:
$ igen config output.path /Users/<Your_Name>/Desktop/
You can use a special value @here
to set the current working directory as the output path (absolute path):
$ igen config output.path @here
Other special values: @desktop
, @downloads
, @documents
To view the available configurations, run:
$ igen config keys
Output:
Available configuration keys:
project.name
project.developer
project.company
project.id
output.path
To delete the configuration file, run:
$ igen config delete [--global]
--global
: global configuration.
Install Clean Architecture templates for Xcode:
$ igen xcode install-templates
Uninstall templates:
$ igen xcode uninstall-templates
Run:
$ igen -h