├── File Templates
└── Artem Tishchenko
│ ├── Module.xctemplate
│ ├── TemplateIcon.png
│ ├── TemplateIcon@2x.png
│ ├── TemplateInfo.plist
│ └── ___FILEBASENAME___
│ │ ├── Assembly
│ │ └── ___FILEBASENAME___Assembly.swift
│ │ ├── Contracts
│ │ └── ___FILEBASENAME___Contracts.swift
│ │ ├── Interactor
│ │ └── ___FILEBASENAME___Interactor.swift
│ │ ├── Presenter
│ │ └── ___FILEBASENAME___Presenter.swift
│ │ ├── Router
│ │ └── ___FILEBASENAME___Router.swift
│ │ ├── View
│ │ └── ___FILEBASENAME___View.swift
│ │ └── ViewState
│ │ └── ___FILEBASENAME___ViewState.swift
│ └── Service.xctemplate
│ ├── TemplateIcon.png
│ ├── TemplateIcon@2x.png
│ ├── TemplateInfo.plist
│ └── ___FILEBASENAME___
│ ├── ___FILEBASENAME___.swift
│ ├── ___FILEBASENAME___Assembly.swift
│ └── ___FILEBASENAME___Type.swift
├── Project Templates
└── iOS
│ └── Artem Tishchenko
│ ├── Base.xctemplate
│ ├── LaunchScreen.storyboard
│ └── TemplateInfo.plist
│ └── VIPER.xctemplate
│ ├── ApplicationViewBuilder.swift
│ ├── Classes
│ ├── Architecture
│ │ ├── InteractorProtocol.swift
│ │ ├── PresenterProtocol.swift
│ │ ├── RouterProtocol.swift
│ │ └── ViewStateProtocol.swift
│ ├── Library
│ │ └── Swilby
│ │ │ ├── Assembly.swift
│ │ │ ├── AssemblyFactory.swift
│ │ │ ├── DependencyContainer.swift
│ │ │ ├── ObjectKey.swift
│ │ │ ├── StrongBox.swift
│ │ │ ├── WeakBox.swift
│ │ │ └── WeakContainer.swift
│ ├── Modules
│ │ └── Main
│ │ │ ├── Assembly
│ │ │ └── MainAssembly.swift
│ │ │ ├── Contracts
│ │ │ └── MainContracts.swift
│ │ │ ├── Interactor
│ │ │ └── MainInteractor.swift
│ │ │ ├── Presenter
│ │ │ └── MainPresenter.swift
│ │ │ ├── Router
│ │ │ └── MainRouter.swift
│ │ │ ├── View
│ │ │ └── MainView.swift
│ │ │ └── ViewState
│ │ │ └── MainViewState.swift
│ └── Services
│ │ └── NavigationService
│ │ ├── NavigationAssembly.swift
│ │ ├── NavigationService.swift
│ │ └── NavigationServiceType.swift
│ ├── Resources
│ └── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ └── Contents.json
│ │ └── Contents.json
│ ├── RootApp.swift
│ ├── RootView.swift
│ ├── TemplateIcon.png
│ ├── TemplateIcon@2x.png
│ └── TemplateInfo.plist
├── README-RU.md
├── README.md
└── install.swift
/File Templates/Artem Tishchenko/Module.xctemplate/TemplateIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maukur/SwiftUI-Viper-Architecture/a937ceb96e8af93b6838197a4ae11c6d520f26de/File Templates/Artem Tishchenko/Module.xctemplate/TemplateIcon.png
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Module.xctemplate/TemplateIcon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maukur/SwiftUI-Viper-Architecture/a937ceb96e8af93b6838197a4ae11c6d520f26de/File Templates/Artem Tishchenko/Module.xctemplate/TemplateIcon@2x.png
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Module.xctemplate/TemplateInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Kind
6 | Xcode.IDEFoundation.TextSubstitutionFileTemplateKind
7 | Description
8 | Module
9 | SortOrder
10 | 1
11 | DefaultCompletionName
12 | Module
13 | Platforms
14 |
15 | com.apple.platform.iphoneos
16 |
17 | Options
18 |
19 |
20 | Description
21 | The name of the module to create
22 | Identifier
23 | moduleName
24 | Name
25 | New Module Name:
26 | NotPersisted
27 |
28 | Required
29 |
30 | Type
31 | text
32 |
33 |
34 | Default
35 | ___VARIABLE_moduleName___
36 | Identifier
37 | productName
38 | Type
39 | static
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Module.xctemplate/___FILEBASENAME___/Assembly/___FILEBASENAME___Assembly.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 |
10 | import SwiftUI
11 |
12 | final class ___VARIABLE_moduleName___Assembly: Assembly {
13 |
14 | func build() -> some View {
15 |
16 | let navigation = container.resolve(NavigationAssembly.self).build()
17 |
18 | // Router
19 | let router = ___VARIABLE_moduleName___Router(navigation: navigation)
20 |
21 | // Interactor
22 | let interactor = ___VARIABLE_moduleName___Interactor()
23 |
24 | //ViewState
25 | let viewState = ___VARIABLE_moduleName___ViewState()
26 |
27 | // Presenter
28 | let presenter = ___VARIABLE_moduleName___Presenter(router: router, interactor: interactor, viewState: viewState)
29 |
30 | viewState.set(with: presenter)
31 |
32 | // View
33 | let view = ___VARIABLE_moduleName___View(viewState: viewState)
34 | return view
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Module.xctemplate/___FILEBASENAME___/Contracts/___FILEBASENAME___Contracts.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import SwiftUI
10 |
11 |
12 | // Router
13 | protocol ___VARIABLE_moduleName___RouterProtocol: RouterProtocol {
14 |
15 | }
16 |
17 | // Presenter
18 | protocol ___VARIABLE_moduleName___PresenterProtocol: PresenterProtocol {
19 |
20 | }
21 |
22 | // Interactor
23 | protocol ___VARIABLE_moduleName___InteractorProtocol: InteractorProtocol {
24 |
25 | }
26 |
27 | // ViewState
28 | protocol ___VARIABLE_moduleName___ViewStateProtocol: ViewStateProtocol {
29 | func set(with presenter: ___VARIABLE_moduleName___PresenterProtocol)
30 | }
31 |
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Module.xctemplate/___FILEBASENAME___/Interactor/___FILEBASENAME___Interactor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 |
10 | import Foundation
11 |
12 | final class ___VARIABLE_moduleName___Interactor: ___VARIABLE_moduleName___InteractorProtocol {
13 |
14 | }
15 |
16 | // MARK: Private
17 | extension ___VARIABLE_moduleName___Interactor {
18 |
19 | }
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Module.xctemplate/___FILEBASENAME___/Presenter/___FILEBASENAME___Presenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import SwiftUI
10 |
11 | final class ___VARIABLE_moduleName___Presenter: ___VARIABLE_moduleName___PresenterProtocol {
12 |
13 | private let router: ___VARIABLE_moduleName___RouterProtocol
14 | private weak var viewState: ___VARIABLE_moduleName___ViewStateProtocol?
15 | private let interactor: ___VARIABLE_moduleName___InteractorProtocol
16 |
17 | init(router: ___VARIABLE_moduleName___RouterProtocol,
18 | interactor: ___VARIABLE_moduleName___InteractorProtocol,
19 | viewState: ___VARIABLE_moduleName___ViewStateProtocol) {
20 | self.router = router
21 | self.interactor = interactor
22 | self.viewState = viewState
23 | }
24 |
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Module.xctemplate/___FILEBASENAME___/Router/___FILEBASENAME___Router.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | final class ___VARIABLE_moduleName___Router: ___VARIABLE_moduleName___RouterProtocol {
12 | private var navigation: any NavigationServiceType
13 |
14 | init(navigation: any NavigationServiceType){
15 | self.navigation = navigation
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Module.xctemplate/___FILEBASENAME___/View/___FILEBASENAME___View.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct ___VARIABLE_moduleName___View: View {
12 |
13 | @StateObject var viewState: ___VARIABLE_moduleName___ViewState
14 |
15 | var body: some View {
16 | Text("Hello iOS")
17 | }
18 | }
19 |
20 | struct ___VARIABLE_moduleName___Previews: PreviewProvider {
21 | static var previews: some View {
22 | ApplicationViewBuilder.stub.build(view: .___VARIABLE_moduleName___)
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Module.xctemplate/___FILEBASENAME___/ViewState/___FILEBASENAME___ViewState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import SwiftUI
10 |
11 | final class ___VARIABLE_moduleName___ViewState: ObservableObject, ___VARIABLE_moduleName___ViewStateProtocol {
12 | private let id = UUID()
13 | private var presenter: ___VARIABLE_moduleName___PresenterProtocol?
14 |
15 | func set(with presener: ___VARIABLE_moduleName___PresenterProtocol) {
16 | self.presenter = presener
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Service.xctemplate/TemplateIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maukur/SwiftUI-Viper-Architecture/a937ceb96e8af93b6838197a4ae11c6d520f26de/File Templates/Artem Tishchenko/Service.xctemplate/TemplateIcon.png
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Service.xctemplate/TemplateIcon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maukur/SwiftUI-Viper-Architecture/a937ceb96e8af93b6838197a4ae11c6d520f26de/File Templates/Artem Tishchenko/Service.xctemplate/TemplateIcon@2x.png
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Service.xctemplate/TemplateInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Kind
6 | Xcode.IDEFoundation.TextSubstitutionFileTemplateKind
7 | Description
8 | Service
9 | SortOrder
10 | 4
11 | DefaultCompletionName
12 | Service
13 | Platforms
14 |
15 | com.apple.platform.iphoneos
16 |
17 | Options
18 |
19 |
20 | Description
21 | The name of the Service to create
22 | Identifier
23 | serviceName
24 | Name
25 | New Service Name:
26 | NotPersisted
27 |
28 | Required
29 |
30 | Type
31 | text
32 |
33 |
34 | Default
35 | ___VARIABLE_serviceName___
36 | Identifier
37 | productName
38 | Type
39 | static
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Service.xctemplate/___FILEBASENAME___/___FILEBASENAME___.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | class ___FILEBASENAME___: ___VARIABLE_serviceName___Type {
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Service.xctemplate/___FILEBASENAME___/___FILEBASENAME___Assembly.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | class ___FILEBASENAME___: Assembly {
12 | func build() -> ___VARIABLE_serviceName___Type {
13 | let service = ___VARIABLE_serviceName___()
14 | return service
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/File Templates/Artem Tishchenko/Service.xctemplate/___FILEBASENAME___/___FILEBASENAME___Type.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | protocol ___FILEBASENAME___ {
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/Base.xctemplate/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/Base.xctemplate/TemplateInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Kind
6 | Xcode.Xcode3.ProjectTemplateUnitKind
7 | Identifier
8 | com.artemtishchenko.unit.cocoaTouchApplicationBase
9 | Ancestors
10 |
11 | com.apple.dt.unit.applicationBase
12 | com.apple.dt.unit.iosBase
13 |
14 | Targets
15 |
16 |
17 | TargetIdentifier
18 | com.apple.dt.cocoaTouchApplicationTarget
19 | SharedSettings
20 |
21 | ASSETCATALOG_COMPILER_APPICON_NAME
22 | AppIcon
23 | LD_RUNPATH_SEARCH_PATHS
24 | $(inherited) @executable_path/Frameworks
25 |
26 |
27 |
28 | Options
29 |
30 |
31 | Identifier
32 | universalDeviceFamily
33 | Name
34 | Devices:
35 | Description
36 | Which device family to create a project for
37 | SortOrder
38 | 1
39 | Type
40 | popup
41 | Default
42 | Universal
43 | Values
44 |
45 | Universal
46 | iPhone
47 | iPad
48 |
49 | Units
50 |
51 | iPhone
52 |
53 | Nodes
54 |
55 | Info.plist:UISupportedInterfaceOrientations~iPhone
56 | Resources/Assets.xcassets
57 |
58 | Definitions
59 |
60 | Resources/Assets.xcassets
61 |
62 | Group
63 |
64 | Resources
65 |
66 | Path
67 | Images-iPhone.xcassets
68 | SortOrder
69 | 100
70 |
71 |
72 |
73 | iPad
74 |
75 | Project
76 |
77 | SharedSettings
78 |
79 | TARGETED_DEVICE_FAMILY
80 | 2
81 |
82 |
83 | Nodes
84 |
85 | Info.plist:UISupportedInterfaceOrientations~iPad
86 | Resources/Assets.xcassets
87 |
88 | Definitions
89 |
90 | Resources/Assets.xcassets
91 |
92 | Group
93 |
94 | Resources
95 |
96 | Path
97 | Images-iPad.xcassets
98 | SortOrder
99 | 100
100 |
101 |
102 |
103 | Universal
104 |
105 | Project
106 |
107 | SharedSettings
108 |
109 | TARGETED_DEVICE_FAMILY
110 | 1,2
111 |
112 |
113 | Nodes
114 |
115 | Info.plist:UISupportedInterfaceOrientations~iPhone
116 | Info.plist:UISupportedInterfaceOrientations~iPad
117 | Resources/Assets.xcassets
118 |
119 | Definitions
120 |
121 | Resources/Assets.xcassets
122 |
123 | Group
124 |
125 | Resources
126 |
127 | Path
128 | Images-Universal.xcassets
129 | SortOrder
130 | 100
131 |
132 |
133 |
134 |
135 |
136 |
137 | Identifier
138 | hasUnitTests
139 | Name
140 | Include Unit Tests
141 | NotPersisted
142 |
143 | SortOrder
144 | 100
145 | Type
146 | checkbox
147 | Default
148 | true
149 | Units
150 |
151 | true
152 |
153 | Components
154 |
155 |
156 | Identifier
157 | com.apple.dt.unit.cocoaTouchApplicationUnitTestBundle
158 | Name
159 | ___PACKAGENAME___Tests
160 |
161 |
162 |
163 |
164 |
165 |
166 | Identifier
167 | hasUITests
168 | Name
169 | Include UI Tests
170 | NotPersisted
171 |
172 | SortOrder
173 | 101
174 | Type
175 | checkbox
176 | Default
177 | true
178 | Units
179 |
180 | true
181 |
182 | Components
183 |
184 |
185 | Identifier
186 | com.apple.dt.unit.cocoaTouchApplicationUITestBundle
187 | Name
188 | ___PACKAGENAME___UITests
189 |
190 |
191 |
192 |
193 |
194 |
195 | Identifier
196 | languageChoice
197 | Units
198 |
199 | Swift
200 |
201 | Nodes
202 |
203 | Definitions
204 |
205 |
206 |
207 |
208 |
209 | Nodes
210 |
211 | Info.plist:iPhone
212 | Info.plist:UIRequiredDeviceCapabilities:base
213 | Info.plist:LaunchScreen
214 | Base.lproj/LaunchScreen.storyboard
215 |
216 | Definitions
217 |
218 | Info.plist:iPhone
219 | <key>LSRequiresIPhoneOS</key>
220 | <true/>
221 | Info.plist:UIRequiredDeviceCapabilities
222 |
223 | Beginning
224 | <key>UIRequiredDeviceCapabilities</key>
225 | <array>
226 | End
227 | </array>
228 | Indent
229 | 1
230 |
231 | Info.plist:UIRequiredDeviceCapabilities:base
232 | <string>armv7</string>
233 | Info.plist:statusBarTintForNavBar
234 | <key>UIStatusBarTintParameters</key>
235 | <dict>
236 | <key>UINavigationBar</key>
237 | <dict>
238 | <key>Style</key>
239 | <string>UIBarStyleDefault</string>
240 | <key>Translucent</key>
241 | <false/>
242 | </dict>
243 | </dict>
244 |
245 | Info.plist:LaunchScreen
246 | <key>UILaunchStoryboardName</key>
247 | <string>LaunchScreen</string>
248 |
249 | Base.lproj/LaunchScreen.storyboard
250 |
251 |
252 | Path
253 | LaunchScreen.storyboard
254 | SortOrder
255 | 101
256 |
257 |
258 |
259 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/ApplicationViewBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import SwiftUI
10 |
11 | final class ApplicationViewBuilder : Assembly, ObservableObject {
12 |
13 | required init(container: Container) {
14 | super.init(container: container)
15 | }
16 |
17 | @ViewBuilder
18 | func build(view: Views) -> some View {
19 | switch view {
20 | case .main:
21 | buildMain()
22 | }
23 | }
24 |
25 | @ViewBuilder
26 | fileprivate func buildMain() -> some View {
27 | container.resolve(MainAssembly.self).build()
28 | }
29 |
30 | }
31 |
32 | extension ApplicationViewBuilder {
33 |
34 | static var stub: ApplicationViewBuilder {
35 | return ApplicationViewBuilder(
36 | container: RootApp().container
37 | )
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Architecture/InteractorProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | protocol InteractorProtocol: AnyObject {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Architecture/PresenterProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | protocol PresenterProtocol: AnyObject {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Architecture/RouterProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | protocol RouterProtocol: AnyObject {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Architecture/ViewStateProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | protocol ViewStateProtocol : AnyObject {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Library/Swilby/Assembly.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | protocol AssemblyType: AnyObject {
12 | associatedtype Container
13 | var container: Container {get set}
14 | init(container: Container)
15 | }
16 |
17 | class Assembly: AssemblyType {
18 |
19 | var container: Container
20 |
21 | required init(container: Container) {
22 | self.container = container
23 | }
24 | }
25 |
26 | // Box
27 | extension Assembly {
28 | func weakBox(_ configure: () -> T) -> T {
29 | return self.container.weakBox(configure)
30 | }
31 |
32 | func strongBox(_ configure: () -> T) -> T {
33 | return self.container.strongBox(configure)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Library/Swilby/AssemblyFactory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | protocol AssemblyFactoryProtocol: AnyObject {
12 | func apply(_ assembly: T.Type, name: String?)
13 | func resolve(_ type: T.Type, name: String?) -> T.Type
14 | }
15 |
16 | class AssemblyFactory {
17 | typealias AssemblyCollection = [String : Any.Type]
18 | fileprivate var assemblyCollection = AssemblyCollection()
19 | }
20 |
21 | extension AssemblyFactory: AssemblyFactoryProtocol {
22 | func apply(_ assembly: T.Type, name: String? = nil) {
23 | let key = ObjectKey(assembly, name: name).key
24 | self.assemblyCollection[key] = assembly
25 | }
26 |
27 | func resolve(_ type: T.Type, name: String? = nil) -> T.Type {
28 | let key = ObjectKey(type, name: name).key
29 | guard let assembly = assemblyCollection[key] else { fatalError("Assemblay '\(String(describing: type))' has't been registered, use 'apply( _:)' method") }
30 | return assembly as! T.Type
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Library/Swilby/DependencyContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | typealias Container = Resolver
12 | typealias LightContainer = Applyer & Resolver
13 |
14 | // Container
15 | protocol Applyer {
16 | func apply(_ type: T.Type, name: String?)
17 | func apply(_ type: T.Type)
18 | }
19 |
20 | extension Applyer {
21 | func apply(_ type: T.Type) {
22 | self.apply(type, name: nil)
23 | }
24 | }
25 |
26 | // Resolver
27 | protocol Resolver: WeakBox, StrongBox {
28 | func resolve(_ type: T.Type, name: String?) -> T
29 | func resolve(_ type: T.Type) -> T
30 | }
31 |
32 | extension Resolver {
33 | func resolve(_ type: T.Type) -> T where T : Assembly {
34 | return self.resolve(type, name: nil)
35 | }
36 | }
37 |
38 | // DI Container
39 | class DependencyContainer {
40 | internal var weakBoxHolder = [String : WeakContainer]()
41 | internal var strongBoxHolder = [String : AnyObject]()
42 |
43 | let assemblyFactory: AssemblyFactoryProtocol
44 |
45 | init(assemblyFactory: AssemblyFactoryProtocol) {
46 | self.assemblyFactory = assemblyFactory
47 | }
48 | }
49 |
50 | extension DependencyContainer: Resolver {
51 | func resolve(_ type: T.Type, name: String?) -> T where T : Assembly {
52 | let module = self.assemblyFactory.resolve(type, name: name)
53 | return module.init(container: self)
54 | }
55 | }
56 |
57 | extension DependencyContainer: Applyer {
58 | func apply(_ type: T.Type, name: String?) where T : AssemblyType {
59 | self.assemblyFactory.apply(type, name: name)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Library/Swilby/ObjectKey.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | internal struct ObjectKey {
12 | fileprivate let objectType: Any.Type
13 | fileprivate let name: String?
14 |
15 | fileprivate(set) var key: String {
16 | get { return String(self.hashValue) }
17 | set {}
18 | }
19 |
20 | internal init(_ objectType: Any.Type, name: String? = nil) {
21 | self.objectType = objectType
22 | self.name = name
23 | }
24 | }
25 |
26 | // MARK: Hashable
27 | extension ObjectKey: Hashable {
28 | func hash(into hasher: inout Hasher) {
29 | hasher.combine(String(describing: objectType).hashValue ^ (name?.hashValue ?? 0))
30 | }
31 | }
32 |
33 | // MARK: Equatable
34 | func == (lhs: ObjectKey, rhs: ObjectKey) -> Bool {
35 | return lhs.objectType == rhs.objectType && lhs.name == rhs.name
36 | }
37 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Library/Swilby/StrongBox.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | protocol StrongBox: AnyObject {
12 | var strongBoxHolder: [String : AnyObject] { set get }
13 | }
14 |
15 | extension StrongBox {
16 | func strongBox(_ configure: () -> T) -> T {
17 | let key = ObjectKey(T.self).key
18 | if let object = self.strongBoxHolder[key] {
19 | return object as! T
20 | }
21 | let object = configure()
22 | strongBoxHolder[key] = object as AnyObject
23 | return object
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Library/Swilby/WeakBox.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | protocol WeakBox: AnyObject {
12 | var weakBoxHolder: [String : WeakContainer] { set get}
13 | }
14 |
15 | extension WeakBox {
16 | func weakBox(_ configure: () -> T) -> T {
17 | let key = ObjectKey(T.self).key
18 | if let object = self.weakBoxHolder[key]?.value as? T {
19 | return object
20 | }
21 | let object = configure()
22 | weakBoxHolder[key] = WeakContainer(value: object as AnyObject)
23 | return object
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Library/Swilby/WeakContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | class WeakContainer {
12 | fileprivate weak var _value: AnyObject?
13 | var value: T? {
14 | set { self._value = newValue as AnyObject }
15 | get { return _value as? T }
16 | }
17 |
18 | init(value: T) {
19 | self._value = value as AnyObject
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Modules/Main/Assembly/MainAssembly.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 |
10 | import SwiftUI
11 |
12 | final class MainAssembly: Assembly {
13 |
14 | func build() -> some View {
15 |
16 | let navigation = container.resolve(NavigationAssembly.self).build()
17 |
18 | // Router
19 | let router = MainRouter(navigation: navigation)
20 |
21 | // Interactor
22 | let interactor = MainInteractor()
23 |
24 | //ViewState
25 | let viewState = MainViewState()
26 |
27 | // Presenter
28 | let presenter = MainPresenter(router: router,
29 | interactor: interactor,
30 | viewState: viewState)
31 |
32 | viewState.set(with: presenter)
33 |
34 | // View
35 | let view = MainView(viewState: viewState)
36 | return view
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Modules/Main/Contracts/MainContracts.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import SwiftUI
10 |
11 |
12 | // Router
13 | protocol MainRouterProtocol: RouterProtocol {
14 |
15 | }
16 |
17 | // Presenter
18 | protocol MainPresenterProtocol: PresenterProtocol {
19 |
20 | }
21 |
22 | // Interactor
23 | protocol MainInteractorProtocol: InteractorProtocol {
24 |
25 | }
26 |
27 | // ViewState
28 | protocol MainViewStateProtocol: ViewStateProtocol {
29 | func set(with presenter: MainPresenterProtocol)
30 | }
31 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Modules/Main/Interactor/MainInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | final class MainInteractor: MainInteractorProtocol {
12 |
13 | }
14 |
15 | // MARK: Private
16 | extension MainInteractor {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Modules/Main/Presenter/MainPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import SwiftUI
10 |
11 | final class MainPresenter: MainPresenterProtocol {
12 |
13 | private let router: MainRouterProtocol
14 | private weak var viewState: MainViewStateProtocol?
15 | private let interactor: MainInteractorProtocol
16 |
17 | init(router: MainRouterProtocol,
18 | interactor: MainInteractorProtocol,
19 | viewState: MainViewStateProtocol) {
20 | self.router = router
21 | self.interactor = interactor
22 | self.viewState = viewState
23 | }
24 |
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Modules/Main/Router/MainRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | final class MainRouter: MainRouterProtocol {
12 | var navigation: any NavigationServiceType
13 |
14 | init(navigation: any NavigationServiceType){
15 | self.navigation = navigation
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Modules/Main/View/MainView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct MainView: View {
12 |
13 | @StateObject var viewState: MainViewState
14 |
15 | var body: some View {
16 | Text("Hello iOS")
17 | }
18 | }
19 |
20 | struct MainPreviews: PreviewProvider {
21 | static var previews: some View {
22 | ApplicationViewBuilder.stub.build(view: .main)
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Modules/Main/ViewState/MainViewState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import SwiftUI
10 |
11 | final class MainViewState: ObservableObject, MainViewStateProtocol {
12 | private let id = UUID()
13 | private var presenter: MainPresenterProtocol?
14 |
15 | func set(with presener: MainPresenterProtocol) {
16 | self.presenter = presener
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Services/NavigationService/NavigationAssembly.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | final class NavigationAssembly: Assembly {
12 |
13 | //Only one navigation should use in app
14 | static let navigation: any NavigationServiceType = NavigationService()
15 |
16 | func build() -> any NavigationServiceType {
17 | return NavigationAssembly.navigation
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Services/NavigationService/NavigationService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 |
10 | import SwiftUI
11 |
12 | public class NavigationService: NavigationServiceType {
13 |
14 | public let id = UUID()
15 |
16 | public static func == (lhs: NavigationService, rhs: NavigationService) -> Bool {
17 | lhs.id == rhs.id
18 | }
19 |
20 | @Published var modalView: Views?
21 | @Published var popupView: Views?
22 | @Published var items: [Views] = []
23 | @Published var alert: CustomAlert?
24 | }
25 |
26 |
27 | enum Views: Identifiable, Equatable, Hashable {
28 |
29 | var id: String { stringKey }
30 |
31 | static func == (lhs: Views, rhs: Views) -> Bool {
32 | lhs.stringKey == rhs.stringKey
33 | }
34 |
35 | func hash(into hasher: inout Hasher) {
36 | hasher.combine(self.stringKey)
37 | }
38 |
39 | case main
40 |
41 | var stringKey: String {
42 | switch self {
43 | case .main:
44 | return "main"
45 | }
46 | }
47 | }
48 |
49 |
50 | enum CustomAlert: Equatable, Hashable {
51 | static func == (lhs: CustomAlert, rhs: CustomAlert) -> Bool {
52 | lhs.hashValue == rhs.hashValue
53 | }
54 |
55 | func hash(into hasher: inout Hasher) {
56 | switch self {
57 | case .defaultAlert:
58 | hasher.combine("defaultAlert")
59 | }
60 | }
61 |
62 | case defaultAlert(yesAction: (()->Void)?, noAction: (()->Void)?)
63 | }
64 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Classes/Services/NavigationService/NavigationServiceType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import Foundation
10 |
11 | /// The `NavigationServiceType` protocol defines a navigation service in the application,
12 | /// providing management of the view stack, modal windows, popups, and alerts.
13 | protocol NavigationServiceType: ObservableObject, Identifiable {
14 |
15 | /// An array of views that make up the current navigation stack.
16 | /// Used for managing transitions between screens.
17 | /// By default, this array is empty, and the root page is bound to the NavigationStack body in the RootView body.
18 | var items: [Views] { get set }
19 |
20 | /// The current modal view, if active.
21 | /// Can be `nil` if there is no active modal window.
22 | var modalView: Views? { get set }
23 |
24 | /// The current popup view, if active.
25 | /// Can be `nil` if no popup is displayed.
26 | var popupView: Views? { get set }
27 |
28 | /// The current alert (dialog window), if active.
29 | /// Can be `nil` if there are no active alerts.
30 | var alert: CustomAlert? { get set }
31 | }
32 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Resources/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/Resources/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/RootApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import SwiftUI
10 |
11 | @main
12 | struct RootApp: App {
13 |
14 | @ObservedObject var appViewBuilder: ApplicationViewBuilder
15 | @ObservedObject var navigationService: NavigationService
16 |
17 | let container: DependencyContainer = {
18 | let factory = AssemblyFactory()
19 | let container = DependencyContainer(assemblyFactory: factory)
20 |
21 | // Services
22 | container.apply(NavigationAssembly.self)
23 |
24 | // Modules
25 | container.apply(MainAssembly.self)
26 |
27 | return container
28 | }()
29 |
30 | init() {
31 | navigationService = container.resolve(NavigationAssembly.self).build() as! NavigationService
32 |
33 | appViewBuilder = ApplicationViewBuilder(container: container)
34 | }
35 |
36 |
37 | var body: some Scene {
38 | WindowGroup {
39 | RootView(navigationService: navigationService,
40 | appViewBuilder: appViewBuilder)
41 | }
42 | }
43 |
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/RootView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ___FILENAME___
3 | // ___PROJECTNAME___
4 | //
5 | // Created by ___FULLUSERNAME___ on ___DATE___
6 | // ___COPYRIGHT___
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct RootView: View {
12 |
13 | @ObservedObject var navigationService: NavigationService
14 | @ObservedObject var appViewBuilder: ApplicationViewBuilder
15 |
16 | var body: some View {
17 | NavigationStack(path: $navigationService.items) {
18 | appViewBuilder.build(view: .main)
19 | .navigationDestination(for: Views.self) { path in
20 | switch path {
21 | default:
22 | fatalError()
23 | }
24 | }
25 | }
26 | .fullScreenCover(item: $navigationService.popupView) { item in
27 | appViewBuilder.build(view: item)
28 | .presentationBackground(.clear)
29 | }
30 | .fullScreenCover(item: $navigationService.modalView) { item in
31 | appViewBuilder.build(view: item)
32 | }
33 | .alert(isPresented: .constant($navigationService.alert.wrappedValue != nil)) {
34 | switch navigationService.alert {
35 | case .defaultAlert(let yesAction, let noAction):
36 | return Alert(title: Text("Title"),
37 | primaryButton: .default(Text("Yes"), action: yesAction),
38 | secondaryButton: .destructive(Text("No"), action: noAction))
39 | case .none:
40 | fatalError()
41 | }
42 | }
43 |
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/TemplateIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maukur/SwiftUI-Viper-Architecture/a937ceb96e8af93b6838197a4ae11c6d520f26de/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/TemplateIcon.png
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/TemplateIcon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maukur/SwiftUI-Viper-Architecture/a937ceb96e8af93b6838197a4ae11c6d520f26de/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/TemplateIcon@2x.png
--------------------------------------------------------------------------------
/Project Templates/iOS/Artem Tishchenko/VIPER.xctemplate/TemplateInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Kind
6 | Xcode.Xcode3.ProjectTemplateUnitKind
7 | Identifier
8 | com.artemtishchenko.unit.VIPER
9 | Ancestors
10 |
11 | com.artemtishchenko.unit.cocoaTouchApplicationBase
12 | com.apple.dt.unit.languageChoice
13 |
14 | Concrete
15 |
16 | Description
17 | This template provides a starting point for an SwiftUI application that uses a "VIPER" Architecture.
18 | SortOrder
19 | 1
20 | Options
21 |
22 |
23 | Identifier
24 | languageChoice
25 | Units
26 |
27 | Swift
28 |
29 | Nodes
30 |
31 | RootApp.swift
32 | RootView.swift
33 | ApplicationViewBuilder.swift
34 |
35 | Classes/Architecture/InteractorProtocol.swift
36 | Classes/Architecture/PresenterProtocol.swift
37 | Classes/Architecture/RouterProtocol.swift
38 | Classes/Architecture/ViewStateProtocol.swift
39 |
40 | Classes/Modules/Main/Contracts/MainContracts.swift
41 | Classes/Modules/Main/Assembly/MainAssembly.swift
42 | Classes/Modules/Main/View/MainView.swift
43 | Classes/Modules/Main/ViewState/MainViewState.swift
44 | Classes/Modules/Main/Interactor/MainInteractor.swift
45 | Classes/Modules/Main/Presenter/MainPresenter.swift
46 | Classes/Modules/Main/Router/MainRouter.swift
47 |
48 | Classes/Services/NavigationService/NavigationAssembly.swift
49 | Classes/Services/NavigationService/NavigationServiceType.swift
50 | Classes/Services/NavigationService/NavigationService.swift
51 |
52 | Classes/Library/Swilby/Assembly.swift
53 | Classes/Library/Swilby/AssemblyFactory.swift
54 | Classes/Library/Swilby/DependencyContainer.swift
55 | Classes/Library/Swilby/ObjectKey.swift
56 | Classes/Library/Swilby/StrongBox.swift
57 | Classes/Library/Swilby/WeakBox.swift
58 | Classes/Library/Swilby/WeakContainer.swift
59 |
60 |
61 |
62 |
63 |
64 | Definitions
65 |
66 |
67 |
68 | RootApp.swift
69 |
70 | Path
71 | RootApp.swift
72 |
73 |
74 |
75 |
76 | RootView.swift
77 |
78 | Path
79 | RootView.swift
80 |
81 |
82 |
83 |
84 | ApplicationViewBuilder.swift
85 |
86 | Path
87 | ApplicationViewBuilder.swift
88 |
89 |
90 |
91 |
92 | Classes/Services/NavigationService/NavigationAssembly.swift
93 |
94 | Group
95 |
96 | Classes
97 | Services
98 | NavigationService
99 |
100 | Path
101 | Classes/Services/NavigationService/NavigationAssembly.swift
102 |
103 |
104 | Classes/Services/NavigationService/NavigationServiceType.swift
105 |
106 | Group
107 |
108 | Classes
109 | Services
110 | NavigationService
111 |
112 | Path
113 | Classes/Services/NavigationService/NavigationServiceType.swift
114 |
115 |
116 | Classes/Services/NavigationService/NavigationService.swift
117 |
118 | Group
119 |
120 | Classes
121 | Services
122 | NavigationService
123 |
124 | Path
125 | Classes/Services/NavigationService/NavigationService.swift
126 |
127 |
128 |
129 |
130 | Classes/Architecture/InteractorProtocol.swift
131 |
132 | Group
133 |
134 | Classes
135 | Architecture
136 |
137 | Path
138 | Classes/Architecture/InteractorProtocol.swift
139 |
140 |
141 | Classes/Architecture/ViewStateProtocol.swift
142 |
143 | Group
144 |
145 | Classes
146 | Architecture
147 |
148 | Path
149 | Classes/Architecture/ViewStateProtocol.swift
150 |
151 |
152 | Classes/Architecture/RouterProtocol.swift
153 |
154 | Group
155 |
156 | Classes
157 | Architecture
158 |
159 | Path
160 | Classes/Architecture/RouterProtocol.swift
161 |
162 |
163 | Classes/Architecture/PresenterProtocol.swift
164 |
165 | Group
166 |
167 | Classes
168 | Architecture
169 |
170 | Path
171 | Classes/Architecture/PresenterProtocol.swift
172 |
173 |
174 |
175 |
176 |
177 | Classes/Modules/Main/Contracts/MainContracts.swift
178 |
179 | Group
180 |
181 | Classes
182 | Modules
183 | Main
184 | Contracts
185 |
186 | Path
187 | Classes/Modules/Main/Contracts/MainContracts.swift
188 |
189 |
190 | Classes/Modules/Main/Assembly/MainAssembly.swift
191 |
192 | Group
193 |
194 | Classes
195 | Modules
196 | Main
197 | Assembly
198 |
199 | Path
200 | Classes/Modules/Main/Assembly/MainAssembly.swift
201 |
202 |
203 | Classes/Modules/Main/View/MainView.swift
204 |
205 | Group
206 |
207 | Classes
208 | Modules
209 | Main
210 | View
211 |
212 | Path
213 | Classes/Modules/Main/View/MainView.swift
214 |
215 |
216 | Classes/Modules/Main/ViewState/MainViewState.swift
217 |
218 | Group
219 |
220 | Classes
221 | Modules
222 | Main
223 | ViewState
224 |
225 | Path
226 | Classes/Modules/Main/ViewState/MainViewState.swift
227 |
228 |
229 | Classes/Modules/Main/Interactor/MainInteractor.swift
230 |
231 | Group
232 |
233 | Classes
234 | Modules
235 | Main
236 | Interactor
237 |
238 | Path
239 | Classes/Modules/Main/Interactor/MainInteractor.swift
240 |
241 |
242 | Classes/Modules/Main/Presenter/MainPresenter.swift
243 |
244 | Group
245 |
246 | Classes
247 | Modules
248 | Main
249 | Presenter
250 |
251 | Path
252 | Classes/Modules/Main/Presenter/MainPresenter.swift
253 |
254 |
255 | Classes/Modules/Main/Router/MainRouter.swift
256 |
257 | Group
258 |
259 | Classes
260 | Modules
261 | Main
262 | Router
263 |
264 | Path
265 | Classes/Modules/Main/Router/MainRouter.swift
266 |
267 |
268 |
269 |
270 | Classes/Library/Swilby/Assembly.swift
271 |
272 | Group
273 |
274 | Classes
275 | Library
276 | Swilby
277 |
278 | Path
279 | Classes/Library/Swilby/Assembly.swift
280 |
281 |
282 |
283 | Classes/Library/Swilby/AssemblyFactory.swift
284 |
285 | Group
286 |
287 | Classes
288 | Library
289 | Swilby
290 |
291 | Path
292 | Classes/Library/Swilby/AssemblyFactory.swift
293 |
294 |
295 | Classes/Library/Swilby/DependencyContainer.swift
296 |
297 | Group
298 |
299 | Classes
300 | Library
301 | Swilby
302 |
303 | Path
304 | Classes/Library/Swilby/DependencyContainer.swift
305 |
306 | Classes/Library/Swilby/ObjectKey.swift
307 |
308 | Group
309 |
310 | Classes
311 | Library
312 | Swilby
313 |
314 | Path
315 | Classes/Library/Swilby/ObjectKey.swift
316 |
317 | Classes/Library/Swilby/StrongBox.swift
318 |
319 | Group
320 |
321 | Classes
322 | Library
323 | Swilby
324 |
325 | Path
326 | Classes/Library/Swilby/StrongBox.swift
327 |
328 | Classes/Library/Swilby/WeakBox.swift
329 |
330 | Group
331 |
332 | Classes
333 | Library
334 | Swilby
335 |
336 | Path
337 | Classes/Library/Swilby/WeakBox.swift
338 |
339 | Classes/Library/Swilby/WeakContainer.swift
340 |
341 | Group
342 |
343 | Classes
344 | Library
345 | Swilby
346 |
347 | Path
348 | Classes/Library/Swilby/WeakContainer.swift
349 |
350 |
351 |
352 |
353 |
354 |
--------------------------------------------------------------------------------
/README-RU.md:
--------------------------------------------------------------------------------
1 | ## Overview
2 |
3 | SwiftUI Viper Architecture - The development paradigm of clean, testable code and modular iOS applications.
4 |
5 | This repository contains Xcode templates for quickly creating a project, modules, and services.
6 |
7 | * [Viper](#viper)
8 | + [Introduction](#introduction)
9 | + [SwiftUI Viper Architecture](#swiftUI-viper-architecture)
10 | * [Installation](#installation)
11 | * [Requirements](#requirements)
12 | * [Example project](#example-project)
13 | * [Usage](#usage)
14 | + [Create a new Project](#create-a-new-project)
15 | + [Create a new Module](#create-a-new-module)
16 | + [Create a new Service](#create-a-new-service)
17 | * [Author](#author)
18 | * [License](#license)
19 | * [Special Thanks](#special-thanks)
20 |
21 |
22 | ## Viper
23 |
24 | ### Introduction
25 |
26 | VIPER (View, Interactor, Presenter, Entity, Router) — это архитектурный паттерн для построения приложений. В SwiftUI, этот паттерн не так распространен как в UIKit, но его все еще можно использовать для организации кода, если прибегнуть к маленькой хитрости и добавить такую сущность как ViewState.
27 |
28 | Для чего нужен ViewState и на какой концепции он основан ?
29 |
30 | ViewState (состояние представления) аналогичен @IBOutlet свойствам и данным, хранящимся в viewController, но в новой концепции с использованием @Published свойств и view.
31 |
32 | Давайте проясним это с помощью аналогии:
33 |
34 | #### Storyboard и ViewController:
35 | - Storyboard представляет собой визуальное представление пользовательского интерфейса, в котором вы размещаете элементы интерфейса, такие как кнопки, текстовые поля и др. Он отвечает за организацию и расположение элементов.
36 | - ViewController - это объект, управляющий взаимодействием между данными и интерфейсом. Он содержит логику, которая обрабатывает ввод пользователя, обновляет представление и работает с данными.
37 | #### View и ViewState:
38 | - View представляет собой базовый строительный блок в пользовательском интерфейсе. Это абстракция, которая отображает часть пользовательского интерфейса, такую как кнопка, текстовое поле, изображение и т.д.
39 | - ViewState - это абстракция, представляющая собой состояние представления в Swift. Она содержит данные, необходимые для отображения текущего состояния интерфейса. Это может быть, например, текущий текст в текстовом поле, выбранный элемент в таблице и т.д.
40 |
41 | ### SwiftUI Viper Architecture:
42 |
43 | #### View:
44 | - Отвечает за отображение данных пользователю и взаимодействие с пользователем.
45 | - Обрабатывает пользовательский ввод и передает его презентеру для обработки.
46 |
47 | #### ViewState:
48 | - Контролирует взаимодействие между Presenter и View.
49 | - Отвечает за хранение отображаемых данных в пользовательском интерфейсе.
50 | - Принимает пользовательский ввод от представления и преобразует в команды для презентера.
51 |
52 | #### Interactor:
53 | - Содержит бизнес-логику и правила для обработки данных.
54 | - Отвечает за запросы к хранилищу данных (например, базе данных, сети) и их обработку перед представлением.
55 | - Не содержит кода, связанного с отображением или интерфейсом пользователя.
56 |
57 | #### Presenter:
58 | - Отвечает за обработку данных от интерактора и подготовку их для отображения в пользовательском интерфейсе.
59 | - Контролирует взаимодействие между интерактором и представлением.
60 | - Принимает пользовательский ввод от представления, обрабатывает его и преобразует в команды для интерактора.
61 |
62 | #### Entity (Model):
63 | - Представляет собой объекты данных, которые используются в приложении.
64 | - Обычно является простыми объектами данных без методов, содержащими только свойства.
65 |
66 | #### Router:
67 | - Отвечает за навигацию между экранами приложения.
68 | - Решает, какой экран должен быть показан в ответ на определенные действия пользователя.
69 |
70 | ## Installation
71 |
72 | Only need execute this command in terminal:
73 |
74 | ```swift
75 | swift install.swift
76 | ```
77 |
78 | ## Requirements
79 |
80 | * Xcode 10+
81 | * Swift 4.2+
82 |
83 | ## Example project
84 |
85 | [Download](https://github.com/maukur/SwiftUI-Viper-Example/) example project built on the basis of this paradigm.
86 |
87 |
88 | ## Usage
89 |
90 | ### Create a new Project
91 |
92 | ```swift
93 | Open Xcode
94 | File > New > Project or press shortcuts ⇧⌘N
95 | Choice VIPER Architecture
96 | Profit! 🎉
97 | ```
98 | #### Project structure
99 | ```swift
100 | ┌── ApplicationViewBuilder.swift
101 | ├── RootApp.swift
102 | ├── RootView.swift
103 | └── Classes
104 | ├── Modules
105 | │ └── Main
106 | │ ├── Assembly
107 | │ │ └── MainAssembly.swift
108 | │ ├── Contracts
109 | │ │ └── MainContracts.swift
110 | │ ├── Interactor
111 | │ │ └── MainInteractor.swift
112 | │ ├── Presenter
113 | │ │ └── MainPresenter.swift
114 | │ ├── Router
115 | │ │ └── MainRouter.swift
116 | │ ├── View
117 | │ │ └── MainView.swift
118 | │ └── ViewState
119 | │ └── MainViewState.swift
120 | ├── Services
121 | │ └── NavigationService
122 | │ ├── NavigationAssembly.swift
123 | │ ├── NavigationService.swift
124 | │ └── NavigationServiceType.swift
125 | ├── Architecture
126 | │ ├── InteractorProtocol.swift
127 | │ ├── PresenterProtocol.swift
128 | │ ├── RouterProtocol.swift
129 | │ └── ViewStateProtocol.swift
130 | └── Library
131 | └── Swilby
132 | ├── Assembly.swift
133 | ├── AssemblyFactory.swift
134 | ├── DependencyContainer.swift
135 | ├── ObjectKey.swift
136 | ├── StrongBox.swift
137 | ├── WeakBox.swift
138 | └── WeakContainer.swift
139 | ```
140 |
141 | ### Create a new Module
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | ```swift
150 | Open Xcode Project
151 | Select Modules in Xcode Project Navigator
152 | Create new file
153 | File > New > File... or press shortcuts ⌘N
154 | Choice Module or Service
155 | Enter Name
156 | After you have created a Module you need to remove the reference on the folder
157 | Highlight the Folder in the Xcode Project Navigator
158 | Press Backspace Key
159 | Press "Remove Reference" in the alert window
160 | Now you need to return your Folder to the project.
161 | Drag the Folder from the Finder to the Xcode project
162 | Profit! 🎉
163 | ```
164 |
165 | #### Module structure
166 | You can use different modules in one project based on the complexity of your screen.
167 | One screen - one module.
168 |
169 | All your modules should be in the "Modules" folder along the path "Classes/Assemblys/Modules"
170 |
171 | ```swift
172 | ┌── Assembly
173 | ├── Contracts
174 | ├── Interactor
175 | ├── Presenter
176 | ├── Router
177 | ├── View
178 | └── ViewState
179 | ```
180 | #### Setup Modules
181 | Important! You need to add your Service, Module to the DI Container in the RootApp.swift
182 |
183 | ```swift
184 | container.apply(MainAssembly.self)
185 | // add your module here
186 | ```
187 |
188 | ### Create a new Service
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 | ```swift
197 | Open Xcode Project
198 | Select Services in Xcode Project Navigator
199 | Create new file
200 | File > New > File... or press shortcuts ⌘N
201 | Choice Module or Service
202 | Enter Name (if you want to create "Service" you must specify at the end of the name "Service" for example - NetworkService or SettingsService)
203 | After you have created a Service you need to remove the reference on the folder
204 | Highlight the Folder in the Xcode Project Navigator
205 | Press Backspace Key
206 | Press "Remove Reference" in the alert window
207 | Now you need to return your Folder to the project.
208 | Drag the Folder from the Finder to the Xcode project
209 | Profit! 🎉
210 | ```
211 | #### Service structure
212 | Each service is engaged in its own business: the authorization service works with authorization, the user service with user data and so on. A good rule (a specific service works with one type of entity) is separation from the server side into different path: /auth, /user, /settings, but this is not necessary.
213 |
214 | All your services should be in the "Services" folder along the path "Classes/Assemblys/Services"
215 |
216 | You can learn more about the principle of developing SoA from [wikipedia](https://en.wikipedia.org/wiki/Service-oriented_architecture)
217 |
218 | ```swift
219 | ┌── ServiceAssembly
220 | ├── ServiceProtocol
221 | └── ServiceImplementation
222 | ```
223 | #### Setup Services
224 | Important! You need to add your Service, Module to the DI Container in the RootApp.swift
225 |
226 | ```swift
227 | container.apply(NavigationServiceAssembly.self)
228 | // add your service here
229 | ```
230 |
231 |
232 | ## Author
233 |
234 | 🧑🏻💻 Artem Tishchenko [Personal Blog](https://www.linkedin.com/in/tim-tis/)
235 |
236 |
237 | ## License
238 |
239 | MIT License
240 |
241 | Copyright (c) 2023 Artem Tishchenko
242 |
243 | Permission is hereby granted, free of charge, to any person obtaining a copy
244 | of this software and associated documentation files (the "Software"), to deal
245 | in the Software without restriction, including without limitation the rights
246 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
247 | copies of the Software, and to permit persons to whom the Software is
248 | furnished to do so, subject to the following conditions:
249 |
250 | The above copyright notice and this permission notice shall be included in all
251 | copies or substantial portions of the Software.
252 |
253 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
254 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
255 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
256 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
257 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
258 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
259 | SOFTWARE.
260 |
261 | BASED ON: [Core iOS Application Architecture](https://github.com/bartleby/Core-iOS-Application-Architecture)
262 |
263 | ## Special thanks
264 | * Artem Korenev - [LinkedIn](https://www.linkedin.com/in/artem-korenev-42b320243/)
265 | * Aleksei Artemev - [iDevs.io](https://idevs.io)
266 | * CustomerTimes iOS team - [Customertimes.com](https://customertimes.com/)
267 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Overview
2 |
3 | SwiftUI Viper Architecture - The development paradigm of clean, testable code and modular iOS applications.
4 |
5 | This repository contains Xcode templates for quickly creating a project, modules, and services.
6 |
7 | * [Viper](#viper)
8 | + [Introduction](#introduction)
9 | + [SwiftUI Viper Architecture](#swiftUI-viper-architecture)
10 | * [Installation](#installation)
11 | * [Requirements](#requirements)
12 | * [Example project](#example-project)
13 | * [Usage](#usage)
14 | + [Create a new Project](#create-a-new-project)
15 | + [Create a new Module](#create-a-new-module)
16 | + [Create a new Service](#create-a-new-service)
17 | * [Author](#author)
18 | * [License](#license)
19 | * [Special Thanks](#special-thanks)
20 |
21 |
22 | ## Viper
23 |
24 | ### Introduction
25 |
26 | VIPER (View, Interactor, Presenter, Entity, Router) is an architectural pattern for building applications. In SwiftUI, this pattern isn't as commonly used as in UIKit, but it can still be employed for code organization with a little trickery, by introducing an entity like ViewState.
27 |
28 | What is ViewState used for and what concept is it based on?
29 |
30 | ViewState (View State) is similar to @IBOutlet properties and data stored in a viewController, but in the new concept, it utilizes @Published properties and views.
31 |
32 | Let's clarify this with an analogy:
33 |
34 | #### Storyboard and ViewController:
35 | - Storyboard is a visual representation of the user interface where you place interface elements such as buttons, text fields, and others. It is responsible for organizing and positioning these elements.
36 | - ViewController is an object that manages the interaction between data and the interface. It contains the logic that handles user input, updates the view, and works with data.
37 | #### View and ViewState:
38 | - View is the basic building block in the user interface. It's an abstraction that represents a part of the user interface, such as a button, text field, image, etc.
39 | - ViewState is an abstraction representing the state of a view in Swift. It contains the data needed to display the current state of the interface. This could be, for example, the current text in a text field, the selected item in a table, etc.
40 |
41 | ### SwiftUI Viper Architecture:
42 |
43 | #### View:
44 | - Responsible for presenting data to the user and interacting with them.
45 | - Handles user input and passes it to the presenter for processing.
46 |
47 | #### ViewState:
48 | - Controls the interaction between the Presenter and the View.
49 | - Responsible for storing displayed data in the user interface.
50 | - Receives user input from the view and translates it into commands for the presenter.
51 |
52 | #### Interactor:
53 | - Contains the business logic and rules for processing data.
54 | - Handles requests to the data store (e.g., database, network) and processes them before presenting them.
55 | - Does not contain code related to presentation or user interface.
56 |
57 | #### Presenter:
58 | - Responsible for processing data from the interactor and preparing it for display in the user interface.
59 | - Controls the interaction between the interactor and the view.
60 | - Receives user input from the view, processes it, and converts it into commands for the interactor.
61 |
62 | #### Entity (Model):
63 | - Represents data objects used in the application.
64 | - Typically, they are simple data objects without methods, containing only properties.
65 |
66 | #### Router:
67 | - Handles navigation between screens in the application.
68 | - Decides which screen should be shown in response to specific user actions.
69 |
70 | ## Installation
71 |
72 | Only need execute this command in terminal:
73 |
74 | ```swift
75 | swift install.swift
76 | ```
77 |
78 | ## Requirements
79 |
80 | * Xcode 14+
81 | * Swift 5.7+
82 |
83 | ## Example project
84 |
85 | [Download](https://github.com/maukur/SwiftUI-Viper-Example/) example project built on the basis of this paradigm.
86 |
87 |
88 | ## Usage
89 |
90 | ### Create a new Project
91 |
92 | ```swift
93 | Open Xcode
94 | File > New > Project or press shortcuts ⇧⌘N
95 | Select VIPER Architecture
96 | Profit! 🎉
97 | ```
98 | #### Project structure
99 | ```swift
100 | ┌── ApplicationViewBuilder.swift
101 | ├── RootApp.swift
102 | ├── RootView.swift
103 | └── Classes
104 | ├── Modules
105 | │ └── Main
106 | │ ├── Assembly
107 | │ │ └── MainAssembly.swift
108 | │ ├── Contracts
109 | │ │ └── MainContracts.swift
110 | │ ├── Interactor
111 | │ │ └── MainInteractor.swift
112 | │ ├── Presenter
113 | │ │ └── MainPresenter.swift
114 | │ ├── Router
115 | │ │ └── MainRouter.swift
116 | │ ├── View
117 | │ │ └── MainView.swift
118 | │ └── ViewState
119 | │ └── MainViewState.swift
120 | ├── Services
121 | │ └── NavigationService
122 | │ ├── NavigationAssembly.swift
123 | │ ├── NavigationService.swift
124 | │ └── NavigationServiceType.swift
125 | ├── Architecture
126 | │ ├── InteractorProtocol.swift
127 | │ ├── PresenterProtocol.swift
128 | │ ├── RouterProtocol.swift
129 | │ └── ViewStateProtocol.swift
130 | └── Library
131 | └── Swilby
132 | ├── Assembly.swift
133 | ├── AssemblyFactory.swift
134 | ├── DependencyContainer.swift
135 | ├── ObjectKey.swift
136 | ├── StrongBox.swift
137 | ├── WeakBox.swift
138 | └── WeakContainer.swift
139 | ```
140 |
141 | ### Create a new Module
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | ```swift
150 | Open Xcode Project
151 | Select Modules in Xcode Project Navigator
152 | Create new file
153 | File > New > File... or press shortcuts ⌘N
154 | Select Module or Service
155 | Enter Name
156 | After you have created a Module you need to remove the reference on the folder
157 | Highlight the Folder in the Xcode Project Navigator
158 | Press Backspace Key
159 | Press "Remove Reference" in the alert window
160 | Now you need to return your Folder to the project.
161 | Drag the Folder from the Finder to the Xcode project
162 | Profit! 🎉
163 | ```
164 |
165 | #### Module structure
166 | You can use different modules in one project based on the complexity of your screen.
167 | One screen - one module.
168 |
169 | All your modules should be in the "Modules" folder along the path "Classes/Assemblys/Modules"
170 |
171 | ```swift
172 | ┌── Assembly
173 | ├── Contracts
174 | ├── Interactor
175 | ├── Presenter
176 | ├── Router
177 | ├── View
178 | └── ViewState
179 | ```
180 | #### Setup Modules
181 | Important! You need to add your Service, Module to the DI Container in the RootApp.swift
182 |
183 | ```swift
184 | container.apply(MainAssembly.self)
185 | // add your module here
186 | ```
187 |
188 | ### Create a new Service
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 | ```swift
197 | Open Xcode Project
198 | Select Services in Xcode Project Navigator
199 | Create new file
200 | File > New > File... or press shortcuts ⌘N
201 | Select Module or Service
202 | Enter Name (if you want to create "Service" you must specify at the end of the name "Service" for example - NetworkService or SettingsService)
203 | After you have created a Service you need to remove the reference on the folder
204 | Highlight the Folder in the Xcode Project Navigator
205 | Press Backspace Key
206 | Press "Remove Reference" in the alert window
207 | Now you need to return your Folder to the project.
208 | Drag the Folder from the Finder to the Xcode project
209 | Profit! 🎉
210 | ```
211 | #### Service structure
212 | Each service is engaged in its own business: the authorization service works with authorization, the user service with user data and so on. A good rule (a specific service works with one type of entity) is separation from the server side into different path: /auth, /user, /settings, but this is not necessary.
213 |
214 | All your services should be in the "Services" folder along the path "Classes/Assemblys/Services"
215 |
216 | You can learn more about the principle of developing SoA from [wikipedia](https://en.wikipedia.org/wiki/Service-oriented_architecture)
217 |
218 | ```swift
219 | ┌── ServiceAssembly
220 | ├── ServiceProtocol
221 | └── ServiceImplementation
222 | ```
223 | #### Setup Services
224 | Important! You need to add your Service, Module to the DI Container in the RootApp.swift
225 |
226 | ```swift
227 | container.apply(NavigationServiceAssembly.self)
228 | // add your service here
229 | ```
230 |
231 |
232 | ## Author
233 |
234 | 🧑🏻💻 Artem Tishchenko [Personal Blog](https://www.linkedin.com/in/tim-tis/)
235 |
236 |
237 | ## License
238 |
239 | MIT License
240 |
241 | Copyright (c) 2023 Artem Tishchenko
242 |
243 | Permission is hereby granted, free of charge, to any person obtaining a copy
244 | of this software and associated documentation files (the "Software"), to deal
245 | in the Software without restriction, including without limitation the rights
246 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
247 | copies of the Software, and to permit persons to whom the Software is
248 | furnished to do so, subject to the following conditions:
249 |
250 | The above copyright notice and this permission notice shall be included in all
251 | copies or substantial portions of the Software.
252 |
253 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
254 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
255 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
256 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
257 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
258 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
259 | SOFTWARE.
260 |
261 | BASED ON: [Core iOS Application Architecture](https://github.com/bartleby/Core-iOS-Application-Architecture)
262 |
263 | ## Special thanks
264 | * Artem Korenev - [LinkedIn](https://www.linkedin.com/in/artem-korenev-42b320243/)
265 | * Aleksei Artemev - [iDevs.io](https://idevs.io)
266 | * CustomerTimes iOS team - [Customertimes.com](https://customertimes.com/)
267 |
268 | ## ☕️ Donate:
269 | If you find this repository useful, you can thank me
270 |
271 | [](https://www.buymeacoffee.com/x68mf5jw4yl)
272 |
273 | Or give a star the repository
274 |
275 | [](https://github.com/maukur/SwiftUI-Viper-Architecture)
276 |
277 |
278 |
--------------------------------------------------------------------------------
/install.swift:
--------------------------------------------------------------------------------
1 | //
2 | // main.swift
3 | // Core-iOS-Application-Architecture
4 | //
5 | // Created by Artem Tishchenko on 11/11/2023
6 | // Copyright All rights reserved.
7 | // Special thanks Alexey Artemev from iDevs
8 |
9 | import Foundation
10 |
11 | func printInConsole(_ message:Any){
12 | print("==> \(message)")
13 | }
14 |
15 | let fileManager = FileManager.default
16 | let homeDirectoryForCurrentUser = fileManager.homeDirectoryForCurrentUser.path
17 | let currentPath = fileManager.currentDirectoryPath
18 | let templatePath = "\(homeDirectoryForCurrentUser)/Library/Developer/Xcode/Templates/"
19 |
20 | let projectDir = "Project Templates/"
21 | let moduleDir = "File Templates/"
22 |
23 | let sourceProjectPath = "\(currentPath)/\(projectDir)"
24 | let sourceModulePath = "\(currentPath)/\(moduleDir)"
25 |
26 | let projectTemplatePath = "\(templatePath)/\(projectDir)"
27 | let moduleTemplatePath = "\(templatePath)/\(moduleDir)"
28 |
29 | func makeDir(path: String) {
30 | try? fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
31 | }
32 |
33 | func moveTemplate(fromPath: String, toPath: String) throws {
34 | let toURL = URL(fileURLWithPath:toPath)
35 | try _ = fileManager.removeItem(at: toURL)
36 | try _ = fileManager.copyItem(atPath: fromPath, toPath: toPath)
37 | }
38 |
39 |
40 | do {
41 | printInConsole("Install Project templates at \(projectTemplatePath)")
42 | makeDir(path: projectTemplatePath)
43 | try moveTemplate(fromPath: sourceProjectPath, toPath: projectTemplatePath)
44 |
45 | printInConsole("Install Module templates at \(moduleTemplatePath)")
46 | makeDir(path: moduleTemplatePath)
47 | try moveTemplate(fromPath: sourceModulePath, toPath: moduleTemplatePath)
48 |
49 | printInConsole("All templates have been successfully installed.")
50 | } catch let error as NSError {
51 | printInConsole("Could not install the templates. Reason: \(error.localizedFailureReason ?? "")")
52 | }
--------------------------------------------------------------------------------