Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Use Static Library in Embedded Private Frameworks and App on iOS

What should be the proper way to use classes in a static library in a private framework, an app and an extension? My sample project can be found here https://github.com/keithyipkw/framework

In the second commit, the SDK was linked with the .a. running the app created the error

Ld /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator/FrameworkApp.app/FrameworkApp normal x86_64
    cd /Users/keithyip/Documents/Workspace/FrameworkApp
    export IPHONEOS_DEPLOYMENT_TARGET=8.4
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -arch x86_64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.4.sdk -L/Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator -F/Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator -filelist /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Intermediates/FrameworkApp.build/Debug-iphonesimulator/FrameworkApp.build/Objects-normal/x86_64/FrameworkApp.LinkFileList -Xlinker -rpath -Xlinker @executable_path/Frameworks -Xlinker -objc_abi_version -Xlinker 2 -fobjc-arc -fobjc-link-runtime -Xlinker -no_implicit_dylibs -mios-simulator-version-min=8.4 /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator/SDK.framework/SDK -Xlinker -dependency_info -Xlinker /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Intermediates/FrameworkApp.build/Debug-iphonesimulator/FrameworkApp.build/Objects-normal/x86_64/FrameworkApp_dependency_info.dat -o /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator/FrameworkApp.app/FrameworkApp

Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_GAI", referenced from:
      objc-class-ref in AppDelegate.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

The symbol is global in the .a but local in the SDK

$ nm -a /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator/SDK.framework/SDK | grep '_OBJC_CLASS_$_GAI'
00000000000d94e0 s _OBJC_CLASS_$_GAI

In the third commit, I added the .a to the app target. The app ran but with warnings

objc[3743]: Class GAI is implemented in both /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator/SDK.framework/SDK and /Users/keithyip/Library/Developer/CoreSimulator/Devices/752A7B8E-405E-4403-BDD8-A168613774B1/data/Containers/Bundle/Application/D16B5121-2DA9-452B-9574-95B35AE3E197/FrameworkApp.app/FrameworkApp. One of the two will be used. Which one is undefined.

I checked the SDK and app binary according to the file paths in the warning

$ nm -a /Users/keithyip/Library/Developer/Xcode/DerivedData/FrameworkApp-bpzqozighjdtncegosucvgelzagc/Build/Products/Debug-iphonesimulator/SDK.framework/SDK | grep '_OBJC_CLASS_$_GAI'
00000000000d94e0 s _OBJC_CLASS_$_GAI

$ nm -a /Users/keithyip/Library/Developer/CoreSimulator/Devices/752A7B8E-405E-4403-BDD8-A168613774B1/data/Containers/Bundle/Application/D16B5121-2DA9-452B-9574-95B35AE3E197/FrameworkApp.app/FrameworkApp | grep '_OBJC_CLASS_$_GAI'
0000000100032c88 s _OBJC_CLASS_$_GAI

Digging deeper with nm -m, libGoogleAnalyticsServices.a is different from other static libraries. Linking to other libraries in a dynamic library is problem free.

0000000000002b20 (__DATA,__objc_data) private external _OBJC_CLASS_$_GAI

It seems to be impossible to keep the symbols public

clang: error: invalid argument '-keep_private_externs' not allowed with '-dynamiclib'
like image 283
keithyip Avatar asked Sep 09 '15 16:09

keithyip


1 Answers

Normally you can simply link a static library to a dynamic library. However in this case the symbols in the .a are private external (visibility=hidden), it is impossible to include libGoogleAnalyticsServices.a to a dynamic library and use the symbols outside the dynamic library on iOS. The options to solve it as this moment are

  1. Change the dynamic library to a static library
  2. Refactor the code to remove GA from the dynamic library
  3. Create wrapper classes for GA so the callees do not need to link to GA

If someone run into a similar situation in the future, other options are

  1. Ask the vendor to expose the symbols
  2. Check if there is any "objcopy --globalize-symbols" equivalent for iOS. The closest thing is objconv as I write this. It supports OS X but not iOS. An objcopy equivalent for Mac / iPhone?
like image 190
keithyip Avatar answered Oct 20 '22 00:10

keithyip