Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 703
Jonathan Ellis Avatar asked Jul 14 '15 22:07

Jonathan Ellis


People also ask

How do I check Xcframework version?

For xcode version click xcode>about xcode. For the framework just open a header file from that framework, usually in the comments on top the version is mentioned.

What is Cocoa framework in Swift?

Cocoa is a set of object-oriented frameworks that provides a runtime environment for applications running in OS X and iOS. Cocoa is the preeminent application environment for OS X and the only application environment for iOS.

What is Cocoa Touch class in Swift?

Cocoa and Cocoa Touch are the application development environments for OS X and iOS, respectively. Both Cocoa and Cocoa Touch include the Objective-C runtime and two core frameworks: Cocoa, which includes the Foundation and AppKit frameworks, is used for developing applications that run on OS X.


2 Answers

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.

like image 150
Jonathan Ellis Avatar answered Oct 27 '22 00:10

Jonathan Ellis


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
like image 20
Nick Avatar answered Oct 27 '22 01:10

Nick