Cannot see Swift classes from test target - ios

I have an iOS app using both ObjC and Swift code. I tried setting up a test target for it today with no success. I have a single test case class written in Swift. I imported my app's module there. I made sure the classes I am trying to access are public. But I cannot see them from my test target. I can see ObjC code from there though.
I tried the exact same steps on a dummy project and there it worked fine.
I don't want to add the classes for testing to the test target's compile sources. I am also using Xcode 6.3 and updating to Xcode 7 is not an option for me at the moment.
Any ideas on what I am doing wrong?

Make sure your import is marked with a #testable annotation. For example, you want:
#testable import myprojectname
as opposed to:
import myprojectname

The issue went away after deleting and recreating the test target.

Related

Xcode Unit Testing missing certain project classes

I'm attempting to write some unit tests. But, somehow, some classes are found and others are not.
Their targets are the same as is seemingly everything else. And yet some classes I can use and others are not found.
I have #testable import MyProject at the top of course.
In my image below you can see what I mean. Of two classes of the same grouping only one is being recognized:
Double-check and ensure that your file has the correct Target Membership
Fixed by adding an import statement for the class to the Swift bridging header.

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.

Adding Swift files to test target not fixing unit tests

I have looked at a lot of blogs and tried many things to get my Swift unit tests to work in Xcode 6.0.1 (or 6.1 for that matter). I'm trying to access classes in my app's target so I wrote this line:
var vc: LoginViewController!
Of course, I get the "Use of undeclared type 'LoginViewController'" error.
I then try to add LoginViewController to my test target, but then I get "use of unresolved identifier" errors on other classes in my project. So I try to add those classes to my test target, but I end up with a seemingly endless source of errors like the screenshot below:
Declaring all my classes as public, causes other errors and seems like bad practice. Is there anyway to include unit tests in a Swift project that relies on many frameworks and classes? I simply want to start with something almost exactly like the code in this article.
After much headache and putting this on the back burner, I found that the main problem was when adding files to the test target membership, the Objective-C classes would still complain. I figured it was an old compatibility issue with the new way Swift does unit tests, but the solution was my test target didn't know there was a bridging header. Thus, I added a reference to it in my test target's build settings, like so:
It seems simple and obvious now, but the errors were unhelpful. No other solutions I saw for Swift unit tests suggested this could be an issue, but now I know.
Tl;dr
For unit tests to work in Swift, the test target must know everything the app target knows, so add a reference to your bridging header in your test target too (if applicable).
If you're using Xcode 7, there's now a much better way of dealing with this using #testable import {ModuleName}.
Just make sure that the module you want to test has the build setting Enable Testability set to YES.
I am using Xcode 6.1
You need to add your swift file to the target membership of the test target

How to I import 3rd party frameworks into Xcode Playground?

How do I import 3rd part frameworks into Xcode Playground?
Swift Playground obviously has a framework import mechanism because we can import Cocoa, SpriteKit, and in an OSX Playground, XCPlayground (XCPlayground seems missing from iOS, oddly)
What I'd really like to do is hide code from my playground and be able to show a minimal example. Any thoughts on how to load a Framework into Swift playground?
See also:
How to import own classes from your own project into a Playground
Is it possible to import third-party Objective-C code into a Swift playground?
(This question is different because the request is to use a framework in Playground rather than simply regular swift files)
There is currently no supported way to import your own framework or app code into a playground, short of pasting it into the playground editor. We're aware that this is very desirable functionality, but as always we encourage people to "vote with bugreporter" at bugreport.apple.com
Right now, if you're just trying to hide code for showing in an example, code folding in the editor might do the trick.
It is now possible to import your own frameworks into a playground. This provides a way to share code between your applications and playgrounds, which can both import your frameworks. To do this, your playground must be in the same workspace as the project that produces your framework. You must have already built your framework. If it is an iOS framework, it must be built for a 64-bit run destination (e.g. iPhone 5s). You must have an active scheme which builds at least one target (that target's build location will be used in the framework search path for the playground). Your "Build Location" preference (in advanced "Locations" settings) should not be set to "Legacy". If your framework is not a Swift framework the "Defines Module" build setting must be set to "Yes". Once all these conditions are fulfilled, importing your framework will work in a playground.
Edited: As of Beta5 this is now supported when the playground is part of the workspace that builds the framework as a target. There are more details on the Apple dev forums site, but hopefully #Rick Ballard will add them to his answer here and that should becoke the definitive answer for this question.
Don't do the below anymore!
For the short term, while there's no supported solution, if you're producing a Module/Framework you can copy it into the SDKs System/Library/Frameworks directory and then just import <#Module#> the same way as you import system Frameworks.
For an OS X Playground: /Applications/Xcode6-Beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/System/Library/Frameworks
And for iOS: /Applications/Xcode6-Beta.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.0.sdk/System/Library/Frameworks/
But before you do that... go file a radar as #Rick says in his answer.
I've written a tutorial covering import of custom frameworks in Beta 6
In short, if you've got your playground and framework either in the same project or workspace, and your framework is built for 64 bits (even for iOS), they play very well together.
source: http://qiita.com/ryokosuge/items/2551cd4faa9dca324342
If there is anyone who still had difficulty understanding how to go about it after reading the accepted answer, this may help.
It's not in English though so I will explain here:
Using Carthage:
1: Setup your Cartfile in the root of the project, add your libraries and run:
carthage update --platform iOS --use-submodules
2: Make sure your project is saved as a Workspace. From the Xcode menu bar: File > Save As Workspace. Shutdown Xcode and reopen from the new .xcworkspace file.
3: Add your library's .xcodeproj file in your workspace: File > Add files to "your_workspace_name". Then find it in: ${SRCROOT}/Carthage/Checkouts/your_library_name/your_library_name.xcodeproj
4: Find the Embedded Binaries and Linked Frameworks and Libraries section in your project's general settings. Add your library's .framework file to both.
5: Clean(⇧+⌘+K) and build(⌘+B).
6: Import your library in the playground.
I solved it by copying the built frameworks to the Built Products Directory, where Playgrounds in the workspace also searches for frameworks. Note: you also need to run lipo and strip away unused architectures from the FAT binaries.
The steps needed (in a nutshell):
Create an aggregate target
Add a script phase to copy the frameworks
from Carthage/Build/iOS/ to BUILT_PRODUCTS_DIR (I use a swift script
in the example below, but you can use also use bash, ruby python
etc.)
Run aggregate target Now you can import the framework(s) in a
Playground
Additionally you can leverage the aggregate target in your app:
Embed frameworks from BUILT_PRODUCTS_DIR, not Carthage/Build/iOS/
Add aggregate target to app scheme to re-establish frameworks after a clean.
Here is an example project setup as above: https://github.com/richardnees/CarthagePlaygrounds
Like using Objective-C classes in Swift code, you can import 3rd-party library/classes into your project and Xcode will ask you if you wanna create a <#YourProjectName>-Bridging-Header header file. Choose yes and import all header files you need in that file. After that, in your playground, you can simply do import <#YourProjectName>-Bridging-Header after import UIKit and you will be able to use the classes you imported to your project.
Update: this is actually incorrect. Please see my comment below. Sorry for any confusion...

How to import own classes from your own project into a Playground

Assume a setup like this:
You have an Xcode 6 project, where you've implemented your own classes (say MyView and MyViewController) with both Objective-C and Swift
You have added a Playground into your project
In the Playground, it's possible to import modules (frameworks) like UIKit with the import keyword. How do you enable access to the project's other classes from the Playground?
Just trying to access project classes directly results with an error message:
Use of unresolved identifier 'MyView'
As of Xcode 6.0 Beta 5, it is now possible to import your own frameworks into a playground. This provides a way to share code between your applications and playgrounds, which can both import your frameworks. To do this:
Your playground must be in the same workspace as the project that produces your framework. Your workspace must contain a target that produces the framework, instead of using a pre-built framework.
You must have already built your framework. If it is an iOS framework, it must be built for a 64-bit run destination (e.g. iPhone 5s), and must be built for the Simulator.
You must have an active scheme which builds at least one target (that target's build location will be used in the framework search path for the playground).
Your "Build Location" preference (in advanced "Locations" settings of Xcode) should not be set to "Legacy".
If your framework is not a Swift framework the "Defines Module" build setting must be set to "Yes".
You must add an import statement to your playground for the framework.
Once all these conditions are fulfilled, importing your framework will work in a playground.
In Xcode 7 we introduced another mechanism that you can use to import your own classes as source, instead of importing a framework; you can read about this "Auxiliary Sources" support at http://help.apple.com/xcode/mac/8.0/#/devfa5bea3af
I actually managed to refer to other Swift files in the current project by doing this:
Create an empty Playground and place is somewhere under your project.
Open the YourPlayground.playground bundle (yes, it's a bundle = directory) in Terminal.
Edit contents.xcplayground for example with vi and add another section like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='3.0' sdk='iphonesimulator'>
<sections>
<code source-file-name='section-1.swift'/>
<code source-file-name='section-2.swift'/>
</sections>
<timeline fileName='timeline.xctimeline'/>
</playground>
Rename section-1.swift to section-2.swift (if you created the Playground from scratch, there should be an example section-1.swift in your bundle)
Add a hard link (symbolic link doesn't seem to work) named section-1.swift which will point outside the bundle to your Swift class file like:
ln ../../Classes/MyView.swift section-1.swift
Close Xcode and open the Playground again.
Now there should be two sections, one with the contents of your Swift class file and the other one with the example content you got from creating the Playground from scratch.
This way I can actually run code lying outside the Playground, but Xcode seems to crash more often when doing it like this.
Edit:
As of Xcode 6 beta 5 you're now able to refer to project files, as Rick Ballard instructs in his answer.
Since Beta 5 of Xcode 6 it is possible to import your code if it is in a framework. What you need to do is create a framework target, add the Swift files there and in your playground do
import ModuleName
You can look up the module name in the build settings. It's usually the same as the target name.
Remember to make the code you want to see public. You'll need to build the project before changes are available in the playground. (You'll also need to edit the playground to trigger re-execution.)
Important
Do not give the playground file the same name as the target! If you do, importing seems to work but you'll get the following error when the playground tries to execute:
Playground execution failed: error: Couldn't lookup symbols:
I wasted an hour on figuring that out. :)
I wasn't able to get it working using any of the answers here, so I started playing around and found a simple way that worked for me to import a swift class into a playground.
Just create a playground in your project, theres a directory inside it called 'sources', just drag a copy of the swift class into that folder and the playground then will have access to it.
For example:
I just put links to all my swift files in the Sources folder:
cd /path/to/project/MyPlayground.playground/Sources
ln -s ../../*.swift .
This way changes in your source file will take effect in your playground immediately. Worked very nicely.
Xcode 8.2, Swift 3.0.1, macOS Sierra
All you have to do - is write in the beginning:
import ModuleName
(assuming your playground placed in the same workspace as framework/project)
If it's doesn't work:
Rebuild your project
Recreate playground and copy all from old playground there
It solves a lot of strange errors with failed init's and imports of whatever!

Resources