Swift may be super-fast at runtime, but during compilation... well, it can be slow. Linkedin revealed in a blog post that they gave all their developers Mac Pros to get their compile times under control. I've experienced a project that takes 10mins each time you want to launch the simulator with 40KLOC. I had high hopes that Swift 3 would have an optimised compiler and solve this, however I found my compile times took around 30% longer after I migrated from 2 to 3. Also, after a codebase hits a certain size, the incremental compilation appears to give up, and any changes result in a full re-compile. And finally, code completion becomes unusably slow (or simply fails) after a certain codebase size too. Needless to say, these problems are disastrous to your productivity, so what can we do to solve them? Hopefully I can help you below.
My main recommendation is to split your app up into frameworks. Now I'm certainly not recommending creating your own cocoapods or carthage libraries or git submodules - I've done that before, it's way too much effort. However, with Xcode's nested projects, this solution can work with minimal ongoing maintenance.
The main architectural issue here is that you need to enforce careful separation of concerns, so that your code will split neatly into mostly-independent frameworks. Once you've done this, Xcode will only compile just the frameworks that have changed and your compile times + indexing + code completion should improve.
I recommend splitting your app up into frameworks like so:
How do we make these frameworks? Here's the process for adding to a workspace. If you're using a project only, it may be slightly different:
publicif things have to be subclassable. I recommend using public where you can because it's like java's
final, eg: no subclassing, which gives the compiler room for optimisations.
import Fooat the top of your swift file in the main/consumer project.
There really aren't too many downsides to this approach, but some things to watch out for:
Xcode will recompile the frameworks if you change them automatically before recompiling the main module. But if it can't compile a framework, it might try compiling the main project against the last compiled version of the framework, and give a bunch of weird errors. So sometimes you should select the subproject's scheme and try building that first.
Sometimes have to turn off SWIFT_WHOLE_MODULE_OPTIMIZATION to get useful errors if builds are hanging.
Sometimes when you have compile errors, it's worth looking in the Cmd+8 tab to see the build output, in case it's an earlier error in one of the frameworks that is snowballing to further things.
To load images that are stored in the main project's
xcassets from the subproject, you'll have to do this:
UIImage(named: "X", in: Bundle(for: type(of: self)), compatibleWith: nil). Alternatively you can put assets in a bundle in the subproject which is tidy.
Apparently if you have an app with too many frameworks, you can increase your app's startup time. For instance, it is common to see 5s startup time on iOS9 with ~20 frameworks. As of iOS9.3 onwards, this problem is largely mitigated, and it is more common to see a 0.3s delay with a dozen frameworks. Apple recommends only having 6 or less frameworks in your app. I haven't done any tests, and I cannot say for sure if these techniques would be any slower than having one monstrously large app module. But this is something to keep in mind. You can read more here: useyourloaf.com/blog/slow-app-startup-times
As of writing (Xcode 8.1) you can often get a significant compile speedup by adding the following user-defined build setting, it may be worth a try for you:
SWIFT_WHOLE_MODULE_OPTIMIZATION = YES
Thanks for reading, I hope this helps!
You can read more of my blog here in my blog archive.
(Comp Sci, Hons - UTS)
iOS Developer in Sydney.
I have worked at places such as Google, News Corp, Fox Sports, NineMSN, FetchTV, Woolworths, and Westpac, among others. If you're looking for a good iOS developer, drop me a line!