Apple Watch pre-build action to change storyboard customModule references - ios

I currently have a project with 3 different versions of the same app (different branding and such), which is working just fine. I've since then added 3 new Apple Watch targets (1 for each app "version"), where which 2 of them reference the files in the "master" Apple Watch target.
Everything works fine as long as i remember to change the module reference for each storyboard view, so that it maps to the correct interface controller in the corresponding watchkit extension target.
However, remembering to switch this every time i need to run/build a app version/target is a pain and not really a long term solution.
I've created the following command which I want to be run at the correct time, such that it changes the storyboard references before it is compiled.
perl -pi.bak -e 's/customModule=\"(.*?)\">/customModule=\"watchMyTarget_Extension\">/g' ${SRCROOT}/watch/Base.lproj/Interface.storyboard
I also concluded that I would probably want to reset the change after the app was compiled, since I don't want to have a file change for git to complain about. Which is why the aforementioned script creates a .bak file. So after the compilation is done and packed/run on device or whatever, I want to run
mv ${SRCROOT}/watch/Base.lproj/Interface.storyboard.bak ${SRCROOT}/watch/Base.lproj/Interface.storyboard
I've tried placing the scripts in the target's (watchTarget, not appTarget) build scheme, Build->Pre/Post Actions which didn't seem to have any effect. And I tried putting it in Run->Pre/Post Actions which worked to some degree, but it seemed like the post action kicked in before the app was pushed to the simulator and thus crashing the application ("could not run see device logs" or something like that).
So where on earth do I need to put these scripts to have them run at the correct time in the build process?

you should use add New Run Script Phase in your target's Build Phases, place it before the Compile Sources
Steps: (from Apple)
In the project editor, select the target to which you want to add a
run script build phase.
Click Build Phases at the top of the project editor.
Choose Editor > Add Build Phase > Add Run Script Build Phase.
Disclose the Run Script section in the project editor.
Configure the script in the Run Script template.

My solution is to go the Build settings of each watch extension target, setting the Product module name to the same value, for example, xxx_watch_extension. Then we should be able to choose this module for custom classes on the storyboard.
It works fine for me.

Related

iOS Asset/Storyboards not updating unless a "Clean Build" Swift package manager, Xcode, Storyboards, iOS

TLDR;
Assets(storyboards, images) not updating unless clean build with Swift package manager local dependency.
Explanation:
I use Swift package manager and have a local dependency (module) that has storyboards, image assets etc. However, if I make any change on them and just cmd+r to run the app, changes are not shown; for example, if I change label text on the storyboard, it won't show the updated text if I just run cmd+r.
To make them appear, I have to clean the build folder and re-run the app again.
But if I change any swift file it works fine.
Try just going to file>Packages>Update Packages To Latest Version
I had a similar behavior with one of my projects. Changes to the storyboard wouldn't be reflected in the simulator if I chose the new build system.
I know that the legacy build system is now deprecated, but you can still try to build with it and see if it is a bug of the new build system.
File > Project settings (or Workspace settings) > Build System > Legacy Build System
Try Updating the packages and also delete your derived Data and archives and give it a go. Seems more of a Xcode bug /cashe issue . Are you using Xcode 13?
It's difficult to find the exact reason because it could depend on several factors. When you open your storyboard file, go to Editor->Automatically Refresh Designable Views and be sure it's checked.
Check also Xcode->Preferences->General->Issues and be sure the check below called "Show Live Issues" is flagged as this picture:
Another reason about your assets refresh failure could depend by the missing resources inside the list: Targets->Build Phases->Copy Bundle Resources
Check also if your resources are shared with a file hosting service like Dropbox, it could be have a local cache which keeps the old version.

Xcode. How download files at the stage of project assembly?

I'm doing an iOS framework and now i need to implement something like this: At the stage of the project assembly, I want to make a request to the server, get the JSON in response and write it to the project file. Thus, the user who downloaded the application, and opened it without the Internet will already have these files. In Android, it is possible to implement this function using the Gradle plugin. What the right way to implement this in iOS? Thanks in advance for the answer.
You can use a Run Script phase in the Build Phases section of your project.
Step 1
Add the initial version of the file to your project. Make sure it's added to the target.
Step 2
Select your project and go the build phases tab. You should be able to see the file being copied to the bundle in the Copy Bundle Resources section.
Step 3
Add a new Run Script to your build. Click the plus button at the top-left of the editor.
Give the phase a sensible name by double clicking the label.
Reorder the phase as early as possible by dragging it up the list.
Step 4
Using your favourite scripting language/command line thing download the new file and replace the existing file.
e.g (untested demo bash sample, probably wont work)
MYFILE=${TEMP_DIR}/something.json
curl -o $MYFILE https://myserver.com/stuff/something-latest.json
cp -f $MYFILE ${SRCROOT}/Sploopy/Resources/something.json
You can examine the environment with the build inspector and see what values you can use to get the file in place.
Ones that will come in handy are:
TEMP_DIR is a good place to put files temporarily.
SRCROOT so you know where to copy the downloaded file to.
CONFIGURATION so you can choose when to do this action. Personally I would only do it on Release but YMMV. It will be a blocking action.
Step 5
Profit

iOS - Exclude Test files from Release build

While developing an iOS application, I am required to run unit & ui tests on a preloaded database. So, I planned to include the DB file preloaded in the App only for DEBUG build as UI/Unit tests will run only with DEBUG build. How can i exclude the DB file from Release build?
Currently I can detect if the host app is running for testing or not using following way,
https://stackoverflow.com/a/33466038/1084174,
but how can exclude and include database.db using this technique?
A little guidance will really be appreciated.
Finally i solve the problem.
Open your project file. At the bottom of the screen click Add Build Setting -> Add User Defined Setting and name it EXCLUDED_SOURCE_FILE_NAMES.
Click the arrow to the left of EXCLUDED_SOURCE_FILE_NAMES to expand it. In the Release configuration for this variable, add libTestFlight.a and any other files you’d like to exclude (separated by spaces).
The standard way to do this would be to create a new Target in Xcode to use for debug. You can then use the target selector on files
Here you can see I have a source file that is included in my main target, but not in my messages extension. This appears in the Utilities pane on the right hand side of the screen. You can achieve the same by including a file in your debug target, but not release (or vice versa).

When does an iOS program run `Run Script` and load Info.plist

Before I run my App, I would like to run a external shell to update my Info.plist.
With Build Phases->Run Script, I can run my external shell. And here is my question, I saw Info.plist update the data when the program was building, but when I ran into my app, I found that the data read from plist was the former old data.
I also found another funny thing.
One side, if I just built the project(command + B), I saw plist update and then I ran the program, the data showed in UI was new data.
Another side,if I used the Run Button in Xcode, I saw the Info.plist update when Xcode show build successed, however, the data showed in UI was old data.
So, what happens? Could you help me to solve it to let my data be always the newest.
=============
Now, I have moved my 'Run Script' to run external shell before 'Compile Source(swift files)', and it works. However, I still can't understand, since it runs during build time, why the running program can't get the right data from Info.plist if I runs my 'Run Script' after 'Compile Source(swift files)'? does the program load in the data during its run time or compile time?
Building an app involves many different phases. Compiling sources is just one of them, there's also linking, copying resources, signing, etc. Some of them are explicitly listed in the "Build Phases" tab, others are not.
So it's really a matter of running the script at the appropriate time in the build process so that the Info.plist is modified before it is actually used by the build process.
If you click on the "Report Navigator" icon (last icon in the list at the top of the left-side panel), then on the last build, you'll see the report of that build which lists all the steps that were performed during the build process, including running scripts and processing the Info.plist.

Dynamically change Bundle Loader of Unit Test

I have an Xcode project with multiple targets, but I want the same Unit Test to run across all of them.
I have created the Unit Test and attached it to each target in my project (using the Edit Scheme>Test menu). Xcode still uses the "Bundle Loader" project setting to determine which app to run when performing a Test though.
So I created an .xcconfig file which the Unit Test uses. This is what it looks like:
SO_BUILDING_PRODUCT_NAME = None
BUNDLE_LOADER = $BUILT_PRODUCTS_DIR/$(SO_BUILDING_PRODUCT_NAME).app/$SO_BUILDING_PRODUCT_NAME
As you can see, it's pretty straightforward. I then wrote a bash script which will change "None" to the actual name of the Xcode target that is being built. I then added this script to each target's Pre-Actions Build phase.
I can see as soon as I tell Xcode to Test, the .xcconfig file updates instantly and I can even see Xcode's UI for the Unit Test update automatically. Unfortunately though each time I change my target, I have to build twice for the change to take effect. It seems like I'm making my change too late for Xcode to notice or care.
Does anyone have some suggestions about how to force Xcode to take notice of the change I make at the start of the Build process?
As far as I have seen (although I haven't explored this since Xcode 5 was released), as soon as you hit Build, it's impossible to change any build settings via .xcconfig files, Xcode seems to take a snapshot before you get the chance to run any scripts or anything like that.
Unless those limitations of dynamic .xcconfig configuration have changed, you will need to try a different approach. I'm not familiar with BUNDLE_LOADER, but if you can change that variable via a script, rather than changing the .xcconfig file itself, you may have more luck.

Resources