Xcode 12.5: SPM Dependency Cache Location - ios

Swift Package Manager got a new feature in Xcode 12.5:
Swift Package Manager caches package dependencies on a per-user basis, which reduces the amount of network traffic and increases performance of dependency resolution for subsequent uses of the same package. If needed, you can disable cache use in xcodebuild by using the new -disablePackageRepositoryCache flag. (72204929)
I would like to know where this is cached. Maybe we can use this to easily cache those dependencies in continuous environments.
I want to know what is the difference of this change to the behavior before since before there was already a cache folder in DerivedData. So what is the new thing here?

Xcode 12.5 release notes appear to be referring to ~/Library/Caches/org.swift.swiftpm/.

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?

How to best modularize an Xcode project to speed up building

I work in a team on an iOS project that has grown to enormous size in terms of Swift code.
It takes about 10 minutes to build the project from the clean state and, most scrutinizing, 30 seconds to build and run the project after changing anything in the code, even if that code pertains to a single line in a private method in Swift file symbols from which are not used anywhere else.
We've tried lots of thing to improve build times, including techniques from this nice resource https://github.com/fastred/Optimizing-Swift-Build-Times
Nothing helped, you still need to wait the whole 30 seconds after changing every minor thing to see it in the app.
We use Xcode 10, the "New build system" with the compilation mode set to Incremental. If I build the project via Perform Action > Build With Timing Summary, the longest phase is "Swift code compilation" which is nothing new. We suspect that Xcode tries to follow conservative compilation decision making and rebuilds every Swift file that could potentially have any connection to the modified Swift code. And it seems that Xcode is wrong most of the time and does redundant work.
I kinda miss Objective-C days when the compiler would look at the all import/include statements and only rebuild explicitly declared dependencies, which meant blazing fast build times.
So I now think that maybe we could break our project into modules and ubiquitously use import in Swift to tell the compiler what Swift files depend on what other Swift files.
Is there a good and perhaps automated way to modularize a big project into many small components to speed up regular lets-try-how-it-works builds?
You can split your projects into Cocoa Touch Frameworks. -> https://www.raywenderlich.com/5109-creating-a-framework-for-ios
Add each framework to its own git repo. It may be private repo.
Create private cocoapods. -> https://guides.cocoapods.org/making/private-cocoapods.html
Add new dependencies to your project podfile. -> pod 'YourFramework', :git => 'FrameworkGitRepoPath'
I might be late to the party here, but we are using this tool: https://github.com/yonaskolb/XcodeGen/
Not only did it allow us to easily split the project into modules, it completely eliminated conflicts in the project file, because it doesn't need to be added to the repository any more.

make Xcode build fail or at least warning if I use methods from "future" SDK version, relative to Target SDK version [duplicate]

Is there a way to have Xcode tell me when I'm calling a method that isn't available in the SDK of the minimum supported target?
For example, the method [NSURLConnection sendAsynchronousRequest:queue:completionHandler:]. This method is available on iOS5 and up. But my application's minimum target is iOS4.
If I use that method (sendAsync), I'd like Xcode to tell me that that method isn't available for the minimum target I'm trying to support.
I've tried putting __IPHONE_OS_VERSION_MAX_ALLOWED=40000 in the preprocessor settings, but that just triggers a bunch of Apple SDK errors that aren't helpful. (Probably because my active SDK is iOS5.1)
Is the only solution to get ahold of old SDKs and install them in Xcode?
Are there any easier solutions?
There is unfortunately no standard way of doing this. By setting the target OS to a lower number than the base SDK, Xcode will weakly link the libraries and frameworks. When doing that Xcode will not warn you for using methods that may not be available on the target OS.
You could temporarily set the base SDK lower, but that might not always work. Since you want to ignore most of the errors and warnings produced (because they are only called conditionally in your code path), and many warnings and errors are dependant on other error that you may need to resolve before the compiler will give any meaningful output.
I do not think there exist any static analysis tools for this, neither from Apple nor third party.
After doing some research, reading the Apple Doc about it, and trying a number of things. The solution is downloading an old Xcode DMG from Apple, grab the .pkg file for the same SDK as your deployment target and install it in your version of Xcode. Here's how:
Download older Xcode.dmg from Apple
Open the DMG
In Terminal, go into packages: "cd /Volumes/[DMG]/Packages; open ."
Find the SDK you want, something like iPhoneSDK_4.0.pkg
Install that package, but change the install directory to /Applications/Xcode/Contents/Developer
Restart Xcode if it was open.
Now that you have the same SDK as your deployment target, set your BaseSDK to the same. When you build you'll get warnings about missing methods. Your project may or may not successfully build with an older BaseSDK in a new version of Xcode, but that doesn't matter - you've just found the method calls you need to wrap in a feature check with respondsToSelector:.
As of Xcode 7.3, the compiler can now generate these warnings for you. All you need to do is set the -Wpartial-availability warning flag in the Build Settings, as described in this answer.

How to give a static library a version number?

We have a static library in Xcode which we need to assign a version number to, just like in an app.
This is useful for many reasons, but mainly because if we could assign a version number we could easily build the library to a specific output folder:
Library-1.0.0
Library-1.0.1
Library-1.0.2
Library-1.1.0
Library-2.0.0
If you see what I mean.
It might also be useful for our clients to manage dependencies and such in their apps.
Is there a way that we can add a version number to the build?
Thanks for your help.
This has everything you need to know: Framework Versioning
Essentially it boils down to a few settings within the framework's Build Settings. There are Major versions and Minor versions. Major version is found in the build setting "Framework Version". It starts off with a value of "A" by default. Minor versions are set with the setting "Current Library Version".
Major versions indicate incompatiblity with previous frameworks. Minor versions indicate compatible updates to the current version.

Resources