Get Cocoa Touch Framework project version string in Swift

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

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.


for swift 4.2 it works:

if let bundle = Bundle(identifier: "com.ingconti.SampleFramework") {
       if let build = bundle.infoDictionary?["CFBundleShortVersionString"] {
           print(build)
       }
}