What Does It Mean to "Weak-Link" a Framework

Weak link framework

You are getting that error because you are building against a version of the SDK that does not implemement the MessageUI framework.

What you need to do is to build for iPhone OS 3.0, but in the build settings for your target set the iPhone OS Deployment Target to iPhone OS 2.0 (or whatever minimum version you'd like to support with your final application). This way, you weak-link against the newer framework, but can still deploy the application to older devices.

iOS Framework weak link: undefined symbols error

From the description of your issue, I assume you already know how to make Xcode weakly link against the framework by setting it as 'Optional'.

You have two problems to solve: Class availability and symbol availability. Apple covers this in the Framework Programming Guide: Frameworks and Weak Linking and the SDK Compatibility Guide: Using SDK Based Development

Class availability

This is pretty straightforward: use NSClassFromString() to see if the class is available in the current environment.

if (NSClassFromString("CLLocationManager") != NULL){

If the class is available it can be instantiated and sent messages, otherwise it cannot.

Symbol availability

What you are specifically interested in is using constants or structs from a weakly linked framework. A C-style function would be similar, but those are not a concern when using CoreLocation. We'll use a CoreLocation constant as an example.

Every time you use it you MUST check to make sure it exists:

if (&kCLErrorDomain != NULL){
// Use the constant
}

Note that the & takes the address of the constant for the comparison.
Also note that you CANNOT do this:

if (kCLErrorDomain){
// Use the constant
}

Or this:

if (&kCLErrorDomain){
// Use the constant
}

Constant symbols can also be lookup up at runtime using dlsym.
Using the negation operator in this way will not work. You must check the address of the constant against the NULL address.

I have put a very simple example of an application that is using a static library which weak links against Core Location on GitHub. The example application does not link against Core Location:

TestApp

But the dependency does, as an optional (weak) framework:

optional framework

Does bitcode support weak linking third party frameworks?

In your case, you will need to wait for the fix in framework itself.

We've got similar issue while distributing our framework, which I described here and I just wan't to share results of our investigation, because seems that no-one has published their results.

No needs to distribute without bitcode. Long story short, there were LLVM instrumentation included, which prevents AppStore processing. I've written a whole blog about XCode 12 and BigSur issues with XCFramework.

To sum up, here is a few required steps to make sure while creating XCFramework for distribution:

  • Using archive builds is a MUST, release build isn't enough
  • BUILD_LIBRARY_FOR_DISTRIBUTION must be set to YES
  • SKIP_INSTALL must be set to NO
  • GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO to turn off GCC instrumentation and remove them from the binary
  • CLANG_ENABLE_CODE_COVERAGE = NO to turn off code coverage tools from the binary

Having all of the above helped to solve our preparing and distribution problem and hopefully save you some time if you happened to face same issues as we did.

What is a “weak framework reference”?

In practice, if you build an app with a required reference to a framework, rather than a weak reference, and try to run that app on a device which doesn't include that framework, the app with crash, even if you don't try to use that framework.

If, however, you build an app with a weak reference to a framework, and run it on a device that does not support that framework, and do not access classes (methods, subroutines, etc.) in that framework (because you, say, run-time tested for the existence of that framework beforehand) then your app will not simply crash on startup. But since the framework is weakly linked, you could call that framework from that same app on a device which supported that framework. Thus your app might support different OS versions with and without support for a given framework.

There might also be some performance differences in start up time causing apps with weak references to launch much slower.

How do I weak link frameworks on Xcode 4?

Go to your project -> Targets -> Build Phases -> Link Binary with Libraries.

Then change the library you want to weak-link from "Required" to "Optional".

Weak Link My Own Objective-C Class

To weak link a class e.g. MyUploadManager in your own executable:

  1. To keep the linker happy, add this to Other Linker Flags in the project:

    -Wl,-U,_OBJC_CLASS_$_MyUploadManager

    This allows the class symbol to be undefined even if it is not built into your executable. It will be considered for dynamic lookup instead, effectively the same as a dynamic library symbol.

  2. To keep the runtime happy, add this to your class header:

    __attribute__((weak_import)) @interface MyUploadManager

    When the dynamic linker runs, it substitutes a nil for the class symbol rather than crashing.

Now you can run this without either linker or runtime errors:

if ([MyUploadManager class]) {
self.uploadButton.hidden = NO;
}

Note: As of Xcode 7, the -U linker options conflicts with BitCode, so you may not be able to use this technique for future projects.

Cocoa - weak linking against framework - ignoring version?

@eww and I have continued investigating this, and here's what we were able to find. When the framework and application are being compiled, they are referencing a specific version of R.framework. We were able to see this by running:

otool -L /Applications/StatTag.app/Contents/Frameworks/RCocoa.framework/RCocoa 
/Applications/StatTag.app/Contents/Frameworks/RCocoa.framework/RCocoa:
@rpath/RCocoa.framework/Versions/A/RCocoa (compatibility version 1.0.0, current version 1.0.0)
**/Library/Frameworks/R.framework/Versions/3.3/Resources/lib/libR.dylib (compatibility version 3.3.0, current version 3.3.2)**
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 1252.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1253.0.0)

After some searching, this post came up which clued us in on how to change the R.framework reference to be "Current" instead of the specific version

install_name_tool -change /Library/Frameworks/R.framework/Versions/3.3/Resources/lib/libR.dylib /Library/Frameworks/R.framework/Versions/Current/Resources/lib/libR.dylib /Applications/StatTag.app/Contents/Frameworks/RCocoa.framework/RCocoa

We can see now that it's referencing the Current version folder:

otool -L /Applications/StatTag.app/Contents/Frameworks/RCocoa.framework/RCocoa 
/Applications/StatTag.app/Contents/Frameworks/RCocoa.framework/RCocoa:
@rpath/RCocoa.framework/Versions/A/RCocoa (compatibility version 1.0.0, current version 1.0.0)
/Library/Frameworks/R.framework/Versions/Current/Resources/lib/libR.dylib (compatibility version 3.3.0, current version 3.3.2)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 1252.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1253.0.0)

What tripped us up a little more is when this gets built into our final application, there are other local frameworks referencing RCocoa and R, which meant we had to run install_name_tool multiple times. Happy to report things are now working.



Related Topics



Leave a reply



Submit