Dependencies and package managers
CocoaPods
In DECODE, we primarily use CocoaPods for managing dependencies. Dependencies are defined in Podfile which is committed to git, along with Podfile.lock containing exact resolved dependencies.
We strive to have all projects buildable immediately after checkout. For that purpose, all dependencies are included into the git repository. This does increase the size of the repository, but makes sure each of the projects is complete and in a buildable state at all times. To achieve this, when adding the .gitignore file to the project, it does not omit the Pods/ directory (as many gitignore templates do).
This way, after checking out, the developer does not need to perform pod install that would fetch all the CocoaPods for exact versions as defined in the lock file.
When updating particular Pod version, try to update one by one (per need basis), using pod update <pod_name> instead of updating all defined CocoaPods (just pod update). This way it's easier to track versions that are updated and that are kept to a particular version. Also, as updating a particular pod will also update its dependencies, it's easier to track what is a dependency update and what is a general pod update.
Also, it may be prudent to lock the exact versions (or ranges) in Podfile itself, adding another layer of security along with the .lock file.
Finally, along with public CocoaPods, we used private ones (both DECODE and Customer hosted). This includes both custom code created for some purpose or targetting our fork of a particular library/framework used that required some custom changes (especially if it's not to be merged upstream for various reasons).
Swift Package Manager
Swift package manager is being actively improved and gaining traction for dependency management. As such, it is OK to be used as dependency manager after being discussed with the project owner and development team. Bear in mind that currently there is no simple and nice way to keep dependencies added in this manner in git repo, they are fetched after each checkout (and cleaning of the derived data). There is a workaround by forcing derived data to be in the project directory and tweaking the .gitignore file, but that should be used as a last resort.
Please see the Project structure section, but consider using Swift Packages for project modularization as well. This behaves like regular targets, but avoids issues with project.pbxproj merge conflicts when modifying files at the same time.
Other mechanisms
There are other mechanisms of handling dependencies - Carthage, direct embedding etc. Although they are not generally used in DECODE, there are cases when that may be required. It's OK to use any means necessary, provided it's discussed with project owner and team beforehand.