Does the Swift toolchain eliminate code that is never called? - ios

If I create an Xcode project with the iOS Single View Application template and choose Swift for the language, will the compiler exclude from the release build (binary) functions that never get called?
I'm wondering because I want to include a third-party library that has a lot of superfluous classes & functions, and I want to keep my app small & fast.

While I agree with comments, it is unlikely to impact performance in any significant way even if it was included...
Xcode 6 uses Apple LLVM Compiler Version 6.1, depending on how closely related it is to LLVM Developer Group's version the optimization feature is available http://llvm.org/docs/Passes.html with options such as -dce: Dead Code Elimination, -adce: Aggressive Dead Code Elimination.
One way to know for sure what is included is checking the assembly output using -emit-assembly option in the swift compiler and review the output, or opening the binary in a disassembler such as Hopper ( http://www.hopperapp.com/download.html )

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.

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.

How to distribute Swift Library without exposing the source code?

The first thing I tried is to create a static library but later I found out that it's not supported yet. Apple Xcode Beta 4 Release Notes:
Xcode does not support building static libraries that include Swift
code. (17181019)
I was hoping that Apple will be able to add this in the next Beta release or the GA version but I read the following on their blog:
While your app’s runtime
compatibility is ensured, the Swift language itself will continue to
evolve, and the binary interface will also change. To be safe, all
components of your app should be built with the same version of Xcode
and the Swift compiler to ensure that they work together.
This means that frameworks need to be managed carefully. For instance,
if your project uses frameworks to share code with an embedded
extension, you will want to build the frameworks, app, and extensions
together. It would be dangerous to rely upon binary frameworks that
use Swift — especially from third parties. As Swift changes, those
frameworks will be incompatible with the rest of your app. When the
binary interface stabilizes in a year or two, the Swift runtime will
become part of the host OS and this limitation will no longer exist.
The news is really alarming for me a person who writes components for other developers to use and include in their apps. Is this means that I have to distribute the source code or wait for two years?. Is there any other way to distribute the library without exposing the code (company policy)?
Update:
Is Swift code obfuscation an option at this point ?
Swift is beta now, and even for 1.0 Apple has been pretty clear they're after a restricted feature set -- better to do a small number of things well than to try to do everything.
So for now, there's no way to distribute binary static libraries. Presumably that'll change sometime after Swift 1.0. For now, you can:
Distribute source
Ship a binary framework (instead of a library) if you're okay with the ABI being fragile
Use ObjC for library code
You can always combine approaches, too: e.g., implement the critical (secret) details of your library in ObjC, and ship Swift source that wraps it in a nice Swift API.
Obfuscating code written in a language that's very much subject to change sounds like a recipe for a maintenance nightmare.
I believe the whole approach is wrong. You cannot do something that is not (yet) doable by the technology you are trying to use.
Rationale: Swift is a new language, currently in Beta, and therefore changing. As I see it, this fact means not only that you are not able to ship static libraries, but that (real) developers will not be actually use third-party static libraries. What's the actual use of a library that may not work in the next release of the compiler? The issue gets bigger if you whant to use more than one library, because they might not be compatible! Therefore, even if you would be able to ship static libraries, they wouldn't be actually useful for production environment. So, what's the point?
Suggestion: write your static libraries in Objective-C (or C or whatever "non-beta"). Developers who need third-party libraries (e.g. yours) shouldn't expect them to be written in Swift until Swift is stable. You don't use experimental materials to build real bridges, right? You use well-tested, predictable ones.
From Xcode 9 beta 4, Xcode supports static library with Swift sources.

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.

Invalid Executable Size - From iTunes Connect

I am uploading my iOS application on iTunes. I am using MonoTouch for compiling my LibGdx Game for iOS. In Android it is hardly 7-8mb. But When I upload on iTunes AppStore then its goes to 78 mb. I dont know why ? Please Let me know.
I have also received this error from Apple.
Dear developer,
We have discovered one or more issues with your recent delivery for "Run Panda Run: Racing". To process your delivery, the following issues must be corrected:
Invalid Executable Size - The executable size of 72037504 bytes exceeds the maximum allowed size of 60 MB.
It's hard to give a definite answer without more details. There's a lot of things that can affect the size of the applications. Let's start with the basic.
What you should check:
First, ensure that your application is not being build with Don't link. That will create very big applications since you'll be AOT'ing nearly the full .NET framework that Xamarin.iOS ships;
Second, make sure you're building for a single architecture (ARMv7). FAT binaries (e.g. ARMv7 and ARMv7s) are build two times and needs twice the space;
Third make sure you have not enabled the Debug build (it's possible to do so in Release build, it's a checkbox). That will create larger binaries to support debugging;
Fourth make sure you're using the LLVM compiler. It takes more time to compile but it generates better (and smaller) code;
Those initial checks are pretty easy to do and are the most common reasons for getting very large binaries.
To understand where the size come from you need to know how the application are built.
The main difference between the Android and iOS version is that JIT'ing (just-in-time compilation) is not allowed on iOS (Apple's rules).
That means the code must be AOT'ed (ahead-of-time compilation) and that process creates much larger executables (because IL is way more compact than native code);
If your code is generic-heavy then the final binary can get quite large since it will need to natively compile every generic possibilities (many cases can be shared, but value-types cannot).
What you can do to reduce size:
First try to reduce your managed code size. The easy way to do this is the enable the linker on every assemblies, i.e. Link all assemblies in your project options.
Many people think it's not worth linking their own code - because they know it will be needed at runtime (so the linker cannot remove it) or else they would not have written that code.
That's half true. The linker might not be able to remove most of your application code but if you're using 3rd party assemblies they are not likely 100% used. The linker can remove that extra code (and also remove, from the SDK, all the code that is kept to support that unneeded code). The more shared code you have the more the linker can help you.
Less IL code means faster AOT time, which translate to faster builds and smaller final binaires (including the executable size).
Note: there's a lot of documents and blog entries on how you can control the linker to skip some assemblies, some types or method from being processed/removed.
Second try to reduce your native size. If you're building native libraries then have a second look at them as they will be statically (not dynamically) linked to your final binary (another rule for iOS applications). Some of them might not be worth (feature wise) their weight in your final binary (and there might be lighter alternatives).
Debugging should not be enabled, as it will make the build unnecessarily large.
For more information refer : https://learn.microsoft.com/en-us/xamarin/ios/deploy-test/app-distribution/app-store-distribution/publishing-to-the-app-store?tabs=windows
I had the same problem but in my case I had minimum os version set to 8 in the info.plist causing a larger .ipa file. I changed this to version 10 and was able to pass the size requirements. Even 10 is a bit generous

Resources