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)
}
}
Related
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.
Any of you guys had errors when building your iOS project after updating to the new Google Cast 2.6.0 framework?
I get the following error:
Default initialisation of an object of const type ‘const NSInteger’ (aka ‘const int’).
at the following lines in class GCKDevice:
/** Device capability flag for video out. */
GCK_EXPORT const NSInteger kGCKDeviceCapabilityVideoOut;
/** Device capability flag for video in. */
GCK_EXPORT const NSInteger kGCKDeviceCapabilityVideoIn;
/** Device capability flag for audio out. */
GCK_EXPORT const NSInteger kGCKDeviceCapabilityAudioOut;
/** Device capability flag for audio in. */
GCK_EXPORT const NSInteger kGCKDeviceCapabilityAudioIn;
There is a bug in this version of the SDK for files compiled under Objective-C++. If you import the classes from a .mm file, there is a missing extern which will trigger a problem. As a workaround, if you can move you code to a regular .m file you should be OK (and you should be able to call out to that as long as you don't expose the GCK headers to the mm).
For me it helped to switch the "No Common Blocks" compiler setting to NO:
It pretty much seems to make sense, this setting defines how duplicate variable definitions are handled by the compiler. It is explained here: What is GCC_NO_COMMON_BLOCKS used for?
I'm trying to create an embedded framework for use with iOS8. After creating one called SampleKit (BTW; is there any convention here, should I used a prefix?), it contains a header file that is puzzling me:
//! Project version number for SampleKit.
FOUNDATION_EXPORT double SampleKitVersionNumber;
//! Project version string for SampleKit.
FOUNDATION_EXPORT const unsigned char SampleKitVersionString[];
I know that FOUNDATION_EXPORT is a macro for extern or extern "C", but I'm not sure about the two constants. Where am I supposed to set the value for them?
Project > Build Settings > Versioning > Current Project Version :
The WWDC 2013 lecture slides contain a section that in some cases the explicit bridging casts can be omitted. I wrote this in Xcode 5 (using 10.8 though, not 10.9), and the compiler complains I need a bridging cast. Am I just totally not understanding the concept?
#import <Foundation/Foundation.h>
CF_IMPLICIT_BRIDGING_ENABLED
CFStringRef MyCreateStringFromNothing();
CF_IMPLICIT_BRIDGING_DISABLED
void SomeFunction() {
// compiler requires bridging cast here...
NSString* x = MyCreateStringFromNothing();
}
The reason I ask is I wanted to write:
NSString* s = CFUUIDCreateString(NULL, uuid);
and thought that the new work on implicit bridging should simply enable me to write this. But this also needs a bridging cast.
This appears to be sort of black magic, but from my experiments functions with "Create" in them don't work with implicit bridging.
When I compiled this:
CF_IMPLICIT_BRIDGING_ENABLED
CFStringRef MyCreateStringFromNothing();
CFStringRef MyGetStringFromNothing();
CF_IMPLICIT_BRIDGING_DISABLED
void SomeFunction() {
NSString *const fails1 = MyCreateStringFromNothing();
NSString *const fails2 = CFStringCreateCopy(kCFAllocatorDefault, CFSTR("FOO"));
NSString *const works = MyGetStringFromNothing();
}
The first two fail but the third one "works."
Note that implicit bridging apparently STILL does NOT actually keep the compiler from warning you about converting CFStringRef to NSString *, it just stops requiring you to insert a __bridge as well.
You'll notice that in slide at WWDC2013 in session 404 at 50:30, he uses CFDictionaryGetValue(), which returns a "void *", so it's one of the few functions that won't raise a warning. Any of the CF functions that return an actual type (e.g. CFStringRef, or CFDictionary) are still going to throw warnings, so, meh.
Check the compiler settings on the project and/or target. If this is an older project, you might have a stray option there causing you to use an older compiler. (This tripped me up a few times when I moved to Xcode 5 and wanted to use the latest Obj-C newness.)
These days, I always choose "Default compiler" in my build options.
I followed Claus's post to set up code coverage on Xcode 4.2 with LLVM 3.0. I'm able to see test coverage files, but they're only for my unit test classes, not my actual project classes. I've tried setting Generate Test Coverage Files and Instrument Program Flow to Yes on my main target, but that didn't help, as it failed with the following error:
fopen$UNIX2003 called from function llvm_gcda_start_file
To clarify, I don't think that's even the right approach - I just tried it to see if it would generate code coverage on my project classes.
At this point, I'd be happy to try anything that gets code coverage working on my app. Any suggestions?
You are expecting linker problem, profile_rt library uses fopen$UNIX2003 and fwrite$UNIX2003 functions instead of fopen and fwrite.
All you need is to add the following .c file to your project:
#include <stdio.h>
FILE *fopen$UNIX2003( const char *filename, const char *mode )
{
return fopen(filename, mode);
}
size_t fwrite$UNIX2003( const void *a, size_t b, size_t c, FILE *d )
{
return fwrite(a, b, c, d);
}
This code just remaps the missing functions to standard ones.
Note on $UNIX2003 suffix:
I've found an Apple document saying:
The UNIX™ conformance variants use the $UNIX2003 suffix.
Important: The work for UNIX™ conformance started in Mac OS 10.4, but was not completed until 10.5. Thus, in the 10.4 versions of libSystem.dylib, many of the conforming variant symbols (with the $UNIX2003 suffix) exist. The list is not complete, and the conforming behavior of the variant symbols may not be complete, so they should be avoided.
Because the 64-bit environment has no legacy to maintain, it was created to be UNIX™ conforming from the start, without the use of the $UNIX2003 suffix. So, for example, _fputs$UNIX2003 in 32-bit and _fputs in 64-bit will have the same conforming behavior.
So I expect libprofile_rt to be linked against 10.4 SDK.
I use CoverStory http://code.google.com/p/coverstory/ a GUI for .gcda and .gcno files.
The documentation explains the settings needed to generate these files http://code.google.com/p/coverstory/wiki/UsingCoverstory.