Swift and Obj-c Interoperability issues while unit testing - ios

I have an iOS application written in Obj-C which has unit tests defined as well. Now, i am adding a new feature where i am using a swift class (S) in an Obj-C class(O). I have the bridging header in place for both the main target and the test target. Till this point everything works really well.
Here's the problem,
If i want to create an unit test class(U) for class O in swift and run it, i get an issue saying bridging header not found. I am assuming this is because O uses S and these details are in the bridging header file and then again i am trying to use both S and O in U resulting the failure. If i add any other Obj-C class which doesn't use S, it works perfectly fine.
Here's what i have already done just make sure you know whats happening,
I have a forward declaration in O.h for class S because i know the O.h file will not support holding the -Swift.h import statement and hence it is in O.m file.
Is this scenario supported?
Class O uses Class S.
Class U can test class O by using class S.
Note: O->Objective-C
S->Swift Class
U->Unit test class in swift.

The answer was simple but not very evident. I had to place the import statements in the bridging header files in the order of the usage. For example: if one class has dependency on the other, then its important that you place the dependency's import statement first and then the calling class' file in the bridging header

Related

Import a Swift module in Obj C module

I have a xcworkspace with two xcodeprojects inside (one is a static library where all the base functionalities are implemented and the other is a demo project which uses classes from first one) - and all the code so far has been written in Objective C. What I want to achieve is create a Swift class in the static library and then access it from an Objective C class in the 'demo' xcodeproj.
I have created this 'Test.swift' class and a bridging header that was created automatically (changed the Defines Module property to YES in the Build setting), and everything works well - I can access it from Obj C classes in the same project. Next, I am creating a new "DemoTest.swift" Swift class in the 'demo' project and subclassing the Test.swift (which works). However, when trying to access this class from an Objective C file in the 'demo' project, the compiler doesn't recognize my "base" module -
In file included from
xxxxx/AppDelegate.m:26:
xxxxx/mpdemo-Swift.h:189:9: fatal error: module 'mpbba' not found
#import mpbba;
~~~~~~~^~~~~
1 error generated.
My question is how can I have a Swift code imported into another Objective C module?
Here's what to do:
configure an Objective-C bridging header
Click on your Xcode Project file
Click on Build Settings
Find the Search bar and search for Defines Module.
Change the value to Yes.
Search Product Module Name.
Change the value to the name of your project.
In class, add the following: #import "YourProjectName-Swift.h"
Hope this will work. let me know if you need anything else.

Hide Objective C in mixed Swift Cocoapod library

I am developing a Swift Cocoapod library which actually is a wrapper around some Objective C code.
My goal is to expose only the Swift part of the library, while making the Objective C
as private as possible. However, as the Swift part is a wrapper around the Objective C code, it needs to access it, although just internally.
I have searched and tried various approaches, but none of them worked at all.
First, I tried to address it via the use of a modulemap file. The idea was to create a Swift module called something like MyLibrary containing all the Swift code, while the Objective C code would be on the MyLibrary.Private module (I know the module would still be accessible, but this separation would be enough for me). The modulemap would look something like:
//MyLibrary.modulemap
framework module MyLibrary {
module Private {
header "MyObjectiveC.h"
export *
}
}
And adding the following line in the MyLibrary.podspec file
s.module_map = 'path/to/MyLibrary.modulemap'
However, by using this approach, even if I only import the MyLibrary module would still get access to all the Objective C classes, without having to import the MyLibrary.Private module to do so.
A second approach I tried was to make use of a private.modulemap file. So I would end up with two different modulemap files:
//MyLibrary.modulemap
framework module MyLibrary {}
//MyLibrary.private.modulemap
module MyLibrary.Private {
header "MyObjectiveC.h"
export *
}
I would then add the following line in the MyLibrary.podspec file
s.pod_target_xcconfig = { 'MODULEMAP_PRIVATE_FILE' => '$(PODS_ROOT)/MyLibrary/path/to/MyLibrary.private.modulemap' }
However, I get a failed build stating that it can not find the MyLibrary.Private module.
Is there anything I am doing wrong in these approaches? Or is there any other way to allow my library's Swift classes access the Objective C classes privately without exposing those, at least explicitly, requiring to import an specific *.Private module for it?

How to add a prefix to a lot of existing swift files and classes in Xcode

I'm working on a project and I created a lot of swift classes. As you know with swift you do not need to add a prefix in front of the class or file name; and so I didn't.
Now I was required to add a prefix to them (let's say "KK" in front of each file and class)
I was wandering if someone managed to do this with a single command, I would like to avoid renaming and refactoring each class one by one.
example:
a file called FridgeView.swift containing a class FridgeView
should be renamed KKFridgeView.swift and the class KKFridgeView
P.S. I know we do not need prefix in swift but it is a requirement of my company I really have no choice :)
Step One Click the Project Name
]1
Then you can find Class Prefix on you right Side
Add your prefix and then Create all you viewController and Class
It can be set from file inspection menu but it will only new files added into project. it will not impact already added files.

Cannot call value of non-function 'module<F1>'

I created a Swift framework, just a simple test.
The Swift file (F1.swift) code:
public class F1{
public init(){
print("inited")
}
public func call(){
print("called")
}
}
Then, I built the framework and I imported it into another project.
I tried to use it this way:
import F1
in the viewDidLoad of a UIViewController:
var c = F1()
c.call()
The F1.framework has been dragged under:
General > Embedded Binaries
General > Linked Frameworks and Binaries
and I can also see it under:
Build Phases > Link Library With Binaries
Build Phases > Embed frameworks
The XCode "reaction": no issues with the import statement.
I receive an error exactly where the class is instantiated:
Cannot call value of non-function 'module...'
Am I missing something?
[update] Based on some online resources and some other test, I'm supposing the problem lies in Build settings: eg. Build Active Architecture Only could be involved, but it would be interesting to understand how and why.
Find minimal sample Xcode project here.
Using your posted code gives the expected results here:
inited
called
In your example you've named the single class in your module F1 to the same name as the module, namely F1. Most likely Swift can't differentiate between module namespacing and the actual name of your class in the module, so when you just write F1, Swift possibly infers this to be an explicit namespacing annotation; refering to the namespace F1 (made available by your import of module F1). A namespace can naturally not be treated as a type, which would explain the error message you're prompted with ([emphasis mine])
Cannot call value of non-function 'module...'
You may test this theory by explicitly calling the class F1's initializer of module F1, by including both the module namespace and its (single) class type in the call:
var c = F1.F1()

Swift Compiler Error: Use of unresolved identifier 'name'

I tried to include a class called 'name' and I got an error:
Swift Compiler Error: Use of unresolved identifier 'name'
The class exists and doesn't contain any compile errors.
There could be a few possible issues.
One of the classes has a Testing target and other one doesn't. You have to even include all of your classes in the testing target or none of them.
If it's Objective C class, check that the class is in ObjectiveC bridging header file.
If it's NSManagedObject subclass. Add #objc(className) before the class declaration.
If it's part of a different framework, make sure that the class or function is public
I had this one too. You will probably find that your first class is included in your testing module and that "name" isn't. Simply, if you include a class in testing, then every class that it references has to be in testing.
I had this problem too. I was trying to reference Class 1 within the code in Class 2. My problem was that Class 2 had target memberships in A and B, and Class 1 only had Target Memberships in Class A.
You can fix this by opening the Utilities Tab (farthest right button on the top bar of the Xcode window), and make sure that the same boxes are checked for both classes in the Target Membership subsection.
Got problem solved by
Target -> Build Phases -> Compile Sources -> Adding the class file
Add one more to the list.
If it is part of another framework, make certain that the "Build Active Architecture Only" settings are the same.

Resources