Xcode 8 Ambiguous expansion of macro NSLocalizedString - ios

I have the following in my unit test .pch file to allow my unit tests to find the right bundle for the localization files and this was working fine until I upgraded to Xcode8.
#undef NSLocalizedString
#define NSLocalizedString(key, comment) [[NSBundle bundleWithIdentifier:#"Tests-Unit"] localizedStringForKey:(key) value:#"" table:nil]
Now, I get the warning
Ambiguous expansion of macro NSLocalizedString
Which would explain why my unit tests fail as they can't find the localized string value anymore.
Any ideas what is wrong there? Is there something I need to now do differently?
EDIT:
I have a feeling it has something to do with all my source file being in 2 targets i.e one for the project and one for the unit tests which is the way we had it setup. I'm trying to clean this up and removed all my source file from the unit test target and added the
#testable import ProjectName
instead to a Unit test file to be able to access my code for testing but i'm now getting
File 'MyFile.swift' is part of module 'ProjectName'; ignoring import.

I eventually got it all working again.
Let’s pretend our project is called Panda and it consists of both Obj-C and Swift files. All of those files are in both our Panda and PandaTests target.
STEP 1: Ensure all your files have the right Target Membership i.e. Panda target: Only the source files, development frameworks, images etc and PandaTests target: Only the test files, testing frameworks, mock data etc
STEP 2: Ensure your Panda Project has the Build Settings -> Enable Testability set to Yes.
STEP 3: Ensure your PandaTests Project has the Build Settings -> Product Module Name set to PandaTests.
STEP 4:
Do a nice clean by holding down the Option button and then clicking on Product. You should see a Clean Build Folder option.
STEP 5:
For Swift Unit Tests, add #testable import Panda. Since we have now removed all our source code files from the PandaTests target, the unit tests need a way of accessing our project files. This enables our Swift unit tests to access all our Swift files and those Obj-C files that have been included in our Panda-Bridging-Header.h.

In your project Build Settings add -Wno-ambiguous-macro to Other C Flags
It solves the issue (warning is gone), but not sure what are the side effects.

Related

Legacy ObjC/Swift codebase crashes tests in `swift_checkMetadataState`: how to disentangle app/test targets?

I've got a mixed ObjC/Swift codebase with a very mixed-up structure, and the tests no longer run under Xcode 10.2 (details below). I'm trying to make the minimum structural changes to get tests back, so that I have a safety net for the refactoring that will come next. After taking what seems to me to be the obvious first step, the tests fail to build (details again below).
I'm interested in any of
solutions for the original problem (to get tests running again in the messy setup)
solutions for the problem after refactoring (to get refactored tests building in the somewhat tidier setup)
suggestions for build settings to verify or include in the description here, to get clearer on what's going wrong
I'm not interested in advice about how to structure a fresh project or create new targets with more sensible configurations: extracting code from these targets is also non-trivial, so I really need test coverage back before I start any refactoring.
Original situation (very messy)
project myCompany
app target app which builds module MyCompany
contains both Swift and ObjC code, with dependencies both ways
Defines Module = Yes, Product Module Name = MyCompany
test target myCompanyTests
Defines Module = No, Product Module Name = MyCompany
also contains both Swift and ObjC code
CocoaPods for external dependencies, also a bunch of internal Swift modules with dependencies managed by hand
Test files are included only in the myCompanyTests target, but many code files are included in both app and myCompanyTests targets. I think this and the two targets defining the same module name was probably in order to avoid having to import the app target into tests, because of problems with Swift/ObjC interop (see the next section). Building tests produces warnings about classes implemented in two places:
objc[9724]: Class _TtC12MyCompany12DiaryFetcher is implemented in both
/Users/tikitu/Library/Developer/CoreSimulator/Devices/556CAC28-B90B-4B6B-A87C-1A1450795051/data/Containers/Bundle/Application/495F33C2-F7FC-4AE6-B3FE-6908D6361B55/mycompany-develop.app/mycompany-develop (0x10d724060)
and
/Users/tikitu/Library/Developer/Xcode/DerivedData/mycompany-bifciplpbqaeqqdrmhivpjgnojri/Build/Products/Debug-iphonesimulator/mycompany-develop.app/PlugIns/myCompanyTests.xctest/myCompanyTests (0x13566dc38).
One of the two will be used. Which one is undefined.
As of Xcode 10.2, myCompanyTests builds successfully but running the tests fails with an EXC_BAD_ACCESS in swift_checkMetadataState somewhere inside the UIApplicationMain call. My guess is that this is related to the module-name/files-in-both-targets shenanigans.
First attempt at "fix the obvious mistakes"
As a first attempt to tidy things up somewhat I've done the following:
Remove all non-test files from the myCompanyTests target
Rename myCompanyTests Product Module Name to MyCompanyTests
Add #testable import MyCompany in lots of swift tests
Then I start running into Swift/ObjC interop problems, because I need to call Swift code in the app target from ObjC code in the test target. Things I've tried:
#import "MyCompany-Swift.h" in an objc .m test file
'MyCompany-Swift.h' file not found
#import <MyCompany-Swift.h> in an objc .m test file
'MyCompany-Swift.h' file not found
#import <MyCompany/MyCompany-Swift.h> in an objc .m test file (yes you can see I don't actually understand this mechanism)
'MyCompany/MyCompany-Swift.h' file not found
#import "MyCompanyTests-Swift.h" in all objc .m test files that need access to the app target
the generated file MyCompanyTests-Swift.h includes a line #import MyCompany; which looks very promising!
but that line fails with the error Module 'MyCompany' not found
Especially this last one looks suspicious to me, as I would expect the Xcode generated file should "just work". I guess I've overridden some user setting somewhere that's getting in the way: I'd be delighted with any suggestions for places to check.
I won't accept this (yet) as it's incomplete information, but I believe at least that I know why the fixed-up version is failing: the location of MyCompany-Swift.h isn't in the unit-test target's header search paths, because it's the app target. Reference e.g. https://github.com/jeremy-w/MixedLanguageTesting (which suggests adding an ad-hoc entry to the header search paths).
I believe the issue to be that only a framework target auto-creates the swift-to-objc header file Module-Swift.h: neither the app target nor a test target appear to do so.
I successfully resolved these issues by creating two new framework targets
* MyCompanyStuffThatUsedToBeInApp (containing everything that used to be in the app target except main.h), which does bidirectional swift/objc interop and
* MyCompanyTesting (a framework target imported by the tests, which likewise does bidirectional swift/objc interop for test-only code).
There may still be simpler ways to tidy this up, but this one at least is proven to work.

Adding unit tests for an existing objective c + swift project

I have an existing iOs project which I would like to add a unit test target to. The classes I'd like to test are both in objective c and swift.
I've managed to create a test target which allows me to test swift only code by adding the implementation swift files to the test target.
However, as soon as I import or use a class that imports objective c code, I run into Linker issues when building the test target:
...
Symbols not found for architecture x86_64
I've tried adding the objectivec mm files to my target which gets my passed the linker error, but I then get an unresolved identifier error for the class I'm importing.
I'm using xcode 9 and swift 3.
edit: I think this may have something to do with the fact the swift bridging header is not available in the test target, however I'm not sure how to add it.
Your test project is a separate target and should have all files it relies upon to be tested linked separately. So first of all click one of the .m files it's missing and check if the test project is also included in the targets. If this is the case there might be a problem with the bridging header your test project uses. Figure out which one it uses from the build settings of your target and see if it includes the same files as your main project.

Can't access swift files in Unit test target in Xcode

Adding swift files to test target will work, but it is not the best way to do. My problem is I can't able to access Swift file whereas Objective-C files are accessible.
I have checked product module name and set configuration file same as project file for test target. Even removed the test target and readded, but, still encountering "Use of undeclared type in SlideViewController".
Can anyone help me with solving this issue?
By default you won't be able to access internal classes from your unit test target.
The apple docs on writing tests with swift say that you need to take two steps to get around this:
Set the ENABLE_TESTABILITY build setting to YES.
Add #testable to the import statement for your module. #testable import MySwiftApp
If you follow both of those steps your SlideViewController (as long as it is not a private class) should be accessible from your unit test file as if it was declared as an open class.
Try to add this on top of your test file:
#testable import <YOUR_MODULE_NAME>
Sometimes you will need to add files to the build phases of your test target.
1.- Go to project navigator
2.- Select your project
3.- On the project and target list, select the target for your tests (Ex. "MyProjectTests")
4.- Select Build Phases tab
5.- Open "Compile Sources"
6.- Using the plus sign, add the files needed for the compilation

Can't run tests in a Xcode project that imports another framework

I'm trying to write an open source app to show how you can write client + server code in Swift.
The source code is located here: https://github.com/haaakon/QuizWorld (QW for short)
The app is using a framework, located here: https://github.com/haaakon/QuizWorld-API (QWAPI) to access the API. I've imported the QWAAPI as a project into the QW app, it runs fine in simulator, but when running the tests, it doesn't compile because of this Error:
Undefined symbols for architecture x86_64:
"QuizWorld.QuestionViewModel.__allocating_init () -> QuizWorld.QuestionViewModel", referenced from
This is from only one line of code in the test:
let a = QuestionViewModel()
This means that the test target does not correctly get the imported module in. The imports are:
#testable import QuizWorld
#testable import QuizWorldAPI
import Prelude
import ReactiveSwift
import ReactiveExtensions
import Result
I've tried adding alot of different imports, even for the Frameworks used in the API Framework, but nothing seems to fix this. The QW-Tests target also has the correct target dependency setup. Anyone have a clue where i can go next with this? All the code is open source in the repos linked to.
In your test target, you have nothing set in Test Host and Bundle loader build settings. During linking phase it basically fails (doesn't know where from) to load the symbols. Target dependencies only says what should be built before, and "Link binary with libraries" has no effect for dynamic frameworks.
Set following build settings in your test target:
Test Host: $(BUILT_PRODUCTS_DIR)/QuizWorld.app/QuizWorld
Bundle Loader: $(TEST_HOST)
The docs for the latter:
Specifies the executable that will be loading the bundle output file being linked. Undefined symbols from the bundle are checked against the specified executable like it was one of the dynamic libraries the bundle was linked with.
I tried this on your project and it works.
UPD: In your second commit "setup for first test" you have removed these lines, so you might just reverse deletion of those individual lines.
UPD2: Regarding your general project setup. Try running your app on a real device, with this setup it won't find 3rd party frameworks included from QWAPI project, as they are not copied automatically. You will need to setup "Copy frameworks" build phase, to make sure that dynamic frameworks are copied in to the app bundle.

How to unit test an app extension on Xcode 6

Does anyone know how to perform unit testing on app extension target, especially keyboard extension target?
What have I tried (in the unit test target):
In the "General" tap, set it's target to the extension target instead of the container app.
Set the "Bundle Loader" to the path of the binary of the extension target, which looks like $(BUILT_PRODUCTS_DIR)/com.mycompany.keyboard.appex/com.mycompany.keyboard
Set the "Test Host" to $(BUNDLE_LOADER).
In the "Build Phases" tap, set the "Target Dependencies" to both the container app and the extension.
After these things done, I can build it successfully but always get "Test Failed" with an log Test target SogouInputTests encountered an error (Test session exited(1). without checking in. If you believe this error represents a bug, please attach the log file at /tmp/TestStatus-UXfvxw.log).
I'm using Xcode 6 beta 3.
I have reported the bug to Apple. And sadly, the answer is that the keyboard extension is not support unit test now. The answer comes from Apple:
It's not currently supported to run unit tests inside an app extension
Instead, factor the code you want to test into a framework and test the code there
Link the framework into your extension
Just ran into similar issues trying to unit test an extension. Independently did exactly the same thing the author tried with Bundle Loader pointing to .appx path with no success of course.
I really did not like the idea of creating a separate framework just for testing so I ended up of adding testable source into the extension test target. It is really simple if you don't have too many source files in your extension:
Select you extension test target in Project settings
Go to Build Phases
Expand Compile Sources
Click +
Add source files with your testable code.
Build for Testing
Why it works:
Once you add extension sources into your extension test target, XCode is going to double reference each of them and compile in both the normal extension build and the test build thus eliminating linking issues.
Are there any drawbacks?
You will have to manually synchronize the source file list in the extension test target. Whenever you add/remove files in the extension target, you may need to do the same in its test target.
What I did which was easier than the other suggestions (no framework creation, no weird settings on build settings, phases, etc) was adding the file that I wanted to test (from the extension target) into the Target Membership of the test target:
The only "drawback" would be that your test target will also include files from your extension (rather than using #testable import like with the main app), but since you are not shipping your test target I would say there is no drawback :)
Tested on Xcode 10.
Note: Only works with Swift code, with ObjC due the BridgingHeader is not possible to follow this approach.

Resources