I have two Xcode projects, Test and TestFramework in Xcode. Test is related to TestFramework.
There are 2 configurations in TestFramework, which are DEBUG and RELEASE. But 3 configurations in Test. Two of them are same with the configurations in TestFramework. The other one is DEBUG_FOR_TEST.
Test Configuration List:
DEBUG
RELEASE
DEBUG_FOR_TEST
TestFramework Configuration List:
DEBUG
RELEASE
Now When I build Test in DEBUG_FOR_TEST, it will fall into trap by missing TestFramework.
Since there is no DEBUG_FOR_TEST configuration in TestFramework, RELEASE will be used to build. And Test could not find TestFramework in the directory of DEBUG_FOR_TEST. Then it comes to that error.
I try to find the solution. There are several way to solve it:
Add DEBUG_FOR_TEST configuration for TestFramework. It acts but I don't want to change the Build Setting in TestFramework. Because it could be a 3rd party framework sometimes. It could be a hard work under the source control.
Add the path of TestFramework's RELEASE framework into the Framework Search Path of Test. It also acts but I may want DEBUG_FOR_TEST to be related to DEBUG.
So, both of them can not solve this perfectly.
I think if I can specific the configuration while it's propagated through different project, it should be done.
I do find a way in this website by adding SUBPROJECT_CONFIG and running a script. It also acts but very redundant.
I think it's a common problem. Most of the article about this question is written many years before. Apple might provide a common way to solve it now. But I can't find this. Does anyone have ideas?
Related
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?
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.
I once asked a question related to XCTests. And in one of the answers I was told that it is a common practice to use a separate test target (other than the main app) when running unit tests (at least, in iOS development). I tried to find some sources about it, but I couldn't
I understand, that it is probably a best practice, so I would really like to understand it. Could someone explain to me why is it important, what benefits do I get from it and how should I go about doing it? Links to some articles explaining the issue will be much appreciated.
P.S. I understand that I need special environment for tests (fake in-memory database, mocked networking layer, etc.), but up until now I managed to achieve it without a separate test host. But I believe that there might be a better way.
To answer your points:
Why is using a separate test target important?
Separation of concerns. A unit test target does not have the same kind of structure as a regular app target - it contains a test bundle, which references the app target under test, as well as the test classes and any helper classes required. It is not a duplicate of the app target with test code added to it - in fact it should not even contain the code under test. This means that you don't have to make any special effort to keep the test target in sync with the main app target - the fact that the test bundle loads the main app handles all that for you. (You made a comment on Richard Ross's answer to your previous q suggesting you've run into the difficulties duplication causes already).
How should I go about doing this? (checked on Xcode 7).
Assuming you are wanting to add a target to an existing project that doesn't have any tests, select the main project, and then click on the '+' beneath the list of targets in the project. You can also use the File->New->Target menu option.
You'll see a menu asking you to choose a template for your new target. Select 'Test' and within test select 'iOS Unit Testing Bundle'.
Check the fields in the next screen - they should be correct by default - but you might want to double check the 'Target to be Tested' field value is correct if you have a lot of targets in the project/workspace. Click 'OK' and you should have a functioning unit test bundle, with an example test that you should be able to run using Apple-U or Product->Test You'll still need to #import the app classes if you're using ObjC.
If you are creating a new project, all you need to do is tick the 'Include Unit Test' box when creating the project - no other steps are required.
Apple Docs (with links to relevant WWDC presentations)
Tutorials. Most of the tutorials around are a bit out of date. But not that much has changed, so just look at the docs and figure it out. The two below might be useful, otherwise just google. From a quick glance, most of them seem to assume that the project had unit tests set up at the beginning
http://www.raywenderlich.com/22590/beginning-automated-testing-with-xcode-part-12 (2012/iOS 6, but the process is still broadly the same. Also deals with Jenkins, Git and running tests from the CLI).
Unit testing in OSX - most recent post - not iOS, I know, but more up to date than most of the iOS tutorials (Oct 2015), and gives step by step instructions (including setting the unit test target in the build schemes, which probably won't be necessary in your case). Probably worth a look anyway.
I've created several static libraries that are shared among multiple iOS projects. In a lot of these static libraries i've created unit tests. I'd like these tests to run whenever I test the project that the static library is included in.
I've tried including all of the tests inside of the "Test" section in the main projects scheme but this usually results in "Simulator is already in use" errors and the tests fail. Probably because the previous tests are using the simulator.
Skin CreatorTests are for the main project
SEUSUIKitTests, APIKitTests, PurchaseKitTests and MCSkinKitTests are all tests that are from attached static libraries
Am I going about this the right way, or should I be thinking differently?
** EDIT **
Looks like this stack overflow question is having the same problem.
Xcode 5: Multiple test targets in one scheme: "Simulator already in use"
** EDIT 2 **
Radar: http://openradar.appspot.com/15153136
Although correctly setting up your Unit Testing is pretty much a mess and a time sink on XCode (thanks to the very clear messages you get, as you found out), in your case I think the problem is your approach.
If you have several static libraries linked from multiple projects, the tests for the static libraries should not be run from the projects referencing them. Each library should be self-contained and pack it's own tests, and they should be run whenever you change something in that particular library.
The idea is that you should only be testing your own code. If you link an external static library, that library's author is responsible for correctly unit testing this portion. The fact that you're also the author of the external library should not have an impact on this.
I have been writing iOS applications and completed a project with a lot of frameworks. Now I am using it as a template to start a new project that requires less functionality and hence I should be able to reduce the frameworks required, and hopefully reduce build time and size of project.
Question:
Is there a quick way to check which frameworks are no longer required within the project?
I don't think there is a better way than removing the framework, building, and seeing if there are link errors. You might be able to write a bash script but it's probably more work than it's worth.
Sadly not. The quick way is to remove all the frameworks, look for build errors and add back in the necessary frameworks.
search in the project files(cmd+shift+F).i.e whether you are imported any files related to the frameworks.