edelaney05
edelaney05

Reputation: 6987

Xcode Unit Tests - Link error when building for device only

My application unit tests build and test when running in the simulator, but fails with a linker error when building and testing to device.

On my application target I've set the following build settings:

DEPLOYMENT_POSTPROCESSING = NO
GCC_SYMBOLS_PRIVATE_EXTERN = NO

On my unit test I've set the following build settings:

BUNDLE_LOADER = $(BUILT_PRODUCTS_DIR)/<app name>.app/<app>
TEST_HOST = $(BUNDLE_LOADER)

The linker error is:

Undefined symbols for architecture armv7s:
"_<An NSString * const>", referenced from:
      -[UnitTestClassA setUp] in UnitTestClassA.o
"_<Another NSString * const>", referenced from:
      -[UnitTestClassB helperMethod:] in UnitTestClassB.o
      -[UnitTestClassB anotherHelperMethod:] in UnitTestClassB.o
ld: symbol(s) not found for architecture armv7s
clang: error: linker command failed with exit code 1 (use -v to see invocation)

... I have "continue after building errors" turned on in Xcode's preferences, but I don't receive a ton of linker errors complaining about NSString * const's. If I'm doing something wrong, then would expect more link errors than the handful I'm getting since I use string constants throughout my production code.

I'm creating my string constants like this:

.h file...

extern NSString * const ReallyGoodString;

.m file...

NSString * const ReallyGoodString = @"This string is great!";

... the .m file is production code, and part of my application target, and so I do not have to link it into the unit test bundle.

So, what is going on here? Why does this work just fine in the simulator and not on device?

I've posted a sample project to Github that illustrates the problem. You can see in the sample project that this problem is inconsistent: some symbols link just fine others do not.

Upvotes: 14

Views: 2304

Answers (4)

Blake Watters
Blake Watters

Reputation: 6617

This is most likely because you have symbols that are in use by your test suite that are not used by the main application and Dead Code Stripping is enabled. You can disable the Dead Code Stripping option on a per-configuration basis. I fixed a similar issue to enable tests to run on my device by flipping the option to No for Debug builds only.

Upvotes: 12

rob mayoff
rob mayoff

Reputation: 385640

When the linker is creating the Linker-Error executable, it discards FHKViewControllerThisSymbolWontLink because nothing in the executable uses it. The linker doesn't know that it should keep the symbol around to be used by the unit test bundle (which is dynamically loaded at runtime).

You can tell the linker not to strip the unused symbol by tagging it with the used attribute, like this:

NSString * const FHKViewControllerThisSymbolWontLink __attribute__((used)) = @"name";

You will need to do that for each symbol you define that's not used by the main executable but is used by the test suite.

Upvotes: 27

Sp3kk
Sp3kk

Reputation: 79

I once had a similar problem and the solution was as simple as cleaning the project, I guess you already tried this, but maybe not. I'd give it a try at least. Just go to "Product" --> "Clean" in the top navigation bar, or use the shortcut: "shift" "command" "K"

(I would have written this as a comment and not an answer, since it is not more then a simple suggestion. But I don't have enough points to comment, sorry).

Upvotes: 0

Andrew
Andrew

Reputation: 15377

Some possibilities:

  1. You import a header and do not link against the correct library. This is common, especially for headers for libraries like QuartzCore since it is not included in projects by default. To resolve:

    A. Add the correct libraries in the Link Binary With Libraries section of the Build Phases.

  2. You copy files into your project but forgot to check the target to add the files to. To resolve:

    A. Open the Build Phases for the correct target, expand Compile Sources and add the missing .m files.

  3. If you are using logic tests, they are not supported on iOS devices, but only in the simulator. Application tests work on devices though. Docs for that:

iOS: Although Xcode can run logic and application unit tests in simulators, Xcode cannot run logic unit tests on iOS devices. Therefore, if you include and have active both types of unit tests in a scheme, you will not be able to use that scheme to run unit tests on iOS devices.

Upvotes: 1

Related Questions