Andrew Jackson is alledged to have said, “If there’s a job that can’t be done by a democrat, let’s abolish the job.” Andrew Jackson was also a lunatic who should probably never be deputized in an argument for anything. Ever.
After many years of having been an Acolyte of Apple, a Cultist of Cupertino, and a Supplicant of Swift, it’s time to finally admit that maybe, just maybe, there are some tools I need that don’t get released at WWDC every year.
Wait, wait, I know I said I was going to talk about tools not released at Dub-Dub. We do have to start here anyway, I need to get on my soap box for just two little things.
Stop installing Xcode through the Mac App store if you have a Apple Developer’s Account. A developer I have an astounding amount of respect for keeps saying “I’m lazy” as though it’s a good thing. And he’s right, it usually is; automating things is almost always the right way to go. Using that logic as justification to install through the Mac App store is wrong1.
Unless you’ve gone out of your way to turn it off, the Mac App store will automatically install new versions of Xcode when they come out. This can be a wonderful thing for most applications. For developers, however, this is an invitation to a broken build. At least until Swift 5 is finally locked down. But even then, it’s not a great practice. In 2015, I went on vacation and the developer tools updated and it broke the build for my team until I got back.
Instead, if you scroll all the way to the bottom of the Developer Downloads you’ll see text that doesn’t even look like a button inviting you to see more downloads (as of 2018).
This is a collection of almost every developer tool Apple has released since 2002. I don’t know what Shake 4.1 is, but I can find it there. I have yet to find a version of Xcode that I’ve needed that I was unable to install from … I still call it ADC, but I’m an old. Once you have it you can install it anywhere you want in your file system.
One of the early things novice Cocoa programmers discover is how to change their default Xcode version. It’s a fantastic skill to have once you have multiple Xcode running. For years, this was me from June to September. I would have the latest production release of Xcode and the current beta. However at some point in the past couple of years I started needing to support an older version as well. Today, I have Xcodes 7.x, 9.x, 10.x all living in harmony on my machine.
Smart People had finally hammered home the need to script my builds (I’m trying to figure out gradle now, more on that in the future).
And some projects require long term support on older Xcode release.
I got turned on to it thanks to the good folks at Slather. Once I did, it changed my whole world. One of the advantages almost every other build system I’ve had the pleasure of working with has is that he build tools version is stored somewhere in settings. You may have noticed that Xcode doesn’t exaclty do that. But then again, we don’t exaclty have an industry standard build tool.
DEVELOPER_DIR allows you to enter a path as an enviornmental variable to
xcodebuild and change which Xcode you’re using. For example, by setting the developer directory I can build using Xcode 9.4 even though Xcode 10.1 is my default.
DEVELOPER_DIR within a build script, you can specify your version of Xcode to use in version control.
This does mean that your team has to agree by convention where to place Xcode, but from that point the builds are no longer subject to the whims of the default Xcode version.
You are using build scripts, right?
brew is another one of those tools that Mac users get turned on to early on. There’s a lot of amazing tools and new ones are constantly being added. I just want to highlight a couple that are invaluable.
GitHub is my git repository host of choice. I like the UI, the personal account price is right, and most of the open source software I use is hosted on it. The one thing I don’t like is using it to set up my project.
After I first discovered GitHub, this is what every project I set up looked like:
- Create a new repository
- Name is something memorable
- Add a README that I swear would be kept up to date this time
- Add the Swift or Objective-C .gitignore file
- Clone the repository
- Edit the .gitignore
- Push my changes
There’s a strong argument to make that I should just update my global .gitignore if I’m modifying every instance of local .gitignore. The reason to not do that is there’s no guarantee that people you end up sharing your code with will have the same global .gitignore, thereby Inviting Problems. This workflow, frankly, also leads to a lot, a lot, of projects on GitHub that never see significant development.
hub is a CLI for GitHub. With is I can do most of the core GitHub operations that I’ve traditionally depended on the browser for, but without ever exiting the command line. Before you balk at the concept, remember, this means GitHub is now scriptable. It means that after pushing to my repo, I can immediately issue a pull request without having to leave my terminal. It also means I can create a new repo from the terminal. It’s fantastic!
The workflow from above now becomes:
- Create my project and decided it’s worth putting in version control
- git init
- Copy in my custom .gitignore
- hub create -p
- Publish my repo to the remote
That last line is going to create a repository on GitHub with the name of the root of my project. The -p tag indicates that it should be a private repository. The repository will be added as a remote to the project. And you’re now in the same state you were in using the initial workflow. Excepte it’s scriptable.
Well formatted code is one of the shibboleths of good engineering and a solid linter can help. Talking with collegues specializing in other languages, they’ve been shocked that Swift doesn’t have one. Especially given Apple’s intense desire for thing that look good as well as perform good. Enter swiftlint. The first tool entry on this list from Realm.
swiftlint has a lot of room to grow, but where it’s come in two short years is amazing. The long and short of it all is that you can add a build script to your run phase that will check your code for style errors. Getting involved in Swift and using it every day, it’s important to develop good programming habits. This is an invalubale tool to enforce those habits and really make you question what it is you’re doing.
It’s not a perfect tool. There’s a lot of available customization, which is poorly documented. To the point where your best bet is reading the source code to figure it out. Also, there are some edge cases that it doesn’t handle well, but the meat of what it does has been fantastic.
The newest tool in my bag is the most difficult to use and the most needed. It also get’s back to that whole “custom .gitignore” thing.
With xcodegen I’ve added
*.xcodeproj to my .gitignore.
Instead, xcodegen rebuilds my Xcode project files wherever I’ve cloned my repo to. This completely eliminates any concern I have with merging project files. Now I can just delete them and regenerate without concern. Additionally, I know exactly what settings I’m overriding.
There’s a learning curve on setting up the structure of the YAML that seems difficult. That, however, is a sunk cost; once it’s done, it’s done.
Coworkers of mine have finally hammered home how gems and Gemfiles work.
For way too long petty prejudices kept me from investigating too hard, but I did finally get there.
By using a Gemfile, the bundler gem will automaticlly install your gems.
From within a directory that descends from a Gemfile
bundle exec will execute the gem of the version correct to the Gemfile.
I dislike Things That Work By Magic, so I took me some time to get over using rvm with hand rolled gemsets. Now that I’m there… none are so faithful as the converted.
You might be saying, “But now you have to say ‘bundle exec’ any time you want to run your gem. Just sudo it.” That’s not the point. The point is… it’s scriptable.
I came to cocoapods late in the game. This was a poor decision. I was arrogant. I regret it. Package managers are wonderful.
Most of what I build should be a framework. Logical collections of model data and their operations? Framework. A particular set of UI? Framework. Apps should be collections of frameworks.
Once you have those frameworks, use cocoapods to published them locally to your system, and then consume them. Don’t do this manually. Even if they’re in the same repo (they shouldn’t be in the same repo, take some time to break that out). This isn’t just a matter of building something quickly now, it’s also about developing good habits to take forward to other projects.
Additionally, and most importantly, it’s scriptable.
So, you’ve started building frameworks. How do you document those frameworks?
You are documenting those frameworks, right?
jazzy is the second magical tool from Realm that I frequently use and is wonderful for publishing documentation on those frameworks, even if it’s just for me. The most likely consumer for your API is you six months from now he’ll thank you for it. jazzy has a friendly, Apple-like documentation format is easy to navigate. You can add developer’s guides for longer explanations. I loved appledoc, but as they never geared up for Swift, this is the logical successor.
It’s used less frequently than the other two, but enough to get mentioned on this list of tools (again, technically) is slather. I use this to give me more granular coverage reports from my unit testing. The setup for slather is in YAML so it’s stored within an easy to version file in your source code. And yes, of course, it’s scriptable.
Adding It All Up
The philosophy of “I’m lazy” is almost always the right way to go in a deterministic system. Getting to a place where you can be lazy, however, is a result of hard work and experience.
The most important lessions of the past two years have been:
- Make it Scriptable
- Store it in Version Control
Doing that makes so much of your life better. This blog post… the first thing I did was write a script to define the layout of the markdown so that I never have to do it again.
Ideally, what I’d like is something that would:
- Make a directory
- Init a git repository
- Copy in my desired .gitignore
- Setup the files for
All of that exists outside of Xcode.
Going forward, I’ll be spending a lot of time working with gradle and I hope to move more of my build process to that. It actually does work with Xcode. I’ve seen it. There are a couple of things that need to be worked out, but problem solving is what the job is all about.
I know, I know, I’m not suppose to say things are “wrong” or “bad” or “will lead to the destruction of civilization as we know it” because it’s “negative” or “unprofessional”. This is clearly the first entry in the blog and I’m trying to see if a casual voice works. Also, I wanted an excuse to footnote something and see how that comes out. ↩