iOS XCode: How to load a value from a user-defined setting from build settings at run time - ios

I've got the following constant in the code:
static NSString* const MyUrl = #"www.myurl.com";
Is it possible at all to create a user-defined setting and assign a value which can replace the value of MyUrl const at run time or during archive?
My situation is the following: I have a project with various targets. Each target points to a different URL in the code. It would be great if I could manage the URL through a user-defined setting rather than having to change the code every time I change the target.

Consider using info.plist for storing such values.

No you could not change the value of const variable.
You can change the value of URL string by simply NSString *myURL = #"www.url.com";
and use the global variable in AppDelegate and use that from any where in project.

You can use pre processor macros for this purpose. Goto to Xcode>Project>Target>Build Settings>Preprocessor Macros. Then for every build configuration (Debug, release, etc) add a target specific macro. In your source code, you can now identify your target, by just referring to the macro using #ifdef.
Example:
#ifdef TARGET1
static NSString* const MyUrl = #"www.myurl.com";
#endif
#ifdef TARGET2
static NSString* const MyUrl = #"www.myur2.com";
#endif
The following image depicts the declaration of TARGET macro in build settings-

I think you need to use a database or any other source out of your app. And fetch the URL string whenever it changes and you can use your NSString variable inside the code. You can use parse for your need (However, Parse will stop their service next year but there are lots of free online DBs you can use). With this method you don't need to change your code to update URL strings. I hope I could get what you need correctly.

Related

How can I differentiate between multiple targets in xcode at runtime

Working on an old objective c application where in I need to create multiple targets. Question is how do I differentiate between multiple targets run time in the code and accordingly I need to load the resources from bundle.
Project > Build Settings > Preprocessor Macros
define there different macros for different targets e.g.:
TARGET_1
TARGET_2
and in code you can diferenciate it like this:
NSString *pathToMyResource = nil;
#ifdef TARGET_1
pathToMyResource = #"pathToMyResourceForTarget1";
#else
pathToMyResource = #"pathToMyResourceForTarget2";
#endif
EDIT: added swift syntax
#if DEBUG
let apiKey = "KEY_A"
#else
let apiKey = "KEY_B"
#endif
see here: Swift 3: how to use PREPROCESSOR Flags (like `#if DEBUG`) to implement API keys?
You can use #matloob's answer. Below is an another approach.
You can also use Preprocessing for differentiating among targets.
Please have a look at following tutorial. This may also help you.
Reference :
Target Differentiation dynamically - Appcoda

Get Cocoa Touch Framework project version string in Swift

Cocoa Touch Frameworks provide support for versioning, which can be found in Build Settings under the Versioning section.
To access this value at runtime, we can use the FrameworkVersionNumber and FrameworkVersionString[] variables which are auto-generated for us as part of the build process.
When working with a Swift project, these can be found auto-generated at the top of the Objective-C compatibility header:
//! Project version number for Framework.
FOUNDATION_EXPORT double FrameworkVersionNumber;
//! Project version string for Framework.
FOUNDATION_EXPORT const unsigned char FrameworkVersionString[];
However, whilst FrameworkVersionNumber is accessible from Swift, FrameworkVersionString[] is not. In fact looking at the contents of the framework module, I can see that only the first variable is exposed to Swift:
//! Project version number for Framework.
var FrameworkVersionNumber: Double
The problem with this is that since FrameworkVersionNumber is a Double, any version numbers like 3.2.1 simply get changed to 3.200000...
Does anyone know whether this is a flaw in my project setup, a bug in Xcode, or whether there is a way of getting the framework version in Swift as a String or array, so that I can provide more granular versioning than major.minor?
I have actually found a potential workaround for this issue, it's not so clean but it does work:
By default, when Xcode creates a framework it sets the Version to 1.0 and the Build to $(CURRENT_PROJECT_VERSION) which is great, because this value is actually being copied from the Current Project Version field in Build Settings > Versioning.
So what you can do to get this value at runtime is as follows:
let bundle = NSBundle(identifier: "com.yourframework.Framework")! // Get a reference to the bundle from your framework (not the bundle of the app itself!)
let build = bundle.infoDictionary![kCFBundleVersionKey] as! String // Get the build from the framework's bundle as a String
This does work but it feels quite circuitous for something that used to (I believe) be readily accessible from a variable in Objective-C.
IMPORTANT UPDATE - OCT 2021 - XCODE 13
When submitting an app to the App Store, Xcode 13 has a new option called "Manage Version and Build Number" which is ticked by default. If left checked, Xcode will automatically set your app's version number which (rather counter-intuitively), will also apply to all included frameworks. In other words, if your app version is 1.0, your framework version will be overwritten with 1.0.
Make sure you disable this option to avoid your framework version being overwritten.
You can also opt-out of this new behaviour by setting manageAppVersionAndBuildNumber in your export options plist.
For further details, see this discussion on the Apple Developer Forums.
These variables are populated in an automatically generated .c file when building from the project's CURRENT_PROJECT_VERSION. It looks like this:
extern const unsigned char FrameworkVersionString[];
extern const double FrameworkVersionNumber;
const unsigned char FrameworkVersionString[] __attribute__ ((used)) = "#(#)PROGRAM:Mark2SDK PROJECT:Framework-1" "\n";
const double FrameworkVersionNumber __attribute__ ((used)) = (double)1.;
The C array doesn't make it to Swift for some reason. Modifying the array definition to a pointer causes an EXC_BAD_ACCESS crash. What you can do though is create a pointer to the array and use that. In Framework.h:
//! Project version string for Framework.
FOUNDATION_EXPORT const unsigned char FrameworkVersionString[];
// add this
extern const unsigned char * FrameworkVersionStringPtr;
Then, either by creating Framework.c or in another c or m file, add this:
#import "Framework.h"
const unsigned char * FrameworkVersionStringPtr = FrameworkVersionString;
You can then use the string pointer in Swift to get the version:
func version() -> String? {
let ver = String(cString: Mark2SDKVersionStringPtr)
guard let range = ver.range(of: "-") else {
return nil
}
return String(ver[range.upperBound...])
}
print(version())
// 1.0.1
for swift 4.2 it works:
if let bundle = Bundle(identifier: "com.ingconti.SampleFramework") {
if let build = bundle.infoDictionary?["CFBundleShortVersionString"] {
print(build)
}
}

Setup different target for using different constant in Xcode

I know maybe a question is duplicated, but where I can check information. How to setup different targets to build with different bundle name etc.
Right now I know of course how to create different targets in Xcode, it is very simple to copy it from example from first target that was created automatically when I created project.
But I also have Constant.h and Constant.m files. I want to handle constants depend on which target I build for.
Let's say when build for target A I need to setup NSString const *toEmail = #"a#test.com", but in case if I build for the target B then toEmail = #"b#test";
Do I need to create two difference Constant files say ConstantA.m and ConstantB.m or maybe there is another best practice here. I don't want to recreate a wheel )
I setup Preprocessor Macro in target build settings for just one target. (For Example: TARGET_B)
And in code i check using
// Check if it's target B:
#if TARGET_B
NSString const *toEmail = #"b#test.com"
#else
NSString const *toEmail = #"a#test.com"
#end

iOS and XCode: What is the best way to store connection settings (i.e. URLs) for an iOS app?

We started with #defines pointing our app to dev, qa and live API servers. Well, that's rather lazy, not to mention painful to manage.
I'm thinking of storing default URL/connection settings in a .plist and creating dev|stage|live build configurations that will use the corresponding .plist for connections.
Is there a cleaner way to do this?
How secure is a .plist?
Here's how I handle it.
I have a single file where i store information like this. Lets call this file Configuration{h,m}
Within my .h (header) file, i declare the constants like this:
#pragma mark - API
extern NSString * const kAPIHost;
extern NSString * const kAPIVersion;
Within my .m (implementation) file, I finish it off with this
#pragma mark - API
#if TARGET_IPHONE_SIMULATOR
NSString * const kAPIHost = #"http://localhost/";
#else
NSString * const kAPIHost = #"http://liveserver.com";
#endif
NSString * const kAPIVersion = #"v2/";
What happens here is, it checks to see if i'm running on the sim, if i am, then it uses localhost, if it's running on device, it uses the live server.
Finally, I import Configuration.h in my precompiled header (PCH) file.
#import Configuration.h
Now, this file is available throughout your application without needing to import it in every single file. I use it like this.
NSURL * url = [NSURL URLWithString:[kAPIHost stringByAppendingString:[kAPIVersion stringByAppendingString:#"apiEndPoint/"]]];
The comment that troop231 posted sounds very interesting though. Maybe something I can implement at some time in the future. Always looking for more secure ways to do things :)
You could store this in keychain - it's secure - but I don't know why You would want to do that.
You could use extern constants instead of defines:
extern NSString *const in a class.
You can use preprocessor directives to help You manage the addresses:
http://www.techotopia.com/index.php/Using_Objective-C_Preprocessor_Directives
You could create a singleton class that would for example read these urls from .plist at start and then hand it over to any class that is interested.
About security:
using singleton with .plist and shipping app would not reveal Your test / dev addresses.
using #define, externs and preprocessor directives i.e. #ifdef DEBUG would also not reveal Your dev / test addresses in production code.

iOS: How to add conditionality based on Project Target

I am building a project that will eventually be compiled to make 4 slightly different applications. The main change I have to make are image changes, but I also want to change a few UILabels as well.
I know that I could create multiple XIB files and modify them to be attached to each target, but I would is there a way to use a macro #define if to detect the name of the target?
Example: Project: Project1
Targets: TargetA, TargetB
#define if Target = TargetA{
label.text = #"This is targetA";
}
Is this possible?
Thanks!
I found an easy way to do this... you can access the bundle identifier which is unique for each target:
NSLog(#"%#", [[NSBundle mainBundle] bundleIdentifier]);
C preprocess macro will accomplish this feature.
In Xcode:
TARGET->Build Settings->Preprocess Marcos
And your source code:
#if DEBUG
// debug build
#else
// release build
#endif
This is already defined macro. You can define your own macros in all project targets.

Resources