Adding Swift Class to Objective-C Project with Multiple Targets - objective-c

I have an existing Obj-C project that houses many targets that all share the same AppDelegate. I want to bridge a swift class that is used by select targets. I can do this easily when I have one target.
When I add a swift file to the project, I select the desired targets and the necessary bridging-header.h files are generated, but when I try to import those -swift.h files, they are can't be found.
Are there steps I'm missing when it comes to projects that have multiple build targets?
EDIT - More Details
I wanted to add a little bit more detail on how my project is set up.
I have a Framework, we'll call it AppFactory, coded in Obj-C. I have multiple build targets that generate different versions of the Core app depending on information in that target's plist. I want a swift file to be utilized by these apps. In my build settings the Defines Module is marked to Yes, I have create this swift class:
#objec class SwiftClass: NSObject { }
And in doing that, Xcode generated the proper Briding-Header.h files.
According to Apple Guides when you have multiple build targets your import header should include the ProductName/ProductModuleName-Swift.h, which should be auto generated by Xcode.
When I look in to my derived data folder, the ProductModuleName-Swift.h does exist, with in each targets build folder, under the AppFactoryCore folder.

I found a solution to my problem.
Swift File Targets:
Instead of having SwiftClass.swift target both the framework and the selected targets (AppA, AppB & AppC), I backpedaled and solely targeted the framework, AppFactoryCore.
Build Settings (Packaging > Defines Module):
I reverted each app target's Defines Module property from YES to NO, and set this property to YES for the framework target.
Swift Class Declaration:
The guide states:
Because the generated header for a framework target is part of the framework’s public interface, only declarations marked with the public or open modifier appear in the generated header for a framework target.
So I added access control modifiers to my class and class functions
#objc open class SwiftClass: NSObject {
//Code
}
Import Header:
Since SwiftClass.swift is only targeting the framework target, and it is in fact a framework that is being used, the header import SwiftClass.swift into the universal AppDelegate was
#import <AppFactoryCore/AppFactoryCore-Swift.h>
Which finally became visible once all previously stated modifications were done.
Now that the file is global to all targets I added a custom attribute to identify if the target running was is one that should utilize SwiftClass.swift.
Hope this helps anyone else trying to accomplish a relatively similar task!

Related

Problem with using Swift in Objective-C target

I have an old Objective-C project, which has multiple targets, and some of the targets share a framework (let's call it CJDataKit) that's also written in Objective-C. I'm trying to add some Swift code to my project, at least to the main app target, and have some limited implementation of it working, but I'm running into some issues whenever the Swift code needs to use or import the CJDataKit framework, or any header file that itself imports the CJDataKit framework.
What's Working
I wrote a basic UIView subclass in Swift (which didn't need any other code from my app), and I can use this in my Objective-C target, using #objc keyword and by importing "MyApp-Swift.h" in my Objective-C code.
I then wrote a new UIViewController subclass in Swift, and used a couple of simple Objective-C objects from both CJDataKit framework and non-framework classes. I did this by creating a bridging header file, and added the Objective-C headers there.
So far so good, and everything compiles fine.
What's Not Working
The problem happens if I try to import into Swift other Objective-C
files that might be importing the CJDataKit framework inside it. For
e.g. I wanted to write a Swift extension of an existing
UIViewController subclass (call is PageAViewController). This
subclass has imported multiple other header files. If I add it to the
bridging header file, I start getting build errors:
Include of non-modular header inside framework module 'CJDataKit':
'.../CJDataKit/Person.h'
Commenting out the #import CJDataKit makes it work for this particular file, but it still gets compile errors from a different header file (that was imported by PageAViewController). It'll only work if all the files imported don't have a #import CJDataKit, which is difficult and cumbersome. BTW, these files all belong to the app target, not the framework. So something about Swift doesn't like interacting with the CJDataKit framework directly, even though it works fine if it's built independently in the same app target along with the CJDataKit framework.
I've also tried importing the CJDataKit.h header file into the Swift
bridging header file, figuring this way I don't have to individually
import each file from the framework, but that doesn't work either.
That results in a different error:
Could not build module 'CJDataKit.h'
I've tried using #import <CJDataKit/CJDataKit.h> as well but same result.
From my settings:
- "Allow Non-modular Includes In Framework Modules" is Yes on the target, and framework. It is No at the project-level.
- "Defines Module" is also set to Yes, on both the app target and framework, and No at project-level.
Would love some help in getting this setup correctly. I've been searching for a solution, but haven't really found anything.

How to recognize touch inside SVG object in Swift?

I'm currently building an app where I need to recognize a touch point inside a SVG map. So I have a map with multiple rectangles and when the user touches one of these rectangles an action needs to be triggered. How can I solve this?
One option would be to mathematically calculate if the touch location lies in between a rectangle, but I am not a math genius.
My preferred option would be to use a framework. I found the SVGKit framework but unfortunately its written in Objective C.
You can use SVGKit for a Swift project. You have to use an umbrella header file, it is the 'master' header file for a framework.
Importing Objective-C into Swift
Access classes and other declarations from your Objective-C code in
Swift.
Overview
You can use Objective-C and Swift files together in a single project,
no matter which language the project used originally. This makes
creating mixed-language app and framework targets as straightforward
as creating an app or framework target written in a single language.
The process for using your Objective-C declarations from your Swift
code within mixed-language targets differs slightly depending on
whether you’re writing an app or a framework. Both processes are
described below.
Import Code Within an App Target
To import a set of Objective-C files into Swift code within the same
app target, you rely on an Objective-C bridging header file to expose
those files to Swift. Xcode offers to create this header when you add
a Swift file to an existing Objective-C app, or an Objective-C file to
an existing Swift app.
If you accept, Xcode creates the bridging header file along with the
file you were creating, and names it by using your product module name
followed by "-Bridging-Header.h". Alternatively, you can create a
bridging header yourself by choosing File > New > File >
[operating system] > Source > Header File.
Edit the bridging header to expose your Objective-C code to your Swift
code:
In your Objective-C bridging header, import every Objective-C header you want to expose to Swift.
In Build Settings, in Swift Compiler - Code Generation, make sure the Objective-C Bridging Header build setting has a path to the
bridging header file. The path should be relative to your project,
similar to the way your Info.plist path is specified in Build
Settings. In most cases, you won't need to modify this setting.
Any public Objective-C headers listed in the bridging header are
visible to Swift. The Objective-C declarations are automatically
available from any Swift file within that target, with no import
statements. Use classes and other declarations from your custom
Objective-C code with the same Swift syntax you use for system
classes.
Import Code Within a Framework Target
To use the Objective-C declarations in files in the same framework
target as your Swift code, you’ll need to import those files into the
Objective-C umbrella header—the master header for your framework.
Import your Objective-C files by configuring the umbrella header:
Under Build Settings, in Packaging, make sure the Defines Module setting for the framework target is set to Yes.
In the umbrella header, import every Objective-C header you want to expose to Swift.
Swift sees every header you expose publicly in your umbrella header.
The contents of the Objective-C files in that framework are
automatically available from any Swift file within that framework
target, with no import statements. Use classes and other declarations
from your Objective-C code with the same Swift syntax you use for
system classes.
Source
Additional information
How to include Objective-C frameworks in your Swift project

Swift generated header with multiple targets

I am having trouble generating the swift header needed to use Swift classes from ObjC, when there are multiple targets with common code.
I created two targets My OSX App and My iOS App. These targets share common code where an ObjC class is calling a Swift class.
As described in Swift and ObjC in the same project I can add #import "My_iOS_App-Swift.h" to my ObjC class and compile it from the My iOS App target.
However, this doesn't compile from the My OSX App target, as the include needs to match the module name. It is looking for #import "My_OSX_App-Swift.h"but the common code does not use that include.
What is the correct way to mix/match Swift/ObjC in code that is shared between multiple targets? I could manually change every target to use a common MyApp-Swift.h, but that doesn't feel right and may cause other problems.
Set the Product Module Name setting in Build Settings to be the same across your modules
For example: $(PROJECT_NAME)
Or use fixed names if you have watch extensions for different targets like: Main_App and Watchkit
This causes the ###-Swift.h file that is generated has the same name across all modules. This also eliminates the need for adding/checking preprocessor micros.
From: Objective C to Swift header file with multiple targets

Using Swift in Objective-C project

Although I have followed and tried everything from This Thread and read all of Apple's Guide of Swift-ObjC interoperability, I'm unable to recognize or use Swift fies in my project.
I have created a Swift file that declares/defines a class called TorusView that inherits from UIView. I've tried to gain access to this class in another class MenuView by importing the bridging header, importing the Swift class, importing the class with the syntax *-swift.h (which seems to now be *.swift.hin Xcode7.2). I've made all of the modifications to my target build settings recommended in that lengthy Stack question and a variety of others from google searches.
Nothing I've tried has allowed me to create a TorusView objective in my objective-C class.
You need to import a header file YourAppName-Swift.h, it contains all the public (and internal if same target) declared types in Swift.
first: Build Settings --> defines module --> YES.
second:Product Module Name -->YOUR project NAME.
last:improt "YOUR project NAME-Swift.h" in your Object-c file
like this:
enter image description here

How can I build an Objective-C static library to distribute as a single binary and header file?

I'm building a static library, MyLibrary, for iOS in Objective-C that bundles together a dozen useful classes, each with its own .h file. I'd like to distribute MyLibrary as a single compiled binary, libMyLibrary.a, and a single .h header file, MyLibraryAPI.h. MyLibraryAPI.h has a dozen #import statements, one for each of MyLibrary's dozen public classes. Developers who want to include MyLibrary in their host projects should only have to include the libMyLibrary.a binary and the MyLibraryAPI.h header. This is the goal.
So I have set the Role of each public class in the MyLibrary Xcode project to Public and built libMyLibrary.a successfully using Xcode command line build utils and lipo. Then, I manually included all of the dozen MyLibrary header files along with libMyLibrary.a in a host project, and the host project can use the public MyLibrary classes with no problem. Awesome!
The problem is if I remove those dozen header files and use MyLibraryAPI.h instead (as is my goal), the host project's classes can no longer find the MyLibrary header files referenced in MyLibraryAPI.h. Instead, at compile time, I get errors like: MyAwesomeThingDelegate.h: No such file or directory... for each MyLibrary class that I try to #import in MyLibraryAPI.h. I have a folder in my host project root directory called lib and in host project build settings have set the recursive header search path to lib/** and in Library Search Path, set a recursive path to lib/**.
I'd love to hear suggestions from the community on how to correctly set the host project's search paths so that I only need to include libMyLibrary.a and MyLibraryAPI.h to use the MyLibrary classes. Or if I'm doing something wrong, I'd love to hear another suggestion to achieve my goal of distributing a single binary and a single API header file.
I've had the same challenge and I've come with the following solution:
First, I tried to hide as many implementation details as possible. For that reason, I usually built pairs of classes: one class is the public interface and the other one the private implementation. The public class has only one member instance: the pointer to the implementation. The private class has just a forward declaration.
#class MyViewImpl;
#interface MyView : UIView
{
#private
MyViewImpl* _internal;
}
#property (nonatomic, assign) CGRect highlightArea;
- (void) startAnimation;
#end
Second, I put all the public declarations into a single header file. It's as comfortable to work with as with separate header file, but it works. The only imports that are left, are import of iOS stuff, such as:
#import <UIKit/UIKit.h>
I'm giving a second answer here with a different approach.
The way Objective C works is, that the compiler needs the full declaration (i.e. the header files) of all the classes that the users of your library will directly call. The library file (the .a file) only contains the compiled code and no declaration. It will only be used by the linker, which is one of the last steps of building an application.
[Programming languages like C or C++ are the same. Programming languages like Java or C# however store meta information about classes in the compiled code so they don't need no header files, just the .jar or .dll file.]
So one approach would be to give the .a and a directory full of header files to your user. They then add the .a file to their project, add a single #import statement wherever they use your classes and add the path to the header file directory to their build settings (it's called Header Search Paths).