Different Application Name For Development/Release Build - ios

I have an iOS application that has "sysadmin" only features when in the development build which are removed for the release build.
What is the easiest way to have two builds on our testing devices in house, one in development mode, and one in release mode that have different names on the iOS home screen?
The current situation is that when we want to test the release build, I have to manually rebuild it for each device. When we want to switch back, I have to once again manually rebuild the application.

As LoVo mentioned - you will need different app-identifiers and bundle display names. Here's how I would approach the problem:
In Project's build settings set the following values:
Preprocess Info.plist File - Yes
Info.plist Preprocessor Prefix File - InfoPlist_proc.h
Add the header generation script to your Xcode project.
The script in ruby should look like this:
#!/usr/bin/env ruby
# build type is going to be passed
build_type = ENV["CONFIGURATION"]
proc_header_path = File.join(ENV["SRCROOT"], "InfoPlist_proc.h")
File.open(proc_header_path, "w+") do |f|
f.write("#define BUNDLE_IDENTIFIER com.company.app.#{ build_type.downcase }\n")
f.write("#define BUNDLE_DISPLAY_NAME \"My App #{ build_type.downcase }\"")
end
Now in the "Build" stage of your scheme (Cmd+Shift+,) create "Run Script" pre-action:
Add execution permissions to previously created script ($ chmod a+x script.rb) and set it to run from your pre-build script run phase:
Finally in your current Info.plist file change the bundle identifier and display name values with preprocessor definitions:
Now each time you build your app, the InfoPlist_proc.h file will be regenerated. This has a drawback though: to change your app's bundle identifier or name you would have to edit the script, not the Info.plist
But it also gives an advantage: you can inject the bundle version and short version string from what your cvs gives you. I usually use git rev-list --count --all for bundle version and git describe --tags --first-parent for short version string.
Hope this helps.

Set different App-Identifiers and Bundle Display Names

Related

iOS Sourcery with Flutter build

Im trying to build my flutter app for iOS it has a google maps key that I want to protect and not check in to source control it needs to be buildable from azure, to achieve this I'm storing my maps key as a secret variable in azure and as a system environment variable locally, I'm using Sourcery https://github.com/krzysztofzablocki/Sourcery to generate a class for me that contains this key, it all works but only the second time I build, the first build always fails.
So I'm building using this command
flutter build ios --flavor dev --verbose
Which the first run will give me the error
error: Build input file cannot be found:
'/Users/martin/xxx/xxx/xxx/ios/Runner/Credentials.generated.swift' (in target
'Runner'
Then issuing the same command again
** BUILD SUCCEEDED **
this is my run script its called before compile sources and after the flutter run script
this calls my script which calls another script to export the map api key and runs sourcery command using a .yml file as its config heres the script, (it also does some logging)
#!/bin/bash
echo "Generate Credentials Code"
CREDENTIALS_DIR="$SRCROOT/credentials"
# Set credentials if local script for adding environment variables exist
if [ -f "$CREDENTIALS_DIR/add_credentials_to_env.sh" ]; then
echo "Add credentials to environement"
source "$CREDENTIALS_DIR/add_credentials_to_env.sh"
echo "finished running add_credentials_to_env.sh"
fi
echo "RUN SOURCERY"
$SRCROOT/Pods/Sourcery/bin/sourcery --config "$SRCROOT/config.yml"
echo "FINISHED RUNNING SOURCERY"
for file in "$SRCROOT/Runner"/*; do
echo "$file"
done
and here is my config file
sources:
- .
project:
file: Runner.xcodeproj
target:
name: Runner
module: Runner
templates:
- credentials/Credentials.stencil
output:
path: ./Runner/
link:
project: Runner.xcodeproj
target: Runner
args:
mapsApiKey: ${MAPS_API_KEY_IOS}
this generates my class correctly on the first build and seems to be added correctly to the target (edited out my key) but the app will only compile if I run the build command again.
// Generated using Sourcery 1.4.2 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
public struct Credentials {
let mapsApiKey: String
}
public let credentials = Credentials(mapsApiKey:
"xxxxxxxxxxMY_KEYxxxxxxxxxxx")
Any ideas?
xcode 12.5 m1 macbook pro, swift 5
Looks like you generate the file too late. I'll suggest move your script to Aggregate and add it as a dependency to your target
Add Aggregate
Move your script to 'Run script' section
Add 'PreBuildScriptsRunner' as a dependency to your application target, make sure 'Dependencies' section on top of all other sections
Manually setting environment variables is an annoying thing developers would have to do on their own machines, and there are nicer/ more common ways of setting up private keys. After a few years of using environment variables/ bash, it still causes issues which are not easily detectable. You may want to automate/ document it, but then you have to consider developers using zsh, fish vs. bash? Also, I try to avoid using Xcode build phases where possible.
Solution? (This is what I have)
Why don't you use your CI (Azure pipeline?, I use Github workflows) to write a Xcode build configuration file (not a Swift file). The sensitive keys could be in a file Secrets.xcconfig, which is added to your Xcode as a build configuration. Then, in your Info.plist of your application, and your code can load them.
Create a file, Secrets.xcconfig:
SECRET_API_KEY = 12312rfiwhvde.wvascafsf.df325
Add it to your Xcode project, and then to the project's build configuration:
Add Secrets.xcconfig to your .gitignore
Make sure to git ignore the file before committing it to the repo. You can also keep an Example.Secrets.xcconfig which users can use. In the readme, tell users to run cp Example.Secrets.xcconfig Secrets.xcconfig and then to update values in it. Now you can clearly see what keys the application is using (its clearly in the directory). As a bonus, you can add this file the Xcode project, so that when the file is missing, it shows up in red (indicating to the user they really should acquire this file somehow):
In Info.plist, reference the variable:
<dict>
<key>SECRET_API_KEY</key>
<string>$(SECRET_API_KEY)</string>
</dict>
In your code, load the variable that was stored in Info.plist:
let key = Environment.infoDictionary["SECRET_API_KEY"] as? String
In your CI/ Azure pipeline:
Run echo "SECRET_API_KEY = $SECRET_API_KEY_SAVED_IN_CONTINUOUS_INTEGRATION" >> Secrets.xcconfig
Then you can just .gitignore the file instead of setting environment variables. When you work with other developers, you just give them this file, and nothing else needs to be done to build locally.
So I have answered your question not by solving your direct problem, but giving you a more common/ canonical way of solving this problem that many developers have faced before.

iOS app test if Localization translations have same keys

In my iOS app I have localization files such as Localizable.strings.
I want to check that they have same keys and there is no missed keys in each localization.
I thought about performing this in Unit Tests.
Is Unit testing the right place for this? Maybe there is much easier tool for it?
How this Unit testing can be done?
I found article on this topic in Obj-C https://www.cocoanetics.com/2013/03/localization-unit-test/ that is 5 years old. Maybe something else can be used?
You can just use this shell script I made up and run it as a Run Script (within Build Phases) of your App Target.
First, create the shell script and save it somewhere in your App Project, I saved it as check_missing_keys_in_translations.sh for example:
#!/bin/sh
# check_missing_keys_in_translations.sh
#
# Overview: Script will be executed from Build Phases > Run Script
#
# Script: Extract sorted localization keys using "$1" as language code
# given as parameter to locate the right Localizable.strings.
#
# Build Phases > Run Script: The result will be compared to see if
# translations have the same keys or if one of them have more or less.
plutil -convert json 'AppProject/Resources/Localization/'"$1"'.lproj/Localizable.strings' -o - | ruby -r json -e 'puts JSON.parse(STDIN.read).keys.sort'
Just change the path AppProject/Resources/Localization/ to the path where your en.lproj, it.lproj... localization folders are located in your app (in this case called AppProject).
Second, go to your App Project, select the App Target and under Build Phases put this script code within Run Script:
# Check missing localization keys in translations
MISSING_KEYS_TRANSLATIONS=$(diff <($SRCROOT/tools/localization/check_missing_keys_in_translations.sh en) <($SRCROOT/tools/localization/check_missing_keys_in_translations.sh it))
if [ "$MISSING_KEYS_TRANSLATIONS" ]; then
echo "warning: $MISSING_KEYS_TRANSLATIONS"
fi
As always, check the path and adapted to where you saved the script created before. I saved it under App Project/tools/localization/... as you can see. You might want to adapt this script to better reflect your situation as I had only 2 localizable I was interested in checking they had the same keys. Is just shell scripting.
Check screenshot below:

Xcode copy resources to simulator

I want to copy files to the path that the Simulator will use. I'm talking about the path that looks something like this when the Simulator is running
/Users/myUser/Library/Developer/CoreSimulator/Devices/A9A30146-764F-4A4B-BAA2-00D5808D77C2/data/Containers/Data/Application/B85F7B67-FD50-48AA-8E03-2F806858AF71
Is there a way to get that path at build time so that I can copy files there using a Run Script build phase?
Here is why I want to do this:
Currently my app fetches a large amount of resources from the network every time the app is newly installed. The app checks the contents of the local file storage, and if a file is missing or out of date, it will sync them.
To speed up the development feedback cycle, I want to copy those resources from a local directory on my development machine, either during a build step or whenever else makes sense, to the proper destination in the simulator's local storage. This way, I can have a fast development cycle while only needing to delete the build script, or whatever other mechanism, when I'm ready to release the app into production.
This is available to your Run Script build phase as the TARGET_BUILD_DIR environment variable. A reasonable place to copy these files to might be $TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH. This is how CocoaPods installs resources from Pods, see https://github.com/CocoaPods/CocoaPods/blob/master/lib/cocoapods/generator/copy_resources_script.rb
However it's not clear why you would do this in a Run Script phase. For many uses you probably just want to use the existing Copy Bundle Resources build phase.
I don't think you have access to the path you are looking for at build time because the app is built prior to being installed and the installation path may not have even been determined when building the app.
To warm your cache or whatever you want to do for local development I see a few options:
Include these resources in your app bundle and then copy them to the appropriate destination directory at startup. This should be reliable and might be something you want to use in release builds if you want to include some default version of these resources in the app which can then be updated as needed when the app is launched. This approach also increases the size of your app bundle and if its not something you want in release builds might be more work to remove or disable in those environments.
Embed a reference to the location of your development files in the app at build time (e.g. a path relative to SRCROOT). Copy those files into place as needed when the app is launched. This would only work in the simulator and relies on the simulator not enforcing sandbox controls (allowing your app to read for a directory outside the simulator).
Download these files from a server as you would normally but host them locally so your development builds can fetch them from some localhost address quickly.
I'd try to use option 3 to mostly closely replicate the release behavior of the app but use whatever works best for your workflow.
You can copy and paste below command to your Run Script build phase
cp -r "Your Resource Path" "`xcrun simctl get_app_container booted ${PRODUCT_BUNDLE_IDENTIFIER}`"
xcrun simctl get_app_container booted ${PRODUCT_BUNDLE_IDENTIFIER} -> This will take currently booted your product's path, you can even check from terminal like:
xcrun simctl get_app_container booted com.your.packagename

Fastlane: There does not seem to be a CURRENT_PROJECT_VERSION key set for this project

I were following this website guide on setting up fastlane with hockeyapp but I encountered error
:BUILD_NUMBER=>"There does not seem to be a CURRENT_PROJECT_VERSION key set for this project. Add this key to your target's expert build settings."}
I have created build_number_icon.rb in actions folder and I even tried to hardcode the build_number and test run the fastlane. Still getting the same error. Am I missing something? I have been reading the guides again just in case I have missed the steps.
# Build and publish the Beta version to Hockeyapp
lane :beta_hockeyapp do
# Build
build_app(
# Not a production release, so add build number and do the color modulation of the icons
release:false,
# Modulate the colors of the icons by these degrees
modulation:66.6,
# Change the app name
app_name:"<App Name>",
# Set the app id
app_identifier:"com.company.<appname>",
# Set the path to the certificate to use in building
certificate_path:"./Certificates/Company/<cert>.p12",
# Set the password of the p12 certificate file
certificate_password:"<password>",
# Set the path to the provisioning profile to use (change this!)
profile:"./Certificates/Company/<profile>.mobileprovision",
# What configuration to use, usefull for keeping different API keys etc between environments
configuration:"[INT] <project_name>",
# Use this codesigning identity (this is the name of the certificate in your keychain)
codesigning_identity:"Project Distribution",
# Export an enterprise app
export_method:"enterprise",
# the projectname, this is the name of the .xcodeproj file and the folder containing your code in the project
project_name:<Project>,
# the scheme to build
scheme:"Project-Internal",
# the build number to use, we use the build number from Jenkins
build_number: "1234" || ENV["BUILD_NUMBER"]
)
# Push to Hockeyapp as Beta release
publish_hockey(release_type: "0")
end
Fixed the error when you need to set a number at project's build settings > Current project version.
I use fastlane, but not with hockey. I've not personally encountered this issue. However, the warning is alerting you to a specific issue inside your project's target > build setting tool (listed as "Current Project Version" under the "Versioning" subsection). In the search field, enter CURRENT_PROJECT_VERSION and you'll see that this is a different value than your build_number. My project does not have it set, but I suppose hockey maybe has different needs?
At any rate, it seems as though you're overlooking the precise key-value being asked for.

Use name with whitespace for qmake TARGET variable

iOS/OS X application names usually contains spaces (like "App Store.app").
But when i'm trying to use such name in my Qt/ios project like this:
ios: TARGET = "My Cool App"
build process failed with strange error in the autogenerated by Qt Bash script.
Am i doing something wrong, or such whitespaces in names just don't supported?
UPD
the problem first time occurs in qmake-generated shell script:
#!/bin/sh
cp -r $BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME /Users/eraxillan/Projects/<PROJECT_DIR>
If $FULL_PRODUCT_NAME contains spaces, then script just fails.
Script generated and executed only in case of custom DESTDIR project variable value - my case.
So, looks like a bug in qmake-generated script to copy project build artefacts to custom output directory.
Workarounds:
Remove spaces from target name (My Cool App --> my-cool-app)
Do not use custom DESTDIR value at all
Hope this will be helpful
This should work:
TARGET = My" "Cool" "App

Resources