Do import statements in Swift have an associated cost? - ios

Reading the String Manifesto, I saw a paragraph about avoiding the Foundation import when not necessary.
Why is this a concern to the Swift team? Apart from aesthetics and code tidiness, do imports come with a cost?
Could importing unnecessary frameworks impact performance, memory usage, packaged app size or build time?

The page you reference is just saying that they'd like to see more String methods built directly into Swift. Currently some tasks like case-sensitive string comparison require importing Foundation.
Ok I'm going to give the rest of your question a shot. The answer is it depends on what you are importing.
Looking at this answer:
Since Xcode 5, there is a new feature introducing precompiled sources database. Xcode 5 basically compiles all the required frameworks just once, saves builds in the database and that already compiled pieces uses while compiling your code
Also looking at this question:
We see that importing UIKit in every file that uses it is necessary. If the above solution is correct than regardless of how many times UIKit or Foundation is imported it is only compiled once. Thus importing a standard library more than once as no affect on compile time.
However, importing something for the first time would affect compile time because that Library now needs to be compiled when it previously was not needed.
Example if I have to import Foundation in a small Swift program the compile time will be slowed down. When it comes to iOS apps it's basically impossible to not import UIKit which also imports Foundation so I don't think this is worth worrying about since every app will have to compile these libraries as well.
Additionally, we need to look at imports that result from things like Cocoa Pods and Carthage:
Looking at this repo:
There are two ways you can embed third-party dependencies in your projects:
as a source that gets compiled each time you perform a clean build of your project (examples: CocoaPods, git submodules, copy-pasted code, internal libraries in subprojects that the app target depends on)
as a prebuilt framework/library (examples: Carthage, static library distributed by a vendor that doesn’t want to provide the source code)
CocoaPods being the most popular dependency manager for iOS by design leads to longer compile times, as the source code of 3rd-party libraries in most cases gets compiled each time you perform a clean build. In general you shouldn’t have to do that often but in reality, you do (e.g. because of switching branches, Xcode bugs, etc.).
Carthage, even though it’s harder to use, is a better choice if you care about build times. You build external dependencies only when you change something in the dependency list (add a new framework, update a framework to a newer version, etc.). That may take 5 or 15 minutes to complete but you do it a lot less often than building code embedded with CocoaPods.
Looking at this blog:
We see that there are more precise imports that can be used if one is concerned about compile time. Such as import UIKit.UITableViewController
As far as performance and binary size goes, any unused symbols are already optimized out of the final binary by the Swift compiler. If there’s no reference to it at compile time then it’s removed, meaning that importing a framework but not using particular parts of it shouldn’t have any negative implications.
Another blog states:
In this WWDC 2016 talk, Apple suggests replacing dynamic frameworks with static archives to mitigate this. To take this approach, we rebuilt as many of our dynamic frameworks as possible statically and then merged them into a single monolithic dynamic framework named AutomaticCore.
The difference was dramatic: our app’s launch time was cut in half.
TLDR: It's not worth worrying about importing standard things like Foundation or UIKit they are only compiled once and just about every app uses them so any performance decrease is shared. However, if you are importing third party Frameworks you might want to use a static archive to help with compile time.

While there is no doubt that importing frameworks increases the raw file size of your app, they are generally very well compressed. And if you need the framework, you have no choice but to import it. Practically, however, file size is not an issue when it comes to frameworks--relatively large ones, like Mapbox, can be included at the cost of just a few MB to your end product. And it's a shared cost by all apps so it's a wash.
Framework code is also stored in shared libraries, not in your executable, which is a very important distinction. Regardless of which Apple platform you're programming for, read Apple's OS X documentation on frameworks because the general concepts are the same. In it, it briefly talks about the relationship between framework inclusion and performance:
If you are worried that including a master header file may cause your
program to bloat, don’t worry. Because OS X interfaces are implemented
using frameworks, the code for those interfaces resides in a dynamic
shared library and not in your executable. In addition, only the code
used by your program is ever loaded into memory at runtime, so your
in-memory footprint similarly stays small.
As for including a large number of header files during compilation,
once again, don’t worry. Xcode provides a precompiled header facility
to speed up compile times. By compiling all the framework headers at
once, there is no need to recompile the headers unless you add a new
framework. In the meantime, you can use any interface from the
included frameworks with little or no performance penalty.
https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPFrameworks/Tasks/IncludingFrameworks.html
That said, don't import frameworks that are already imported by default from "umbrella" frameworks. For example, importing UIKit automatically imports Foundation so don't import both. Does this really make a difference? No, but caring about the details, even the unimportant ones, is what will snowball someone into a programmer other people want to hire and work with. I can't tell you how many programmers I've collaborated with in the past who had the "who cares" attitude about things like this. It's no surprise their code was always the sloppiest and they're the ones always out of work.

Related

Module XXX Was Not Built with Library Evolution Support

My iOS app has two dependencies (Alamofire and Charts) that it incorporates using Swift Package Manager.
When I build it, I get this warning wherever I import the modules in question, e.g.:
import Alamofire
Module 'Alamofire' was not compiled with library evolution support; using it means binary compatibility for 'YourApp' can't be guaranteed
What I don't understand is:
Why is this binary compatibility an issue, if my app builds the dependencies from source code using SwiftPM (not embedding frameworks distributed as binaries), and
What should I do about it?
The Alamofire repository's issue threads suggest that Library Evolution Support cannot be added to the current version because it hinders development, however it is supported in the previous major version (4). Perhaps that could be a workaround, but I don't want to perform the major code modifications that would result on my app from downgrading Alamofire.
I think the point is that with binary compatibility we don’t have to embed the Swift language frameworks into the app; the system Swift language frameworks will keep working for your app even if the language evolves in the future, without your having to recompile the app. Hence the name, library evolution.
So without that guarantee, the language will be embedded into the app, swelling its size and losing the other advantages of binary stability, such as the ability of your app to take advantage of faster code in the system frameworks when they improve things in the future, etc.
You can probably test that theory by building the app and looking inside it.
So if I’m getting this right, that is the sense in which Alamofire is holding you back, and explains the warning.

Xcode 11 recompiles too much

Xcode 11 is recompiling (nearly?) my whole project, even if I just change a local private variable, or change a value of a constant in local scope, sometimes even in local private function scope. I sometime can get 2 or 3 changes with quick builds as expected, but soon enough it decides to recompile everything again (which takes too long).
Any ideas what might be going on? Is Xcode not able to determine what's changed, why does it recompile so much other stuff (even other modules).
Any advice is highly appreciated, thanks!
We had the same problem and we fixed it. Twice.
Incremental build (same build machine):
before: ~10m
after: ~35s
HOW?
Let's start with our experience first. We had a massive Swift/Obj-C project and that was the main concern: build times were slow and you had to create a new project to implement a new feature (literally). Bonus points for never-working syntax highlighting.
Theory
To truly fix this you have to truly understand how build system works.
For example, let's try this code snippet:
import FacebookSDK
import RxSwift
import PinLayout
and imagine you use all of these imports in your file. And also this file depends on another file, which depends on another libraries, which in turn uses another libraries etc.
So to compile your file Xcode has to compile every library you mentioned and every file it depends on, so if you change one of the "core" files Xcode has to rebuild literally whole project.
Xcode build is multi-threaded, but it consists of many single-threaded trees.
So on the first step of every incremental build Xcode is deciding which files have to be re-compiled and builds an AST tree. If you change a file which is acting as "dependable" on other files, so every other file which acts as "dependent" has to be recompiled.
So the first advice is to lower coupling. Your project parts have to be independent of each other.
Obj-C/Swift bridge
Problem with those trees if you're using a Obj-C/Swift bridge, Xcode has to go through more phases than usual:
Perfect world:
Builds Obj-C code
Build Swift code
Obj-C/Swift bridge:
[REPEATABLE STEP] Build Swift code, which is needed to compile Obj-C code
[REPEATABLE STEP] Build Obj-C code, which is needed to compile Swift code
Repeat 1 & 2 until you have only non-dependable Swift & Obj-C code left
Build Obj-C code
Build Swift code
So if you change something from step 1 or 2, you're basically in a trouble.
The best solution is to minimize Obj-C/Swift Bridge (and remove it from your project).
If you don't have an Obj-C/Swift Bridge, that's awesome and you're good to go to the next step:
Swift Package Manager
Time to move to SwiftPM (or at least configure your Cocoapods better).
Thing is, most frameworks with default Cocoapods configuration drag along with themselves a lot of stuff you don't need.
To test this create an empty project with only one dependency like PinLayout, for example and try to write this code with Cocoapods (default configuration) and SwiftPM.
import PinLayout
final class TestViewController: UIViewController {
}
Spoiler: Cocoapods will compile this code, because Cocoapods will import EVERY IMPORT of PinLayout (including UIKit) and SwiftPM will not because SwiftPM imports frameworks atomically.
Dirty hack
Do you remember Xcode build is multi-threaded?
Well, you can abuse it, if you are able to split your project to many independent pieces and import all of them as independent frameworks to your project. It does lower the coupling and that was actually the first solution we used, but it wasn't in fact very effective, because we could only reduce incremental build time to ~4-5m which is NOTHING compared to the first method.
There's no golden bullet here, but plenty of things to check:
Make sure you're actually using the Debug configuration in your scheme
See below for how to ensure you're using incremental builds versus whole module per matt's advice. Also make sure your Optimization Level for Debug builds is none.
If you're using type-inference heavy frameworks like RxSwift, adding explicit type annotations can speed up build times.
If the project is very big, you could consider refactoring out logical groups of source files into frameworks, but that may be too drastic of a change than you'd prefer
It might help if you provided some more specifics about the project: are you statically linking any libraries? Is it a framework or app target? How big and what swift version are you using? Do you have any custom Build Phases like linters or code generation that could be skipped sometimes?

Multiple frameworks and common library

Using iOS 8, Xcode 6.
Let's say I have 2 dynamic frameworks, frameworkA and frameworkB and they are both dependent on libC. In addition, I have an app that uses both frameworkA and frameworkB. My original thought was to make frameworkA and frameworkB umbrella frameworks and libC a subframework. However, Apple advises against umbrella framework and this post describes why umbrella framework is a bad idea due to potential linker conflict issue.
My second option is to use cocoapods (still new to this so a bit fuzzy on details) to use libC as a pod which then gets compiled into frameworkA and frameworkB. However, it occurred to me that both frameworks still has its own copy of the libC. Since the app uses both frameworks, will this result in a linker conflict issue as well? Is there a better way to solve this problem?
UPDATE
#Rob The projects I work on do require complex dependency management but I kept the problem domain simple in the question to try to better understand how and if using cocoapods can help solve the linker conflict issue with umbrella frameworks. I work with a team of developers who write libraries and can depend on each other's base libraries that provide versioned common APIs. We are required to package and deliver as few libraries as possible to a different organization that is building an app with our libraries and one of their key requirement is that we deliver a dynamic framework.
The best way to solve most problems is to put all the code in a project and compile it. When you have specialized problems that make that problematic, then you should look at other solutions, such as static libraries, and finally frameworks.
Static libraries can make sense if you have a code base that has pieces which require different build requirements. If all the pieces have the same build settings, then just "add files" them into your project from a "common" directory and build your project. Static libraries can be attractive if your build times are very significant and some pieces never change and you want to be able to "clean" without rebuilding those parts. But wait until you start having that problem before you go making complicated multi-package projects.
If you sell closed-source libraries, then frameworks are very attractive. You should strongly avoid adding third-party dependencies for the reasons you note. If you must, the best way is to help your customers package all the pieces as frameworks and have them link everything at the end. But that adds a lot of annoyance; so make sure you really need that third-party piece.
You might also consider frameworks if you have a very large piece of reusable code that has its own lifecycle separate from the main products. But again, keep it simple. Avoid third party stuff inside of it, and if you must have third party stuff, then have the consumers link it at the end.
(This isn't a new solution, BTW. When you use curl, if you want SSL, you need to also download and build OpenSSL and link them together yourself. Curl doesn't come with OpenSSL built in.)
But in the vast majority of cases, this is all overkill. Don't jump to frameworks. Don't jump to libraries. Just put all the code in the project and compile it. 90% of your problems will evaporate. iOS projects in particular just aren't that big usually. What problem is a framework solving?
If you have a lot of code that your organization uses repeatedly in lots of products, then I have heard many teams have good luck using internal CocoaPods to manage that. But that's just to simplify checking the code out. It still all goes into a project and you compile it together into one binary; no frameworks needed. Dynamic frameworks are a nice feature in for certain kinds of problems that were really painful before. But, for most situations, they're just complexity looking for a problem.
(If you have one of those specialized problems, edit your question and I'm happy to discuss further how you might approach it.)
EDIT: (You fall into that "specialized problem," so let's talk about it. I did, too, for many years inside of a large multi-team Mac and iOS dev environment. And we tried just about every different solution, including Frameworks. They're only new on iOS.)
Within an org like you describe, I would strongly recommend packaging each dependency as its own framework (AFNetworking, JSONKit, etc) and each of your pieces as a framework, and then have the app devs link all of them together at the end. In this way, it is identical to other dynamic libraries (libcurl, openssl, etc.) which require the app dev to link everything together.
In no case should dynamic frameworks include other frameworks that could otherwise be required (i.e. frameworks should never package "third party" stuff). That will explode. You cannot make that not explode. You'll either have bloat, build conflicts, or runtime conflicts. It's like merge conflicts. There's a point at which a developer has to make a choice. App-level linking is making that choice.
Making components over-dependent on other components is the source of decades of trouble, from Windows DLL Hell to iOS apps with competing crash handlers. All the best component systems look like Legos, where the end user assembles small pieces that have minimal dependencies. As much as possible, make your internal frameworks rely on nothing but Cocoa. This has some tangible design influences:
Avoid directly requiring logging or analytic engines. Provide a delegate interface that can be adapted to the engines of the caller.
Avoid trivial categories (methods that save just a few lines of code). Just write the code directly.
Avoid adding framework dependencies that aren't buying you a lot. Don't add AFNetworking just to save a few lines of code over NSURLConnection. Of course if you're heavily relying on the features of another framework, that's different. But as a framework developer your threshold should be quite high before requiring another framework.
Strongly avoid being clever in the build or version control. I've seen too many cases where people want to make everything "automatic" for the app-level developer, and so make the system really complicated. Just say "you need to link this and import this and put this in your app delegate startup." Don't create complicated build and version control systems to save 2 minutes on the first build or two lines of initialization logic. These things blow up and waste hours to work around. Don't get clever with +load magic. Just make it clear and consistent.
And of course, good luck. Supporting other devs is always an interesting challenge.

How to handle large Swift Project?

After iPhone app that I'm writing in Swift become quite big (> 150 .swift files + various Objective-C libs), Xcode start behave pretty badly:
every second compilation I get various errors, e.g.:
Command failed due to signal: Segmentation fault: 11
compilation take enormous amount of time (> 2 min on MacBook Pro Retina)
and so on.
I just wonder if everyone has same problems and maybe someone found a way to reduce this nightmare?
What I have done so far — I split project into several dynamic frameworks that I link from main project, it helps to reduce compile time, but introduce some new problems.
I also use iRamDisk to keep DerivedData folder in RAM and periodically delete all files from it, it sometimes helps with SourceKit crashes.
Swift toolchain is still a bit gross, you'll need to use some temporary workarounds until Apple fixes it (see UPDATES below)
Here is a list of items that you can do to keep yourself from going crazy.
Slowness caused by immature Swift compiler
Change your development workflow using Injection for Xcode. Once you installed the plugin, you'll be able to inject code changes in your simulator\device without recompiling. You don't need to hardcode\modify anything in your project. We started using it recently at work and it made a huge impact on our side, even if it doesn't apply to every use case (for example you can't create new functions, you can only modify the existing ones).
Some particular code constructs that the compiler doesn't like and takes too much time to compile. The most common problem is with the Type Checker that slows down compile time exponentially based on how many type checks it needs to do (read more here for practical examples and here for a detailed explanation). In order to identify if you are suffering from this problem you can follow this blog post, you will gather information about the functions that creates slowness by using some compiler additional flags. Alternatively you can use this Xcode plugin to identify the source of the build slowness.
Use dynamic frameworks wisely, where it makes sense. A framework recompilation will be done only when you modify one of its Swift files (dynamic frameworks are only available for iOS >= 7).
Condense code in the same files. Lowering the number of Swift files speeds up the compile process sensibly. You can easily achieve it enabling "Whole module optimization" by adding a user-defined custom flag SWIFT_WHOLE_MODULE_OPTIMIZATION and set it to YES and at the same time set optimization level to none (to disable optimizations that would make it slow) OUTDATED You may consider to use this gist, it's a build script that collapses all your code in a "merge.swift" file.
You'll need to create a new target for it, but it is worth a
try.
Double check things listed here (there are a few some more misc reasons because the compilation is slow)
OUTDATED Try the approach described in this blog post, it involves creating a build script that generates a make file. It requires manual intervention on the build script (it contains the list of swift files).
OUTDATED Try this hacked up incremental compilation technique
UPDATE: Incremental builds introduced on Swift 1.2 (Xcode 6.3)
Apple finally introduced incremental builds with Swift 1.2 (shipped with Xcode 6.3). It's not still perfect, but it's a huge improvement.
From now on a class is recompiled only when it is changed (or when one of the class it depends on has been changed).
However the compiler still can’t understand if the changes to a class are to its interface or not. So any kind of change to a class causes a recompilation of that class and all of its dependencies.
UPDATE: Recompile dependent classes only when public interface changes introduced on Swift 2.1 (Xcode 7.1)
Starting from Swift 2.1 (Xcode 7.1), the dependent classes are recompiled only when you change the public interface of a class, and not at every change. This makes an huge difference in particular for big projects.
Project (mis)configuration (not related to Swift)
Be sure that "Build Active Architecture Only" is YES for debug.
Be sure that you didn't add pre\post compilation scripts that take too much time.
Apple has some advices for speeding up your Xcode build in Technical Note 2190. Have you thought about creating and precompiling an own framework for outsourcing unchanged Swift modules or some/all Objective-C code?
Remove all type inferences in Swift.
This SO topic has some nice ideas and this blog post suggest to
stop generating dSYM bundles and
avoid compiling with -O4 if using Clang.
Although lots of these improvements are related to Objective-C, I am quite sure, that some of them are still relevant for Swift.
The (re)compiling is a known issue that I am sure will be resolved soon. Some recommendations:
Use Objective C where possible - it compiles fast even if it is a part of a Swift project
Split code to frameworks
Specify types instead of leaving it up to the compiler to infer them
Again, there is a good chance that this will be fixed soon, so perhaps it is best not to make big investments in rewriting or reorganizing the code at this point in time.
you could try:
upgrading the amount of RAM in your computer
if you have multiple .swift files that do things on the same view controller, try condensing them into one .swift file per view controller
tweaking the settings under compile sources to see if you have any duplicates or if there are any scripts or settings you can add to make it compile faster...
you can also take a look at this post's answers for some hints as to what you can do to slow down compile time
I've discovered that one of the main causes of segmentation faults and slow compilation is hardcoding big arrays and dictionaries, especially when declaring them as global constants and trying to access values from them from within another .swift file. When I store all that data inside plists, these problems are gone.
In my experience avoid creating the large swift files, when I started a project in my new company, there was a 'UIViewController' with more than 2000 lines, little changes on this file taking much time to build, I made 4 extensions with <500 lines from that class, my speed improvement was incredible.

iOS SDKs: Renaming a lot of classes

I'm developing an iOS SDK that integrates other SDKs (Facebook SDK 3.5, for example).
To prevent collisions and allow my customers to import those SDKs as well, I want to rename all of the classes/enums in my code (for example, rename FBSession to RDFBSession, etc).
Is there an easy way to do this instead of going class-by-class and using Xcode's rename feature?
Apple provide a command-line tool called tops(1) that is designed for scripting large-scale code refactoring (renaming C functions, Objective-C methods, classes, and other tokens):
tops -verbose replace "FBSession" with "RDFBSession" Sources/*.[hm]
If you have a lot of replacements, you can put all of the replace... commands into a file that you pass with the -scriptfile option. The man page has more information on the more complex commands/options (and examples).
Xcode also offers textual Search and Replace. This will be faster than individual refactors, but it is ultimately less automated. You can make the step by step refactoring faster by first minimizing the project to the relevant dependencies/sources (if possible).
However, renaming the declarations in a library will not alter the symbol names of its associated binary. If it is distributed with a binary, then renaming will just result in linker errors or (in some cases) runtime errors.
The best idea if you need to use a 3rd party library which your clients might also use is to simply inform them they need to link the library with their app, then publish the version(s) the current release supports so they know they have some extra testing if they go too far ahead with some libraries.
I think that a better approach than simply renaming your classes would be to download Facebook's open source code, rename the classes there and compile a new static library with a set of renamed header files. Then you can be sure that no collisions occur and that you're using symbols that you named yourself.
I must warn you though - working like this may make updating the SDK a nightmare regardless of how you tackle this specific issue.

Resources