Can a custom build phase modify a file before compilation? - ios

I have an iOS app that uses both Objective C and Swift.
Before including Swift code, the app used a logger which uses c macros.
For security reasons, the c macros then implement a mechanism that strips down the logs when building a release version, and that works perfectly fine.
When the Swift code kicked in, a Swift wrapper was made on top of the c macros. So now a log from the Swift code actually calls the swift wrapper which then calls the c macros. And that is not secure, the arguments sent to the swift wrapper should also be hidden otherwise an attacker may still extract some data.
I was thinking about running a custom build phase (shell script) that would delete all the function calls before creating a release build, but am doubtful this is possible.
So my question is:
Can adding a custom build phase (run script phase) or maybe even a build rule modify a file before compiling it? Or will I have to write a script that I will need to run manually each time before releasing?
Thanks.

Yes, it's perfectly acceptable to add a pre-compilation build step to run a script.

Related

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?

Achieving a preprocessor in Xcode 9?

How, really, would you mod Xcode to include preprocessing on all scripts - in modern Xcode (ie 9+)?
Obviously this was trivial in the Old Days.
Would one basically be using a custom build rule ?
.
.
Or perhaps, would you need basically "A Mac App" which runs (how?) before the compile?
It depends on what you want the preprocessor to do. Assuming that the output files are regular .c, .m, .cpp etc. files that the compiler can handle correctly then you could simply add a new 'Run Script' build phase to run your pre-processor. It has access to the Xcode environment variables so should be able to run effectively. As Nik Kit said though, it depends on exactly what you want to achieve.

Proper steps for a React Native iOS release build

This is just a case of lack of documentation thus far, but when creating a production release build of an iOS React Native app, after setting the Build/Archive schemes to be Release instead of debug, do I need to manually create a main.bundle and uncomment the second jsCodeLocation line in AppDelegate.m, or does something handle that for me automatically?
I've been doing both steps manually thus far, but I'm sure there will be a day that I forget to do so, and I wanted to know what the compile and build scripts are doing for me and what I need to do myself.
(Using react-native#0.21 if applicable.)
on our App we have made that our Build System generate App.plist (with some env contextual client ids/tokens/api urls of service) and we have a property boolean that indicates if the app needs to use bundle or not.
Based on this we switch the mode to use (use bundle VS use live reload server).
I submitted some months ago a PR to bring this in the skeleton, this never goes through though, but feel free to implement your own: https://github.com/facebook/react-native/pull/2101
Also one interesting thing we use to generate our App.plist based on env is a script that basically do this: https://twitter.com/greweb/status/687575516862349312
Hope this helps

How to replace or resolve __TVOS_PROHIBITED dependencies in unistd.h

I'm trying to compile a dependent libraries from the source code and I've got this error:
/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/usr/include/unistd.h:446:8:
note: 'fork' has been explicitly marked unavailable here pid_t
fork(void) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
Is there any way to resolve this issue?
The thing is that I don't actually even need this functionality. When I compile this code for iOS and run it under tvOS then it works.
But I can not submit the application because it contains code compiled for iOS.
What would be the good trick to substitute there a dummy fork() function so it compiles OK (believing that it is not actually used by my specific use-cases).
I don't believe there is a way round this. You cannot create processes under iOS and tvOS is 90% iOS, so the same restriction applies.
You'll have to conditionally compile-out that section of code for iOS/tvOS.

Passing custom build options to closure when building with stealjs

I am using JavascriptMVC to build an app with StealJS build functionality. This is my build.js:
load("steal/rhino/rhino.js");
steal('steal/build').then('steal/build/scripts','steal/build/styles',function(){
steal.build('evi2/scripts/build.html',{to: 'mylovelyapp'});
});
This is working fine. However I would love to use Google Closures #define syntax to change a few variables on build time. While it would be perfectly simple to modify steals steal/build/scripts/scripts.js, I would obviously much prefer to pass the --define flags somewhere in my build-script or in my project files.
Any ideas on how to achieve this would be highly appreciated.

Resources