6

I'm building my own framework which proposed to be distributed to other developers for including to their projects. This framework links optionally certain frameworks (e.g. CoreLocation). The problem is that when I link my framework to real stand-alone project which doesn't contain CoreLocation in Build Phases, I'm getting linker errors like 'Undefined symbols for architecture' when tryin to build this host-project

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

Is it possible to avoid this because I don't want to force developers to include CoreLocation to their projecgts? Actually, I know it's possible, but what should I do to achieve this?

heximal
  • 10,327
  • 5
  • 46
  • 69

2 Answers2

11

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

Community
  • 1
  • 1
quellish
  • 21,123
  • 4
  • 76
  • 83
  • it doesn't help to avoid linker error when compiling host-project – heximal Sep 03 '14 at 16:26
  • @heximal, take a look at the project I linked to. – quellish Sep 03 '14 at 19:07
  • Thank you very much for spending your time trying to help me, but there is CoreLocation linked to TestApp project. My goal is to avoid this. I know, it's possible somehow – heximal Sep 03 '14 at 20:58
  • @heximal, I understand that. This is why TestApp is NOT linked against Core Location: http://imgur.com/GQOnwTd – quellish Sep 03 '14 at 21:38
  • yes, you're right, your example works. I've just seen CoreLocation added to project tree (Frameworks folder) and did't looked into build phases where it was absent. Now I'm trying to understand, what's the difference between your configuration and mine – heximal Sep 04 '14 at 12:02
  • almost won this struggle) can you please give me last clue. how to cast some CL constants using dlsym stuff. e.g. self.locationManager.distanceFilter = kCLDistanceFilterNone; – heximal Sep 06 '14 at 19:56
  • @heximal that is demonstrated in that project. Look at Biblioteque.m – quellish Sep 06 '14 at 20:53
  • Yeah, I saw. It demonstrates cast of kCLErrorDomain constant which is declared as NSString, which is pointer type. kCLDistanceFilterNone I need is a primitive double type. I can't understand how to apply dlsym approach to this (shame to me, I know) – heximal Sep 07 '14 at 06:18
1

You could wrap any code and imports that uses the CoreLocation framework in a

#ifdef __CORELOCATION__
... do stuff
#endif

This will negate all CoreLocation code when the framework is not being linked at compile time

Matthew Cawley
  • 2,828
  • 1
  • 31
  • 42