How to access Build Settings in Xcode 6? - ios

There seems to be a lot of variations of how to access the Build Settings variables (i.e. to define the base URL of a web service for different Debug vs. Release environments).
I created a User-Defined variable in Project -> Building Settings, one for each environment. Let's call it WEB_SERVICE_BASE_URL.
How do I access it in the code? I'm using XCode 6 and Swift.
I've tried this but it doesn't work
let api_key = ${WEB_SERVICE_BASE_URL}
I've also tried this and it also doesn't work
let api_key = NSUserDefaults.standardUserDefaults().stringForKey("WEB_SERVICE_BASE_URL")
Any suggestions? This seems to be a often needed solution, it's so easy in Rails, but not so in iOS development.

Here's how to set it up:
Add a User-Defined setting to your target's Build Settings (which you did with WEB_SERVICE_BASE_URL)
Add a new row to your target's Info.plist file with key: WEB_SERVICE_BASE_URL, type: String, value: ${WEB_SERVICE_BASE_URL}
Here's how get the value:
let api_key = Bundle.main.object(forInfoDictionaryKey: "WEB_SERVICE_BASE_URL") as? String
Note: These keys/values can be extracted from the package, so be sure to avoid storing sensitive data in there.

Related

Can I force applications to run in a different locale to the system setting?

I am developing a C# .NET 6 client-server product using VS2022 with multi-language support. I've set up locale-specific resource strings but at the last minute I realized a problem: while client workstations are set up in local language, the app-server is always in English - so back-end code is using the English localization!
Servers are in English for a good reason (and we can't change this) so is there a way to force a deployed application to use a specified locale? Perhaps in a config file somewhere?
I know I can do this in code by changing Thread.CurrentCulture or similar, but the whole point is I don't want to hard-code it, I want it to be config-driven in a way that overrides the default system setting.
I want it to be config-driven in a way that overrides the default system setting.
You can always add custom setting to your config file and then read it on start up and set needed culture.
For example something like this:
appsettings.json
{
"LocaleOverride" : "en-US",
// rest of settings
}
And somewhere at the start of app (depends on how it is started, if generic/minimal hosting is used, configuration can be read from there, otherwise - manually):
var locale = Configuration["LocaleOverride"];
if(!string.IsNullOrEmpty(locale))
{
var cultureInfo = new CultureInfo(locale);
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
}

Best Approach for maintaining configuration safely

Overview:
I have an iOS project which contains the server configurations in a Plist. I have different plists for different targets (Dev / Stage etc)
Problem:
This plist gets copied to the main bundle, so I feel it is not so safe as one can get it from the ipa file. I would like it not be accessible.
Question:
What would be the best approach to maintain server configurations for different targets ?
Can plists be used if so how to make it not accessible via the ipa file and yet be readable through code ?
Is there a better approach to solve this problem ?
You can avoid using plists by hardcoding the config. This way it is compiled into the app and not easily accessible via the ipa file. It's not entirely secure, but less accessible than a plist.
Create a file MyConfigDev.swift
// this file is added to the Dev target
class Config {
private init() {}
static let shared = Config()
let myParam = "valueForDev"
}
And another file MyConfigStaging.swift
// this file is added to the Staging target
class Config {
private init() {}
static let shared = Config()
let myParam = "valueForStaging"
}
If you build the Dev target the file MyConfigDev.swiftis used, if you build the Staging target the file MyConfigStaging.swiftis used.
In your code you can access the config:
let p = Config.shared.myParam
To get access to your configuration hackers will now need to decompile your ipa.

Xcode Localizable.string multiple targets issue

I have a project with multiple targets, which represent the same app just with different styling and translations.
Since almost whole project looks the same for each target, I need to have just few strings in Localizable.strings file, that I need to be different. And I don't want to copy whole huge Localizable.strings file to each project just because of the fact it has few lines different.
It is required for me to have just 1 strings file because of third-party libraries/SDK that are included in project. So I cannot use tableName for localizedString.
The problem is - I need to have a flexible possibility to override just few lines from Localizable.strings for each target separately. And I don't like the idea just to copy whole file to each target, cause it will lead to annoying flow in the future, in case I will have 10 targets and I need to add 1 string to all of them.
The goal is to have 1 huge Localizable.strings file with all strings included, that would be common for all targets, and have small configuration for each target for the strings that should tell different. So target's file should kinda merge and override the one that is common.
AFAIK it is not natively supported by Xcode, so I'm probably looking for a script that would make it works.
So, script should look into common and target's Localizable files, merge them, and in case some keys are defined in both, then it should use the one from target's file.
Can anyone help me with such script?
P.S. Similar issue exists with .xcassets, and CocoaPods solves it by merging multiple assets into 1, and it works as expected - if some targets has an asset containing the image with the same name that is already included into a common asset, then the one from target will replace it.
P.S.2. Similar feature is natively supported for Android devs - each image, each translations can be overridden by "child" flawor, or whatever it is called :)
TL;DR:
Example project: https://github.com/JakubMazur/SO45279964
OK, the easier thing to do would be shell/python script, because it will work for every build server. I assume that you have a different scheme for each target (otherwise it will make no sense). So what you can do is:
Let's say your target is named:
target1
target2
target3
1) Create separate files contains all the strings that should be different (i will put it under Localizable directory.
Your Localizable.strings file may look like this:
"someGeneralString" = "General string 1";
"AppName" = "This is a string that you probably need to change";
"someOtherGeneralString" = "General string 2";
And any of your targetX.strings file may look like this:
"AppName" = "target[x]"
And here is how it should look like in your project:
Note that your target localizable files should has target membership set only to one target, but your Localizable.strings should be for all targets!
That's all for project configuration. Let's go to scripting (I will use python for that):
#!/usr/bin/python
import sys
supportedLanguages = ["en","pl"]
commonPath = ".lproj/Localizable.strings"
keys = ["AppName"]
class CopyLocalizable():
target = ""
def __init__(self,arg):
self.target = arg
self.perform()
def perform(self):
for lang in supportedLanguages:
pathToLocalizable = lang+commonPath
textToFile = ""
with open(pathToLocalizable,"r") as languageFile:
for line in languageFile.readlines():
for key in keys:
if key in line:
textToFile += self.foundAndReplace(key,lang)
else:
textToFile += line
self.saveInFile(pathToLocalizable,textToFile)
def foundAndReplace(self,key,lang):
pathToTargetFile = "Localizable/"+lang+".lproj/"+self.target+".strings"
with open(pathToTargetFile,"r") as targetFile:
for targetLine in targetFile.readlines():
if key in targetLine:
return targetLine
def saveInFile(self,file,stringToSave):
with open(file,"w+") as languageFile:
languageFile.write(stringToSave)
You can optimize it yourself. It's easier script i can think about to get a job done.
And in the end let's automate it a bit:
- Go to your target
- add a new build phase
- Add a new script:
export PATH="/usr/local/bin:$PATH"
cd SO45279964/
python localize.py target[x]
and watch a magic happen ;)
http://www.giphy.com/gifs/26n6NKgiwYvuQk7WU
Here you can find example project that I've created to run this example:
https://github.com/JakubMazur/SO45279964
To keep it simple, Have a Macro defined for each target in Build Settings & define target specific strings within macro section like
#ifdef __TARGET__
//key values in localizable file
#endif

Azure Storage breaks when I call AZSCloudBlob in Swift

I've followed this article: Azure Storage Client Library for iOS and their sample works. But now I am trying to bring the azure-related code to my own project. I added the framework per instructions and show up like this in my prosect:
But when I uncomment the 3rd line and try to build:
var containerURL: String = ""
let usingSAS = true
var blobs = [AZSCloudBlob]() // uncommenting this results into build error
//var container : AZSCloudBlobContainer = AZSCloudBlobContainer()
//var continuationToken : AZSContinuationToken?
I get this build error:
I've modeled my setting like this:
Any insight on how to fix this!?
I'm running Xcode 7.2 and iOS 9.3
the solution was to make sure that you are pointing to usr/lib/libxml2.2.tbd
I think it inherently calls other sibling libs where if it was in another folder it can't.

Localize iOS App name in Unity

I'm'developing a Unity3D game that shows a different (localized) app name in the iPhone's home screen according to the user local language. Note that:
I already know how to localize the iOS app name by editing the Xcode project (create a InfoPlist.string file, localize it, add the CFBundleDisplayName key to it, etc.)
I also know how automatically localize an Android app name within the Unity editor (add a values-XX.xml file with the app_name property onto Assets/Plugins/Android/res/ folder, etc.)
The question is: how can I automatically localize my iOS app name within the Unity Editor so that I don't need to perform the error-prone task 1. every time I build the project?
I think that PostprocessBuildPlayer should be the way to go, however I haven't found any documentation on how to parse it and/or modify the Xcode project file correctly to achieve this.
Long time ago I ran into trouble when I tried to modify info.plist via the Build Player Pipeline especially when doing it in Append mode. It works only once and then subsequent builds fail with "The data couldn’t be read because it isn’t in the correct format." (s. Unity forum posts like this one and my blog posting about this problem) So I decided to take the alternative way combining a customised build with an Xcode Build Pre-action.
Three steps are required:
(1) Xcode setup:
In Xcode go to Edit Scheme / Build / Pre-actions. Then click the + sign to add a New Run Script Action.
In Provide build settings select Unity-iPhone.
Paste . ${PROJECT_DIR}/modify_info_plist.sh (note the dot and blank at the beginning, is ensures that the script is executed in the caller's shell)
So it should look like this:
(2) Script modify_info_plist.sh:
Within your script you have access to all environmet variables from Xcode (s. Xcode Build Setting Reference) and you can manipulate Info.plist using the defaults command (man page). Here is a sample I used to add gyroscope to the UIRequiredDeviceCapabilities:
# Code snippet used in Unity-iPhone scheme as "Build Pre-Action"
my_domain=${PROJECT_DIR}/Info.plist
status_bar_key=UIViewControllerBasedStatusBarAppearance
logger "Start adding keys to info.plist"
defaults write $my_domain $status_bar_key -boolean NO
if [ `defaults read $my_domain UIRequiredDeviceCapabilities | grep "gyroscope" | wc -l` = "0" ]; then
defaults write $my_domain UIRequiredDeviceCapabilities -array-add "gyroscope"
fi
logger "Keys added to info.plist successfully"
(3) Build Pipeline:
Put the following code in a static editor class to create a new menu item Tools / My iOS Build with shortcut cmd+alt+b:
static string IOSBuildDir= "Develop";
[MenuItem("Tools/My iOS Build %&b")]
public static void IOSBuild () {
string[] levels = { "Assets/Scenes/Boot.unity",
"Assets/Scenes/Level-1.unity",
// ...
"Assets/Scenes/Menu.unity"
};
string path = Directory.GetCurrentDirectory ();
path += "/" + IOSBuildDir + "/Info.plist";
if (File.Exists (path)) {
Debug.Log ("Removing file " + path);
File.Delete (path);
}
BuildPipeline.BuildPlayer (levels, "Develop", BuildTarget.iPhone,
BuildOptions.AcceptExternalModificationsToPlayer);
}
I know this is no perfect solution but it's the only one I found to work stable. Two drawbacks:
Step (1) has to be repeated after major Xcode format changes
New scenes have to be appended in the editor class code in step (3)

Resources