How to resolve symbol name conflict in a Cocoa touch framework - ios

I developed a Cocoa touch framework and am having problems with third party static framework classes which are embedded inside of it.
The problem is symbol collisions when consumer projects use my framework and also import the third party static framework which my framework uses.
I eventually want to remove these classes from my framework since they are conflicting with host project classes (they use same third party framework) and somehow tell my framework to rely on main project third party framework (I will instruct devs to import the framework),
Or alternatively I will add a prefix to these classes so that when hosting projects embed my framework and use the same third party framework as my own framework it won't get a symbol collisions
Any help or direction will be welcomed!

CocoaPods can help you to resolve the problem with duplicate symbols.
Below I provided detailed explanations for how to make it happen:
Definitions
Let's make some definitions for simpler explanations:
MyFramework - framework that you are developing.
MyApplication - application that uses MyFramework.
OtherFramework - third-party framework that is used in MyFramework and MyApplication.
Problem
As I understand the problem is that Xcode fails to build with "duplicated symbols" error in OtherFramework.
Solution
Here are conditions that you need satisfy to fix that problem:
1) MyFramework has to refer to OtherFramework by CocoaPods:
// MyFramework Podfile
use_frameworks!
pod "OtherFramework"
2) MyApplication has to refer to OtherFramework by CocoaPods:
// MyApplication Podfile
use_frameworks!
pod "OtherFramework"
3) MyApplication can use any mechanism to refer to MyFramework (by CocoaPods or by Drag&Drop framework to project).
4) OtherFramework has to be built with CocoaPods.
If it's not built with CocoaPods yet, you can make it yourself.
For this goal you need to create OtherFramework.podspec and optionally submit it to CocoaPods private repository. It doesn't matter if you have source files or just OtherFramework.framework bundle. More details for building CocoaPod here.

TL;DR
Using dynamic frameworks, you should not have to care much about it, as the linker uses a sensible default behaviour. If you really want to do what you ask, you could instruct the linker to do so, at the risk of failure at run-time. See towards the end of the answer for an explanation.
In general, regarding static linking
This is another version of the classic "dependency hell" problem. Theoretically, there are two solutions when linking object files statically:
State your dependency and let the user of your framework solve it in their build system. Some ideas:
External systems such as CocoaPods and Carthage will help you at the downside of forcing constraints onto your user's build system (i.e. the user might not use CocoaPods).
Include the dependency and the header files for it, instructing your users not use your provided version of that dependency. The downside is of course that your user cannot switch the implementation should they need to.
(Maybe the easiest). Build two versions of your framework, one with the dependency library not linked in. The user can then choose wheter to use your provided version or their own. The downside is that there is no good way of determining if their version will be compatible with your code.
Avoid leaking your dependency and encapsulate it within your framework, at the expense of larger code size. This is usually my path of preference these days now that code size is not a real problem even on mobile devices. Methods include:
Include the dependency as source files in your project. Use #define macros to rename the symbols (or using only static symbols).
Building your dependency from source, renaming the symbols in a code pre-build (or one off) step.
Building everything as you were, then renaming the "internal" symbols in the built library. This could potentially cause bugs, especially since swift/obj-c have dynamic aspects. See this question, for example.
This post discusses some of the pros and cons of the different alternatives.
When using dynamic frameworks
If you are building a dynamic framework, best practice is to do "2." above. Specifically, linking dynamically will prevent the duplicate symbol problem, as your library can link to its version of the third party library regardless of the library any client uses.
Here is a simple example (using C for simplicity, but should apply to Swift, ObjC, C++ or anything linked using ld):
Note also that I assume your third party library is written in C/objC/C++, since Swift classes can (AFAIK) not live in static libraries.
myapp.c
#include <stdio.h>
void main() {
printf("Hello from app\n");
third_party_func();
my_dylib_func();
}
mylib.c
#include <stdio.h>
void my_dylib_func() {
printf("Now in dylib\n");
third_party_func();
printf("Now exiting dylib\n");
}
thirdparty_v1.c
#include <stdio.h>
void third_party_func() {
printf("Third party func, v1\n");
}
thirdparty_v2.c
#include <stdio.h>
void third_party_func() {
printf("Third party func, v2\n");
}
Now, let's first compile the files:
$ clang -c *.c
$ ls *.o
myapp.o mylib.o thirdparty_v1.o thirdparty_v2.o
Next, generate the static and dynamic libraries
$ ar -rcs libmystatic.a mylib.o thirdparty_v1.o
$ ld -dylib mylib.o thirdparty_v1.o -lc -o libmydynamic.dylib
$ ls libmy* | xargs file
libmydynamic.dylib: Mach-O 64-bit dynamically linked shared library x86_64
libmystatic.a: current ar archive random library
Now, if we compile statically using the (implicitly) provided implementation:
clang -omyapp myapp.o -L. -lmystatic thirdparty_v2.o && ./myapp
Hello from app
Third party func, v1
Now in dylib
Third party func, v1
Now exiting dylib
Now, this was quite surprising to me, as I was expecting a "duplicate symbol" error. It turns out, ld on OSX silently replaces the symbols, causing the user's app to replace the symbols in my library. The reason is documented in the ld manpage:
ld will only pull .o files out of a static library if needed to resolve some symbol reference
This would correspond to point 1. above. (On a side note, running the above example on Linux sure gives a "duplicate symbol" error).
Now, let's link dynamically as in your example:
clang -omyapp myapp.o -L. -lmydynamic thirdparty_v2.o && ./myapp
Hello from app
Third party func, v2
Now in dylib
Third party func, v1
Now exiting dylib
As you can see, your dynamic library now refer to its version (v1) of the static library, while the app itself will use the other (v2) version. This is probably what you want, and this is the default. The reason is of course that there are now two binaries, each with its own set of symbols. If we inspect the .dylib, we can see that it still exports the third party library:
$ nm libmydynamic.dylib
0000000000000ec0 T _my_dylib_func
U _printf
0000000000000f00 T _third_party_func
U dyld_stub_binder
And sure, if we don't link to the static library in our app, the linker will find the symbol in the .dylib:
$ clang -omyapp myapp.o -L. -lmydynamic && ./myapp
Hello from app
Third party func, v1
Now in dylib
Third party func, v1
Now exiting dylib
Now, if we don't want to expose to the app that we use some static library, we could hide it by not exporting its symbols (hide it as in not letting the app accidentally reference it, not hiding it truly):
$ ld -dylib mylib.o -unexported_symbol '_third_party_*' thirdparty_v1.o -lc -o libmydynamic_no3p.dylib
$ nm -A libmydyn*
...
libmydynamic.dylib: 0000000000000f00 T _third_party_func
libmydynamic_no3p.dylib: 0000000000000f00 t _third_party_func
(A capital T means the symbol is public, a lowercase t means the symbol is private).
Let's try the last example again:
$ clang -omyapp myapp.o -L. -lmydynamic_no3p && ./myapp
Undefined symbols for architecture x86_64:
"_third_party_func", referenced from:
_main in myapp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Now we have successfully hidden our third party static framework to client apps. Note that, again, normally you won't have to care.
What about if you really want 1. in dynamic frameworks
For example, your lib might need the exact version of the third party library that your client provides.
There is a linker flag for that too, of course: -undefined dynamic_lookup.
$ ld -dylib mylib.o -undefined dynamic -lc -o libmydynamic_undef.dylib
$ clang -omyapp myapp.o -L. -lmydynamic_undef thirdparty_v2.o && ./myapp
Hello from app
Third party func, v2
Now in dylib
Third party func, v2
Now exiting dylib
The downside is of course that it would fail at run-time if your client fails to include the static library.

You can always use classes from framework like this:
import Alamofire
let request = Alamofire.Request(...)
And if you have Request class in your own framework, you can use it in the same way:
import YourFramework
let request = YourFramework.Request(...)
There there would not be any conflicts.

I had a similar issue before, but I was using Objective-C third party framework. The problem is solved by using the bridging header to specifically import the framework drag and drop to the consumer project, and leave your framework sort of capsulated. It's just my experience so it might not apply to your case but might as well share it here just in case it helps.

Related

Objective-C / iOS - Resolve duplicated symbols inside a static library

My iOS project used a 3rd party (not open source) static library (i.e. libA.a), and this libA.a used CocoaLumberjack, it compiled CocoaLumberjack directly into itself, and the version of CocoaLumberjack is unclear.
Now I also want to use CocoaLumberjack to track logs in my program, and it will result duplicate symbol errors if I install CocoaLumberjack via CocoaPods.
Questions:
Is there a way to hide
the CocoaLumberjack symbols in libA.a so that Xcode won't report symbol errors?
Any other file logger libraries that can be recommended?
Now I am looking through symbols in libA.a, contrasting it with the source of CocoaLumberjack, and I am closed to find the version of CocoaLumberjack libA.a used, my next step should be only including header files of CocoaLumberjack in my project. It should work, but I don't like this way.
You can unpack the object files from the static library and repack it without the CocoaLumberpack object files.
Something like:
$ ar x libA.a
$ rm cococaLumberjackFile*.o # Whatever they are called
$ ar cr libA.a *.o
If the static library is fat (contains multiple CPU architectures) then this becomes much more difficult and involves lipo and much pain.
EDIT: Just go ahead and use CocoaLumberjack in your code and link with libA.a. It will provide the objects for both the 3rd-party library and CocoaLumberjack.

Can the -ObjC flag be applied selectively to static libraries?

TL;DR
How can I make the -ObjC linker flag target a specific static library and not all the static libraries I am linking against in order to avoid unused object files being linked in with my app?
Too Long; Did Read
So you are developing a new iOS app and you add in your homegrown "objcutil" static library which contains a variety of useful Objective-C classes (not implemented as categories) to do various things that have been useful in the past. So far, so good, and only those object files that are being referenced in the utility library are being linked with the app.
Then you decide to integrate the Google Maps SDK which wants you to use the -ObjC Other Linker Flags and all of a sudden dependencies in the utility library fail to be resolved, because you haven't configured Xcode to link to those libraries.
OK I can resolve the missing dependencies easily enough, however you now have unused object files and library dependencies that you don't need and you'd like to be a bit tidier than that.
So how do you avoid OCD overload?
Some reference from the ld manpage:
-ObjC Loads all members of static archive libraries that define an Objective C class or a category. This option does not apply to dynamic
shared libraries.
Xcode Version: 5.1.1
OSX Version: 10.9.4
OK so the answer is to use -force_load instead of -ObjC as -force_load is more focused.
So WRT to the Google Maps SDK, if you followed the instructions and copied the static framework into the app project directory, then the framework will be in the project root directory and you can remove -ObjC from the Other Linker Flags and replace it with
-force_load GoogleMaps.framework/Versions/Current/GoogleMaps:
Nothing else needs changing.
For other libraries you will need to use the full static library path as the argument to -force_load.

Export an `OBJC_CLASS` from one static lib as part of another

I want to create a static library (actually, a framework, but I know how to do that part) that bundles up code from, among other things, another static library. However, the OBJC_CLASS exports from the original library end up as undefined symbols.
For example, in Xcode 5.1.1 (using default settings/choices at every step, unless otherwise specified):
Create a new "iOS Framework & Library Cocoa Touch Static Library" project named LibA.
Build (for either simulator or a real device, doesn't matter).
Create another new "iOS Framework & Library Cocoa Touch Static Library" project named LibB.
Drag libLibA.a from the LibA products to the Frameworks folder in the LibB project tree.
Drag LibA from the include directory next to the static lib to the top level of the LibB project tree.
Edit LibB.h as shown below.
Build (same target as before).
Create a new "iOS Application" (any type) project named AppC.
Drag libLibB.a from the LibB products to the Frameworks folder in the AppC project tree.
Drag LibB from the include directory to the top level.
Drag LibA from the first project's include directory to the top level.
Verify that LibA appears in the Link Binary With Libraries phase.
In any method of any class the wizard generated (e.g., -[MasterViewController awakeFromNib]), add (void)[[LibB alloc] init].
At the top of the .m file you just edited, add #import "LibB.h".
Build.
Here's the LibB.h promised above:
#import <Foundation/Foundation.h>
#import "LibA.h"
#interface LibB: LibA
#end
I get the following error:
Undefined symbols for architecture i386:
"_OBJC_CLASS_$_LibA", referenced from:
_OBJC_CLASS_$_LibB in libLibB.a(LibB.o)
"_OBJC_METACLASS_$_LibA", referenced from:
_OBJC_METACLASS_$_LibB in libLibB.a(LibB.o)
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Looking at the files, the problem is obvious:
$ nm -g libLibB.a
U _OBJC_CLASS_$_LibA
0000031c S _OBJC_CLASS_$_LibB
U _OBJC_METACLASS_$_LibA
00000308 S _OBJC_METACLASS_$_LibB
U _OBJC_METACLASS_$_NSObject
U __objc_empty_cache
The symbols for _OBJC_CLASS_$_LibA and _OBJC_METACLASS_$_LibA are exported as undefined.
I can reference methods, C functions and structs, globals, etc. from LibA. Even categories on Foundation objects (as long as I do the category-dummy trick). It's only the class and metaclass objects that I can't figure out how to export.
Here's what I've tried to fix it:
Turn off Dead Code Stripping (in all three projects).
Add -ObjC as an extra linker flag (in all projects). (This makes no sense for static libs, and all it does is give you a warning error telling you exactly that, but everyone suggests it to me.)
Create an "Exported Symbols File" (for LibB). (This also only makes sense for dynamic libs.)
Pass ${PROJECT_DIR}/libLibA.a as an "Other Linker Flags" (for LibB) instead of adding libLibA as a framework (in case -lLibA is processed differently from libLibA.a).
What I've tried that I still think may be on the right path, but I'm not sure:
Try to figure out appropriate libtool options that have no corresponding settings in Xcode. (I can wrap it in a Makefile, or and Xcode custom build step, if necessary.)
Enable "Perform Single-Object Prelink", then add ${PROJECT_DIR}/libLibA.a to "Prelink libraries". I get warnings about duplicate symbols and then success but with an empty libLibB.a, so obviously there's something else I need to do. I've done this with .dylibs and dynamic Frameworks on OS X, and there wasn't anything else I needed to do there… but never with static libs.
Workarounds that I know about (and I'll use one of these if there's no real solution):
Require that anyone who wants to use LibB also has to add LibA to their project. And, in particular, the pre-built copy of LibA that we provide.
Distribute LibB as source to be included in your project, instead of a static lib and headers.
Manually ar libLibA.a and LibB.o, then ranlib like it's 1999 (although the docs say this doesn't work, it seems to).
(None of these are too terrible for my simple test project, but in real life, this is not an open source project, that LibA is actually 80 different libs from 3 different projects, and some of the LibA code builds fat armv7/armv7s (which means ar doesn't work on it…), and we're planning to do the usual hack of lipo'ing together the simulator and native builds and making a framework out of them, all of which makes things more of a problem.
I think I may have solved it with single-object prelink (basically this means it does an ld -r to build a giant object file, then passes that to libtool), although I'm still not sure, and I don't love the solution. So, I will post what I've got as an answer, but hope someone else comes along with a better answer.
To get single-object prelink to work, you need to (in LibB):
Add libLibA.a as a Framework.
Make sure it does not appear in the Link Binary With Libraries build phase.
Set "Dead Code Stripping" to No.
Set "Don't Dead-Strip Inits and Terms" to Yes.
Set `Perform Single-Object Prelink" to Yes.
Set "Prelink libraries" to ${PROJECT_DIR}/libLibA.a
Set "Preserve Private External Symbols" to Yes.
(The second step is what I was doing wrong earlier…)
Unfortunately, this seems to break the dependency rules completely, so that every build recompiles every .m (and .pch) that's part of the target, even if nothing has changed.
Other than that annoyance, this seems to work for both AppC and my real project just fine.
AppC does not need "Preserve Private External Symbols"; my real project does. I believe this is because one of the third-party libraries does an ld -r with an empty -exported_symbols_list explicitly to "transform all symbols to private_extern. Otherwise, class objects don't end up that way. However, I'm not 100% sure I understand this one.
Adding this to Other Linker Flags appears to work
-force_load $(CONFIGURATION_BUILD_DIR)/libLibA.a

How do I solve class name conflicts when creating an iOS framework?

I am writing an iOS framework Foo that depends on a static third-party library Lib and have problems getting the linking right.
If Foo was a static library, I would not link it against Lib, and only link the master project against both of them. But this approach does not seem to work with a framework: the linker complains about missing symbols from Lib.
So I took the other approach and linked Foo against Lib. Now the linker is happy, but there’s an obvious catch: if the master project uses Lib for its own reasons and links both against Foo and Lib, I get duplicate symbols:
Class <Something> is implemented in both <Here> and <There>.
One of the two will be used. Which one is undefined.
I know I can stop linking the app against Lib and all will be fine, but I’d like to get things right. How?
I was able to get this working for a framework, though the docs say it should work for a static library as well.
What I did is link the master project against both Foo and Lib as you say. Now Foo has "missing symbol" errors. Select the Foo target and go to Other Linker Flags. Add -weak_framework Lib and presto! the linker will stop complaining. The duplicate symbol runtime errors go away.
According to the docs:
The -weak_framework option tells the linker to weakly link all symbols in the named framework. If you need to link to a library instead of a framework, you can use the -weak_library linker command instead

Ho can I use adMob library for iOS without using -all_load linker flag

I'm adding adMob provide by google to my iOS app and I'm stuck on this part of the installation:
Add -all_load under Other Linker Flags in the project build info
If I add that flag, then another third party library breaks giving me the error message:
ld: duplicate symbol _vw_chartype_table_p in /Users/josh/ Projects/app/libs/libvt_universal.a(vw_ctype-3279EF26D0C25F3A.o) and / Users/josh/Projects/app/libs/ libvt_universal.a(vw_ctype-34AB9EC0B46D954C.o) for architecture i386
Is there any way to use the adMob library without using -all_load? For example, I've tried -force_load $(SOURCE_ROOT)/adMob/libGoogleAdMobAds.a
but
ld: file not found: /Users/USERNAME/Desktop/latest/bbbb/APPNAME/adMob/libGoogleAdMobAds.a
The reason Google suggests using -all_load is that they are using categories in their code, and Objective-C libraries with categories are not properly loaded by the llvm linker (well that was the case in 4.3 xcode, not sure about 4.4 with the newer clang).
So, I guess if you are brave you can try to just remove the all_load flag. It should build fine. If the bug is NOT fixed, what will happen is when you run your code, it will crash, since none of the categories the library uses will have been loaded. This might be a good thing to do in any case, as your project should build, and if it does not you can fix those problems first.
What I do suggest you do is use -force_load, which has llvm load the categories in the adMob library (among other things). To use it you MUST have a fully qualified path (ie starts at '/') to the library. Obviously if you use Terminal and run:
ls -l /Users/USERNAME/Desktop/latest/bbbb/APPNAME/adMob/libGoogleAdMobAds.a
its going to fail. So enter the proper path - hard coded - for now to just see if you can get the project to first build, then run. If it does you can later figure out what is the appropriate $(VAR) to use to find it inside your project.

Resources