In this article I want to share my experience with building dependencies by using Carthage. First of all, Carthage shines with simplicity. It's very simple to start using some external dependency in a Xcode project just by adding proper line into Cartfile
and running carthage update
. But as we all know, life is brutal and sometimes we need to consider more complex examples.
Let's assume there is a team of iOS developers. Tony, John and Keith are working iOS application with ~15 popular dependencies like Alamofire, Kingfisher, ReactiveCocoa etc…
carthage update
or one framework if you run it for single dependency. At the begining of the project we do that very often. Team is looking for a solution to speed this up too.There is no free lunch… I agree, but at the same time I believe sometimes it's worth to spend some time for improving your everyday tools. I've spend a lot of time experimenting with dependency managers, caching their artifacts etc… Let me tell you about three popular solutions of maintaining carthage frameworks.
Before you begin
Let the story begin … Tony is a team leader and he decided to use Carthage as a dependency manager. He defined some rules for other software developers when working with external frameworks:
.gitignore
and include Carthage/Checkouts
in repository,carthage bootstrap
(rebuild all dependencies). CI would need to run that for each pipeline,carthage update ReactiveSwift
.Those are very simple rules, but what about their pros and cons?
0$
per month)Let's compare this solution to problems that might occur:
To sum up: their biggest problem in this approach is time. The only fully solved problem is repository size. CI build time would be very long and would increase proportionally with number of dependencies. As you can see there is still a lot to improve. Let's try something different…
Some day one of the developers - John - found that github allows storing large files in their LFS (large file system). He noticed that this might be great oportunity to start including pre-compiled frameworks in git repo, but still keep it small. He modified Tony's rules a little:
Carthage/Build
and Carthage/Checkouts
to .gitignore
,carthage bootstrap
(rebuild all dependencies), but you need to extract frameworks from LFS,carthage update ReactiveSwift
, some extra work is needed - you need to archive those frameworks, zip them and upload to git-lfs (add to .gitattributes
),This solution is much more complicated especially because of extra steps with zipping and uploading frameworks. There is a great article that describes this and offers some simple Makefile
to automate this step.
5$
per month after reaching 1GB on LFS)Let's compare this solution to problems defined at the begining of the article:
After all I think that this looks much better! Having fast clean builds is much more important for most teams than possibility to use different Xcodes between developers. They are still able to have differen versions installed and only switch between them for specific projects. I believe 5$
per month for LFS is not a big deal. So it's much a better (and difficult) solution, but there is still some room for improvement …
So, time for Keith to show up. He appreciate other developers' research, but Keith cares a lot about team work. He thought that maybe it's possible to share different versions of pre-compiled frameworks compiled by different versions of swift compiler between different projects? That's a lot of variety, but fortunately there is a tool for that! It's called Rome
. I highly encourage you to take a look at documentation on github. In general this tool shares frameworks using Amazon S3 Bucket. Again, Keith changed the rules:
Carthage/Build
and Carthage/Checkouts
to .gitignore
,carthage bootstrap
(rebuild all dependencies) but you need download them from Amazon S3,carthage update ReactiveSwift --no-build
and then try to download it from Amazon and if it does not exist build it and upload,RepositoryMap
which tells Rome which dependencies compiled by Carthage you use.By using some very simple helper script those rules seem to be almost as simple as the one from Naive approach
section. I'm very impressed by this tool especially by the relation between amount of required setup work and given benefits. Let's see what are pros and cons of this solution:
RepositoryMap
$0.023 / GB
)And comparison with an obvious result:
In my opinion this solution is the one that saves you a lot of hours spent on dependency management. Of course sometimes you'll need to build on your machine / CI but you have to guarantee that this job will be reused.
So you already noticed that I believe Rome is the best solution for now and I highly encourage you to use this, but the story shows that there is always something we can improve. You should experiment with different approaches and pick the one that solves your problems. I believe that during reading a story of Tony, John and Keith, you noticed more than just the best friend of Carthage (Rome). It's about team work and improving team workflow. Those guys tried all the time to solve the problem of working together (with CI as a virtual fourth team member) and finally one of them found a solution that fits ideally to their needs!