Module "MyApp" not found in UnitTest-Swift - ios

Im trying to test some Swift class (#objc class) in my legacy Objc code. I am importing "UnitTests-Swift.h" in my test classes.
Then I get this error:
Module "MyApp" not found in the autogenerated "UnitTests-Swift.h"
This is whats inside the top part of the "UnitTests-Swift.h"
typedef int swift_int3 __attribute__((__ext_vector_type__(3)));
typedef int swift_int4 __attribute__((__ext_vector_type__(4)));
#if defined(__has_feature) && __has_feature(modules)
#import XCTest;
#import ObjectiveC;
#import MyApp;
#import CoreData;
#endif
I cleaned the project, checked all the relevant flags ("No such module" when using #testable in Xcode Unit tests, Can't use Swift classes inside Objective-C), removed derived data and so on.. No idea of whats happening, but I m quite sure that #import MyApp shouldnt be there..
Can anyone help me with this?

Just got this issue in my project and after the entire day spent on investigation I finally resolved it.
Basically this happens because you have cyclic dependency between ObjC and Swift. So in my case it was:
Swift Protocol with #obj attribute in a main target;
Swift Class in UnitTest target which inherited this Protocol;
Import UnitTestTarget-Swift.h in any Objective-C class of your UnitTest target
So fairly simple combination leads to this error. In order to fix this you want either:
simply make sure that your Swift Class in UnitTest target is private, so it won't get to UnitTestTarget-Swift.h
or
do not mark your original Swift Protocol as #objc, which will allow you to access your SwiftClass from all ObjectiveC test classes, but those classes won't have any idea about the Swift Protocol.

Because the Swift class to be tested is part of MyApp, you should be importing "MyApp-Swift.h" in the test classes instead of "UnitTests-Swift.h".

You can add a Swift unit test (just create a new unit file and change the extension by .swift).
From that unit test file you can use your Swift classes.
And you can also import that file from your Objective-C unit tests (and the other way around) using the test module bridging headers.
And this would be the default example for your Swift unit test file:
import XCTest
#testable import MyApp
class MyAppSwiftTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}

This solution helped me:
Open Tests Target Build Settings
Search for HEADER_SEARCH_PATHS
In the line called "Header Search Paths" set value $CONFIGURATION_TEMP_DIR/myProjectName.build/DerivedSources
Clean and Cmd+U again
Hope it helps!
Many thanks to this article.

Related

Why can't Xcode find my App for Unit Tests?

I'm fairly inexperienced in iOS. I wanted to write Unit Test for my Cocoapods Project.
My ViewControllers that I want to test are in Pods. When I want to write a unit test, I already receive an error message with
import MyApplication
which says
No such module 'MyApplication'
Note that I used "MyApplication" only as a substitute for orginal name I crossed out in red in the picture.
What is the issue?
EDIT:
code of my test class:
import UIKit
import XCTest
import Pods_MyApplication_Example
#testable import Pods_MyApplication
class Tests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
XCTAssert(true, "Pass")
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure() {
// Put the code you want to measure the time of here.
}
}
}
To create a test target, on the test navigator, I click on the '+' and then choose 'New Unit Test Target'
I am sorry if there are any information missing. I will add those if there are any!
EDIT2: Changed picture which might provide more information
You must use #testable import to import your app's module for unit testing. You have to supply the name of your application target, Pods_MyApplication_Example, not the name of the project. In your example it will look like the following:
#testable import Pods_MyApplication_Example
And remove the import statement that imports the app target.
Thanks for the responses. I had to add the Plugin/Application that I want to test to the Test Target in my Podfile. So the Podfile looks like this:
use_frameworks!
target "MyApplication_tests" do
pod 'MyApplication', :path => '../'
end
After running pod update I could then import and test my Application.
This error can also occur when you don't have the correct test host appear.
If you see this, and you have selected the iOS Simulator or iPadOS simulator. Make sure that your test target supports that platform.
Select the Xcode project in the lefthand browser.
Click on your test target in the Project's General tab.
Under "General" select your appropriate host application under
"Testing".

{Module_name}-Swift.h file not working well only in Swift 4 projects unlike Swift 3.2

Anyone faced problems in {Module_name}-Swift.h file for Swift 4 projects? I've noticed -Swift.h autogenerated file not working well with Swift 4 syntax unlike Swift 3.2!.
For example, -Swift.h file doesn't contain all variables and methods which implemented in the custom Swift classes which inherited from NSObject class!
I've used #objc and #classkeywords but no way.
I don't get any errors! the problem is if I've created a class like this:
import Foundation
class Utils: NSObject {
let abc: String?
func xyz() {
print("")
}
}
and navigate to {Module_name}-Swift.h I see something like that:
SWIFT_CLASS("_TtC3{Module_name}5Utils")
#interface Utils : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
Problem
Both let abc: String? and func xyz() have been never included in {Modue_name}-Swift.hfile!
I think in Swift 4 you have to mark a lot more things #objc (nothing implicit anymore) but other than that it should just be in there.
You can all check it to confirm class name in .h file like:
#class filename;
The generated file {Module}-Swift.h does not contain your variables and methods, the file is generated to give you access to the Module namespace.
The actual interface for the generated module lives in Module.swiftmodule/arm64.swiftmodule (depending on built architecture).
More information on its contents:
https://bugs.swift.org/browse/SR-2502
https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20160111/000827.html
https://stackoverflow.com/a/24396175/1755720
however... the format is not documented anywhere and is subject to change. A good starting point would be to look in include/swift/Serialization/ModuleFormat.h
As to why it's not working - Swift 4 has a migration process, please ensure you have followed it: https://swift.org/migration-guide-swift4/
Xcode will pick up most things ... but it won't get everything!
And why do you need header files for Swift classes? You just can mark swift class as #objc and you will be able to reach all its properties.

I've tried it all, but I can't import my Swift file to my Objective-C file

I've read though multiple threads on this on StackOverflow, and I can't seem to find the answer to my problem. My folder hierarchy is like this:
"Project Name"
- "Helpers"
"FileIWantToImport.swift"
"FileIWantToImportTo.m"
I have:
Set the build settings to have a Product Module Name FMB
Set Defines Modules to YES
a bridging header created by Xcode
cleaned my build
#import FMB-swift.h in my AppDelegate with out any problems
Added #objc to my class declaration in my file so it says #objc class MyClass: NSObject
HOWEVER. I cannot, for the life me, figure out why it won't import into FileIWantToImportTo.m
I have tried:
#import "FMB-swift.h"
#import "Helpers/FMB-swift.h"
#import "Product Name/Helpers/FMB-swift.h"
#import <FMB-swift.h>
#import <Helpers/FMB-swift.h>
#import <Product Name/Helpers/FMB-swift.h>
All I get is "file not found" error. Can someone please point me in the right direction? I've looked through all the other threads and the apple docs and can't figure it out. I'm running in Xcode 7 and 10.11 GMs if that makes a difference.
EDIT: Here is an example project https://github.com/Aghassi/Example-Project
This is what you need to do:
Create a Swift file in your ObjC based project. No need to create bridging headers, since they're used to see ObjC code from swift, not swift code from ObjC.
In your Target, under Build Settings set Embedded Content Contains Swift Code to Yes
Your Swift file should have an #objc public class.
#objc public class Example: NSObject {
public func printSomething(text: String) {
print(text);
}
}
Check your Project's name, and in your .m file, add #import <ProjectsName-Swift.h>, and you should be able to instantiate your class defined in the Swift file, and all it's public methods. You might need to build your project before importing the -Swift.h file.
Here's an example project with an ObjC class using Swift code: https://github.com/lucaslt89/Example-Project.git

How to access an internal Swift class in Objective-C within the same framework?

Working on a mixed framework. imported inside the Obj-C file but the internal classes are not visible, only the public ones.
The documentation clearly states the internal clasees should be available between Swift and Obj-C:
Importing Swift into Objective-C To import a set of Swift files in the same framework target as your Objective-C code, you don’t
need to import anything into the umbrella header for the framework.
Instead, import the Xcode-generated header file for your Swift code
into any Objective-C .m file you want to use your Swift code from.
Because the generated header for a framework target is part of the
framework’s public interface, only declarations marked with the public
modifier appear in the generated header for a framework target. You
can still use Swift methods and properties that are marked with the
internal modifier from within the Objective-C part of your framework,
as long they are declared within a class that inherits from an
Objective-C class. For more information on access-level modifiers, see
Access Control in The Swift Programming Language (Swift 2).
Code Sample (Create a new project with a framework)
// SwiftObject.swift
public class SwiftObject: NSObject {
public class func doSomething() {}
}
internal class YetAnotherSwiftObject: NSObject {
internal class func doSomething() {}
}
// SomeObject.m file
#implementation SomeObject
- (void)someMethod {
[SwiftObject doSomething];
}
- (void)someOtherMethod {
[YetAnotherSwiftObject doSomething]; // Use of undeclared identifier
}
#end
As indicated in the docs, declarations marked with internal modifier don't appear in the generated header, so the compiler does not know about them and thus complaints. Of course, you could send messages using performSelector approach, but that's not convenient and bug-prone. We just need to help the compiler know that those declarations are there.
First, we need to use #objc attribute variant that allows you to specify name for your symbol in Objective-C:
// SwiftObject.swift
#objc(SWIFTYetAnotherSwiftObject)
internal class YetAnotherSwiftObject: NSObject {
internal class func doSomething() {}
}
And then you just need to create #interface declaration with the methods you want to use in your code - so the compiler will be happy, and also apply SWIFT_CLASS macro with the symbol name you've specified earlier - so the linker would pick the actual implementation:
// SomeObject.m file
SWIFT_CLASS("SWIFTYetAnotherSwiftObject")
#interface YetAnotherSwiftObject : NSObject
+ (void)doSomething;
#end
#implementation SomeObject
- (void)someOtherMethod {
[YetAnotherSwiftObject doSomething]; // Should work now !!!
}
#end
I've used the interface declaration in .m file just for clarity, the better option would be to combine such declarations in .h file, and include it.
By declaring methods in that interface we're making a promise to compiler, and it won't complain if you'll put there a method that does not exist (or with wrong signature, etc.) Obviously, you'll crash in runtime in that case - so be cautious.
For me it just worked by checking: "Allow app extension API only". You find it by going to the project setting, select your target and then it is in the General tab under Deployment Info.
Can someone explain to me, why this does solve the problem?
While the above solution works (https://stackoverflow.com/a/33159964/5945317), it seems overly complicated and unintuitive:
Complicated, because it seems to add more things than necessary – I will provide a smoother solution below.
Unintuitive, because the objc macro SWIFT_CLASS resolves to SWIFT_RUNTIME_NAME, and the provided value is not actually the runtime name – nor is the objc class name in the header matching the Swift attribute param in #objc. Still, surprisingly, the solution works – but to me it is not clear why.
Here is what we have tested in our own project, and believe to be the better solution (using the example above):
// YetAnotherSwiftObject.swift
#objc(OBJCPREFIXYetAnotherSwiftObject)
internal class YetAnotherSwiftObject: NSObject {
#objc internal class func doSomething() {}
}
// OBJCPREFIXYetAnotherSwiftObject.h
#interface OBJCPREFIXYetAnotherSwiftObject : NSObject
+ (void)doSomething;
#end
That's it. The interface looks like a regular objc interface. This gives the added benefit that you can include it in other header files (which you cannot do if you use the SWIFT_CLASS macro, as it comes from the autogenerated Swift header file, which in turn you cannot include in an objc header, due to circular dependency).
On the Swift side, the only thing relevant is that you provide the class with the proper objc name. Mind that I only used the name prefix for language consistency – you can even just use YetAnotherSwiftObject everywhere (i.e., in the objc header and in the #objc attribute in Swift – but you need to keep this attribute with explicit naming in any case, and need to keep it consistent with the class name in the header).
This also makes your life easier if you're in the process of converting your objc framework step by step to Swift. You just keep the objc header as before, and now provide the implementation in Swift.
Methods and properties that are marked with the internal modifier and declared within a class that inherits from an Objective-C class are accessible to the Objective-C runtime.
so let's make use of that:
class MyInternalClass: NSObject {
#objc var internalProperty = 42
}
#interface MyPublicClass()
#end
#implementation MyPublicClass
+ (void) printValue {
Class myInternalClass = NSClassFromString(#"MyPackageNameIfAny.MyInternalClass");
id myInternalClassInstance = [myInternalClass new];
int value = [myInternalClassInstance performSelector:#selector(internalProperty)];
NSLog(#"Value is %d ", value); // "value is 42"
}
#end
Using the SWIFT_CLASS macro and #objc class attribute could easily lead to errors when archiving. This approach is safer.

I want to call a class function written in Swift from the AppDelegate (which is in Objective C). What am I doing wrong?

I have a swift file "SomeController.swift" it is like this:
import Foundation
func performSomeStuff() {
println("Performing stuff")
}
Now in the app delegate, I am trying to do this: (note that the swift bridging header is imported)
[SomeController performSomeStuff]
But its not working.
I have also tried this:
import Foundation
class SomeController:NSObject {
class func performSomeStuff() {
println("Performing stuff")
}
}
But it still fails.
What is the correct way?
Add:
#objc
before the class keyword in your swift code so it will be:
#objc class SomeStuff: NSObject {
}
Also add #obj in front of any function that you want to call.
Then in your app delegate make sure to use #import "projectName-Swift.h"
Are you able to access "SomeController" class in objective-c, if not then you firstly need to add "${ProjectName}-Swift.h file and add Swift compilation support in Build settings as:
And for accessing methods from Swift to Objective-C, add
import Foundation
class SomeStuff:NSObject {
#objc class func performSomeStuff() {
println("Performing stuff")
}
}
before functions name.
In some cases, you need finer grained control over how your Swift API is exposed to Objective-C. You can use the #objc attribute if your Swift class doesn’t inherit from an Objective-C class, or if you want to change the name of a symbol in your interface as it’s exposed to Objective-C code.
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html
Ok, So I figured it out. First, here's the correct way. Its like some of you said:
In the swift file, I have it like this:
class MyController:NSObject{
class func performTask {
// Here my task is running.
}
}
Then in the app delegate, I just import the swift header file. And do this:
[MyController performTask];
Now for the part I had wrong. It was an error on my part, but maybe it'll be useful to someone else out there.
When I first created the first swift file, I had placed it inside a folder within the my Source folder. But when I started having multiple swift files, I moved the bridging header outside that folder and into the main Source folder (just for organising).
The problem was, it did not give me a direct error to tell me what was the problem. I had to check the issue navigator to identify the problem.
Hope this helps someone out there.

Resources