Adding unit tests for an existing objective c + swift project - ios

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.

Related

Swift Unit Testing in Xcode is not finding any Objective-C types

I'm trying to write some unit tests in Swift in Xcode. I'm needing to make use of two Swift files because they have the classes I need to utilize in this unit test.
These two Swift files build and run properly when I normally build/run my project.
However, when I attempt to use them in my unit test, I get errors for any types that are referenced in these two Swift files that are coming from any Objective-C file.
See this image:
These missing types are coming from an Objective-C file. Now, these Objective-C files ARE included in my bridging header file so that's not the problem.
I have no other info to work off.
I figured it out. I was missing #testable in the import statement. After adding #testable, it works.

Swift classes marked with #objc not being added to "-Swift.h" header in mixed Objective-C/Swift framework

I am attempting to convert an old statically linked library to a framework. With mixed swift and objective c in the static library, all headers are generated correctly. However, switching to a framework target and adding swift files, marked with the #objc header, the class is not added to the -Swift.h header. I can import the header, but swift classes are not found. This is in Xcode 10.2 and attempted using both Swift 4.2 and 5.
Are there any particular settings in XCode that will affect the generation of the *-Swift.h header in a mixed Objective C/Swift framework target?
I had a similar issue. In my case it was a known issue in Xcode 10.2:
https://developer.apple.com/documentation/xcode_release_notes/xcode_10_2_release_notes
If you’re building a framework containing Swift code and using lipo to create a binary that supports both device and simulator platforms, you must also combine the generated Framework-Swift.h headers for each platform to create a header that supports both device and simulator platforms. (48635615)
...
In my case all I had to do was to update the Carthage to the newest version 0.33.0
The problem appears to be a combination of Apple's new build system, the expectations they set when compiling and the number of inter-dependencies in the project setup.
The new build system runs the Swift compilations in parallel. When having multiple library/framework dependencies that are mixed Objective C and Swift, the compiler appears to not generate the -Swift.h files on time. In static libraries, the -Swift.h files appear to be generated at the end of the Swift Compilation process, meaning they are not generated quickly enough to be used by the Objective C files when the Objective C compilation occurs. When generating a framework, it appears that the Compiler generates the header at the beginning of the compilation process and the Swift files are not fully compiled and the -Swift.h file does not generate appropriately with the Objective C class interfaces and protocols.
What this means ends up meaning is that we can not rely on the "target dependencies" to build the dependent projects correctly.
So how can we build our .framework of mixed Objective C and -Swift.h without a ton of manual scripting.
Here are the tricks I discovered that will work.
Use the old build system. When using the new build system there is an error when it attempts to merge the module map for the static library file saying that it can not find the *-Swift.h file whether or not it exists.
Create your framework by making it a wrapper around the static library by:
Giving them both the same product name and module name.
Add a single .Swift file to the framework build so that it has something to compile and will link the swift libraries.
link to the static library in the framework.
Add all necessary headers to the public headers of the framework.
Add all public headers to the umbrella header.
Use a Run script phase to copy the *-Swift.h file from the static library build to the framework product post compile.
For any public headers that include the *-Swift.h, you may need to post process the header and replace the *-Swift.h import with the appropriate framework import ie . This would not be recommended due to possible cyclical imports in the umbrella header.
When running a clean, build the framework target first manually, before building the application target.
Below is an example script for copying the *-Swift.h file post build.
header_file="${TARGET_TEMP_DIR}/../${PRODUCT_MODULE_NAME}.build/DerivedSources/${PRODUCT_MODULE_NAME}-Swift.h"
header_dir="${BUILT_PRODUCTS_DIR}/${PUBLIC_HEADERS_FOLDER_PATH}"
mkdir -p "$DIR"
echo "copying $header_file $header_dir"
cp -f "$FILE" "$DIR"
UPDATED
Marking all modules Swift compilation mode to "Whole Module" appears to have a positive affect on this issue, but I have not fully tested it.

How to create SDK for iOS in Swift?

I have my own Xcode project which contains some controllers. I want to make its SDK, for use it in another application. In parent application it works as child. Parent app will share some data with my controller and my controller works on it and gives back some result. So kindly guide for me. Examples are - Payment Gateway SDK's. I am looking for the same.
I can see you add tag for swift. In Swift, static libraries are not supported, so you must exclusively use a framework (aka dynamic library) when linking an app to a library. Framework is similar to sdk.
Here are the steps:
1)Create the Framework using New > Project under IOS > Framework & Library, select Cocoa Touch Framework
2)To avoid the "ld: warning: directory not found for option..." goto Library Search Paths in Build Settings for your target and delete the paths.
3)You can't mix Objective-C with Swift so don't even consider adding the Swift-Header bridge file in your code.
4)There are some cases in swift where you need to import code from unexposed Frameworks. I've successfully used the module-map inside the framework to deal with these case.
5)I also select CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES in the Build Settings to solve 'include of non-modular header inside framework module'. That seems to work
6)I make sure that the header file that gets generated is marked as Public (not Project). Click on the file and you'll see the selection in the inspector under 'Target Membership'
Link
Also you can follow this tutorial. Since you have already created project.
Create a framework target, make all source code files a member of the new target, add the new target as target to the podfile. For everything else the tutorial should work. It should help you in understanding the steps.
Build framework

Xcode 8 Ambiguous expansion of macro NSLocalizedString

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.

ios - Parse Issues in NSObjCRuntime, NSZone, and NSObject

I'm using AddThis to add sharing options in my iOS app.
I have imported the classes and added the -fno-objc-arc flag to all the imported classes since they don't use ARC.
However, when I try to run the app I get a slew of Parse Issues such as:
Expected identifier or '('
Unknown type name 'NSString'
Unknown type name 'Protocol'
...
These errors occur in NSObjCRuntime, NSZone, and NSObject. I have the requisite frameworks included as well. Any ideas?
Including this image if it helps:
I had the same issue on my project when I was trying to mix C code (.h and .c) with Objective-C code. Found the reason of the issue:
Check your .pch file to make sure every Objective-C framework #import (such as #import <UIKit/UIKit.h>) is enclosed in:
#ifdef __OBJC__
#endif
If they're outside of this conditional scope, the compiler will try to import Objective-C frameworks into C source code.
I just changed the filename of Base64Transcoder.c to Base64Transcoder.m, and now the project compiles. I have no idea why this fixes the problem, but it works.
I had the same issue, using C and C++ code with objective C, and i doesnt have a .pch
The easiest solution was to go into your build settings -> Custom Compiler Flags and set the "Other C Flags" to "-x objective-c" and set the "Other C++ Flags" to "-x objective-c++"
this will do the trick with xCode 7.2
I have had the same problem when my project contained .cpp files.
If .cpp file doesn't contain ObjectiveC frameworks(e.g. ) it has to 'Default-C++ Source' type
,
but if .cpp file has ObjectiveC frameworks - it must be as 'Objective-C++ Source'
TLDR: if your PCH file is OK, look through your CPP file headers to see if you've accidentally included any headers for Objective C objects.
The Details:
I got this because I had accidentally included an Objective-C class header in a C++ class header, indirectly. The structure was this:
Compass.h defined a pure Objective C class.
ActionTracker.h defined a C++ class that understood Objective C constructs (via ActionTracker.mm).
HelloWorld.h defined a purely C++ class.
In my original setup, HelloWorld.h included ActionTracker.h, but this was OK as ActionTracker.h didn't yet contain Compass.h. Later, I changed my code and included Compass.h in ActionTracker.h, which then pulled it into HelloWorld.h and I got these errors.
I had this same problem when I tried to move the info.plist file from one directory to another.
This somehow triggered XCode to edit the build phases for that target and significantly increased the amount of "Compile Sources" and "Copy Bundle Resources".
Luckily my project has multiple targets that I use for testing, (i.e. App Demo, App Dev, App Local, App 1.1, App 1.2 etc)
So I just duplicated one of the unaffected targets and renamed it (also renamed the bundle identifier and the build scheme) and this obviously fixed the problem for me since it's not the whole project that was affected but only that specific target.
If you want to try my solution, try to create a new target from scratch, or duplicate and rename any of your un-affected targets.

Resources