Who is this article for? It is for individuals who are either starting a project without a project file or those who are already in progress but may be facing challenges with maintenance. It may also appeal to anyone with an interest in the topic At some point, everyone encounters a situation where a project needs to be divided into modules, each with its own scope and associated elements. There are several methods for dividing and maintaining a project. However, I would like to introduce a helpful tool that has aided me in the past For those who require an example in the form of code, I will provide a link to the repository that contains an example related to this article. The example can be found at https://github.com/Ze8c/TuistExample/tree/main So, let's get started with Tuist, following the instructions provided in this guide: https://docs.tuist.io/tutorial/get-started curl -Ls https://install.tuist.io | bash Once is installed, you can generate a project directly from the console Tuist tuist init --platform ios and make tuist generate After initiating the project generation process, the project will automatically open in XCode You'll be able to see the basic project structure and hierarchy. Close and reopen the project for editing XCode tuist edit will open with the settings of our project displayed XCode As you can observe, the settings are based on and resemble the ( ), which makes it easier to get started Swift Swift Package Manager SPM While Tuist allows for more flexible customization of the project, it often leads to code duplication in most cases. To enhance development speed and maintainability, you can utilize the factory pattern and create a more structured approach. Additionally, using flexible s to build a factory can be aesthetically pleasing Swift enum The project setup is divided into subscopes, which is crucial when creating new files within the project setup Important! Let's begin by creating a structure for the " " scope, which will manage certain application settings ProjectDescriptionHelpers public struct AppConfig { public let marketingVersion: String public let buildVersion: InfoPlist.Value public let organizationName: String public let bunldePrefix: String public let iosTargetVersion: String public init( marketingVersion: String, buildVersion: InfoPlist.Value, organizationName: String, bunldePrefix: String, iosTargetVersion: String ) { self.marketingVersion = marketingVersion self.buildVersion = buildVersion self.organizationName = organizationName self.bunldePrefix = bunldePrefix self.iosTargetVersion = iosTargetVersion } } We'll start by creating a small extension for strings extension String { var capitalizingFirstLetter: String { return prefix(1).uppercased() + dropFirst() } } Next, we'll create scopes within the application. These scopes are folders that logically group modules together. For example, we'll create " " and " " scopes, which will contain components related to flow management and core elements such as our design system flow core public enum AppScope: String { case root case core case flow public var folder: String { rawValue.capitalizingFirstLetter } } If the project has external dependencies, we need to add an additional target called " " to the project configuration Dependencies We'll also include a dependency configuration entity to make the process more convenient. This entity will assist us in adding the dependencies to the project enum AppDependencie { case target(AppTarget) case external(ExternalDependencie) } extension AppDependencie { var convert: TargetDependency { switch self { case let .target(item): return .target(name: item.name) case let .external(item): return .external(name: item.name) } } } public enum ExternalDependencie: CaseIterable { case kingfisher } extension ExternalDependencie { public var package: Package { switch self { case .kingfisher: return .remote( url: "https://github.com/onevcat/Kingfisher.git", requirement: .exact("7.6.2") ) } } var name: String { switch self { case .kingfisher: return "Kingfisher" } } } Finally, we'll add the external dependencies to the project in scope “ ” Dependencies let packageManager = SwiftPackageManagerDependencies(dependencies: ExternalDependencie.allCases) let dependencies = Dependencies( swiftPackageManager: packageManager, platforms: [.iOS] ) extension SwiftPackageManagerDependencies { init(dependencies: [ExternalDependencie]) { self.init(dependencies.map(\.package)) } } Once we have defined the scopes, we'll organize our targets and set up their dependencies. It's important to keep track of dependencies to avoid circular dependencies between modules public enum AppTarget: String, CaseIterable { case main case tuistExampleKit case tuistExampleUI } extension AppTarget { public var id: String { rawValue } public var name: String { id.capitalizingFirstLetter } public var scope: AppScope { switch self { case .main: return .root case .tuistExampleKit: return .core case .tuistExampleUI: return .flow } } private var dependencies: [AppDependencie] { switch self { case .main: return [ .target(.tuistExampleKit), .target(.tuistExampleUI) ] case .tuistExampleKit: return [] case .tuistExampleUI: return [ .external(.kingfisher), .target(.tuistExampleKit) ] } } } extension AppTarget { public var target: Target { switch self { case .main: return mainTarget(AppSetup.appConfig) case .tuistExampleKit, .tuistExampleUI: return makeBaseTarget(AppSetup.appConfig) } } private func mainTarget(_ config: AppConfig) -> Target { Target( name: "Main", platform: .iOS, product: .app, bundleId: config.bunldePrefix + "superapp", deploymentTarget: .iOS(targetVersion: config.iosTargetVersion, devices: .iphone), infoPlist: .extendingDefault(with: [ "CFBundleShortVersionString": InfoPlist.Value(stringLiteral: config.marketingVersion), "CFBundleVersion": config.buildVersion, "UIMainStoryboardFile": "", "UILaunchStoryboardName": "LaunchScreen", "CFBundleIconName": "AppIcon", "CFBundleDisplayName": "TestName", "UISupportedInterfaceOrientations": ["UIInterfaceOrientationPortrait"], "UISupportedInterfaceOrientations~ipad": ["UIInterfaceOrientationPortrait", "UIInterfaceOrientationPortraitUpsideDown"], "UIUserInterfaceStyle": "Light", ]), sources: ["Targets/Main/Sources/**"], resources: ["Targets/Main/Resources/**"], dependencies: dependencies.map(\.convert), settings: Settings.settings( base: SettingsDictionary() .bitcodeEnabled(false) .marketingVersion(config.marketingVersion) .merging(["ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon"]) , debug: Configuration.debug(name: .debug).settings, release: Configuration.release(name: .release).settings, defaultSettings: DefaultSettings.essential(excluding: []) ) ) } private func makeBaseTarget(_ config: AppConfig) -> Target { createFolderIfNeeded() let pathSource = "Targets/\(scope.folder)/\(name)/Sources" let pathResource = "Targets/\(scope.folder)/\(name)/Resources" let doesResourceFolderExist = FileManager.default.fileExists(atPath: pathResource, isDirectory: nil) return Target( name: name, platform: .iOS, product: .framework, bundleId: config.bunldePrefix + id, deploymentTarget: .iOS(targetVersion: config.iosTargetVersion, devices: .iphone), infoPlist: .extendingDefault(with: [:]), sources: ["\(pathSource)/**"], resources: doesResourceFolderExist ? ["\(pathResource)/**"] : [], dependencies: dependencies.map(\.convert), settings: Settings.settings( base: SettingsDictionary().bitcodeEnabled(false), debug: Configuration.debug(name: .debug).settings, release: Configuration.release(name: .release).settings, defaultSettings: DefaultSettings.essential(excluding: []) ) ) } private func createFolderIfNeeded() { let sourcesPath = "Targets/\(scope.folder)/\(name)/Sources" if !FileManager.default.fileExists(atPath: sourcesPath) { do { try FileManager.default.createDirectory( atPath: sourcesPath, withIntermediateDirectories: true, attributes: nil ) FileManager.default.createFile(atPath: "\(sourcesPath)/Placeholder.swift", contents: nil) } catch { print("Create dir error", error.localizedDescription) } } } } With all the preparations complete, we'll make some slight adjustments to the property that handles the entire project let project = Project( name: "TuistExample", organizationName: AppSetup.appConfig.organizationName, targets: AppTarget.allCases.map(\.target), resourceSynthesizers: [ .strings(), .assets(), ] ) That's all for now. You can close the project configuration, press in the console, and remember to download all external dependencies using the appropriate command before generating the project ctrl + C tuist fetch Regenerate the project tuist generate Once the project generation is complete, you can enjoy the results.