Skip to main content

Project structure

Project structure

For features (screens), all associated code should be in an enclosing folder, e.g.

/Feature
|- FeatureView.swift
|- FeatureVC.swift
|- FeatureMVProtocol.swift
|- FeatureVM.swift

The idea is to have code bundled as much as possible, so e.g. removing a screen required deleting a directory only (and as little as possible code changes).

Shared views, Services and coordinators are stored in a separate folder structure, but also bundled together (as for services, possibly in another module as well).

Targets / Swift Packages

For larger projects (and smaller if it makes sense), it is recommendable to break layers into frameworks to further prevent tight coupling and separate concerns.

You can do that by adding a new Framework target to the project, or add it via Swift Package Manager as a local package.

To add a new package dependency, there are a number of online resources with details, but general steps are: Add new package to project by File > New Package. Name the package and add it to the project directory, making sure Add to and Group both target the project. This will create a local Library with Package.swift definition file. To target appropriate platforms, make sure to add e.g platforms: [.iOS(.v15)], definition to it. Failing to do so will e.g. prevent you from using Structured Concurrency if you don't target at least iOS 13 and newer versions. Then add more targets (and associate test targets) as you see fit, making sure the Target name is accompanied by the matching folder name in Sources (and Tests) folders. The final step is to add that new Framework to "Frameworks, Libraries and Embedded Content" section of the General tab for the main target.

Both approaches are functionally similar, but adding Swift Package makes extracting to a separate framework easier, and more importantly evades .pbxproj conflicts when adding/changing files in libraries which is almost guaranteed to happen if everything is defined in project.