Swift umbrella header from another target - ios

I have a project with several targets. The main target is a combination of Swift and Objective-C classes and objc files, which use Swift classes by including the ModuleName-Swift.h umbrella header. However, when I include the same source files to several targets the umbrella header can't be found in other than the main target. The module name in the header import should be different for each target. How do I achieve this?
Update:
So far I have not found any other way than setting the same Product Module Name for all targets. That does not seem to have any side effects.

I have the same problem when writing a Today Extension for my app.
I have a file named for example Items.swift located at Today extension component and
included to that target. So I have following situation: Main project fully written on Objective-C
and Today extension is written on Swift
Problem:
I want to include file Items.swift to main project and use it
Solution:
Make sure that Items.swift is included to main app target
Important (!!!) you should create "ProductModuleName"-Bridging-Header.h and add reference to this file in project file (Objective-C Bridging Header option) even if your
main target contain only Objective-C code
Your Items.swift should inherit from NSObject or its descendants
Than add #import "ProductModuleName-Swift.h" to place where you want
to use Items.swift and press Build
Finally you can go to "ProductModuleName-Swift.h" and make sure that interface is generated than you can use your Objective-C version of your class
ProductModuleName you can see at Build Settings -> Packaging -> Product Module Name

First of all, I want to clear up the confusion that ModuleName-Swift.h is Swift compiler generated header that is used to use swift code in Objective-C and umbrella header ModuleName.h is the header for framework targets to expose Objective-C code to Swift.
Regardless, as I understand your question you want to import a header that is a form of target name, in files that are shared across multiple targets. For this you can define a preprocessor macro with different value for each target and in your Obective-C file use the macro value to imprt the header, i.e. #import macro.
Let's say for your example you want to import swift generated header ModuleName-Swift.h in your shared files. The generated header name is available in build settings as SWIFT_OBJC_INTERFACE_HEADER_NAME. You can create a macro SWIFT_HEADER for that header name in preprocessor macros section(GCC_PREPROCESSOR_DEFINITIONS):
# '\' is important to preserve '"'
SWIFT_HEADER=\"$(SWIFT_OBJC_INTERFACE_HEADER_NAME)\"
and then in your Objective-C files import using this macro:
#import SWIFT_HEADER
Additionally, you can create a single xcconfig adding this preprocessor macro and share it for all the targets:
# '\' is important to preserve '"'
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) SWIFT_HEADER=\"$(SWIFT_OBJC_INTERFACE_HEADER_NAME)\"

Related

How to use Swift framework in Objective-c app

I would like to use a Swift library in my Objective-C IOS app. I have the Swift library setup as a separate project, which builds fine in XCode. I can drag the generated Swift framework from the Swift project into the "Frameworks, Libraries and embedded content" list of my Objective-C project target. It appears with an "Embed & Sign" label.
The Apple description here: https://developer.apple.com/documentation/swift/importing-swift-into-objective-c states that
You can work with types declared in Swift from within the Objective-C
code in your project by importing an Xcode-generated header file. This
file is an Objective-C header that declares the Swift interfaces in
your target, and you can think of it as an umbrella header for your
Swift code. You don’t need to do anything special to create the
generated header—just import it to use its contents in your
Objective-C code.
When I look inside the swift .framework file, I can see the header file there. But when I import it in one of my Objective-c .m files, then the compiler says that the file is not found. I have tried both the
#import "Starscream-Swift.h"
and the
#import <Starscream/Starscream-Swift.h>
syntax.
How can I convince XCode to use the header file from the Swift framework? Do I need to copy that header somewhere, maybe? And what else do I need to think about to use a Swift library from an Objective-c IOS app?
I am using XCode version 13.4.1.
By the way, the Swift library I am trying to use is Starstream.
Xcode-generated header file is required when your own project has Objective-C and Swift classes mixed. When dealing with frameworks you either include the framework's umbrella header like this:
#import "SwiftFramework/SwiftFramework.h"
Or, more preferably, you import it with #import expression:
#import SwiftFramework;
Be advised, that only part that is exposed to Objective-C runtime is accessible from this framework (i.e. public/open classes inherited from NSObject or some other Cocoa classes)

Could not build Objective-C module, when using swift in objective-c module

In an iOS application I have a subproject (not cocoapods) in which I have included a swift file and ObjC file (that is used by the swift file). XCode automatically created a bridging file but could not build it because apparantly bridging is not allowed in a framework. The workaround that I used was to add the objective-c header to the umbrella file and it worked. Now I need to use a swift class from ObjC. I have define module to set to YES, the generated file Framework-Swift.h . But when I try to import it in objective-c i get
Could not build Objective-C module
The closest I got after some googleing was this answer:
Ah gotcha. It looks like you're building a mixed Swift & Objective-C
pod - if that's the case, Xcode will try to import
within the generated -Swift.h header.
You'll need to create the header manually and add imports for the
Objective-C classes that you want to expose to Swift.
CocoaPods generates an umbrella header automatically and imports it
within the .modulemap, but Xcode doesn't use that when generating the
-Swift.h header
But I am unsure what header needs to be created manually.
Any ideeas or pointer about using swift in an objective-c framework ? In both ways ?
I also had similar issue when using Swift pods in my Swift project containing several targets. No Objective-C code at all. I tried to clear build folder, pods cache, derived data - nothing worked.
Solution:
Open the Build Settings for a target that contains your module code. Set the "Install Objective-C Compatibility Header" to "No"
There's a great and simple article that wraps up this case:
DEFINES_MODULE=YES
To use ObjC classes in Swift, create a bridging header and specify path to it in the build settings
To use Swift classes in ObjC, #import <ModuleName/ModuleName-Swift.h> in your *.m file (use #class SwiftClass; forward declaration in *.h file, if needed)
For swift classes and their members to be visible to objc
inherit your class from NSObject
prepend it with #objcMembers
make both the class and its members public

How do I build an iOS Framework with the FIT C++ library

I am trying to create a Swift wrapper for the FIT C++ libFitSdkCppiOS.a library but don't really know how to set things up with the mix of C++, Objective-C and Swift code.
Here is what I have done so far:
1. Created a new target for the FITFramework
2. Copied the libFitSdkCppiOS.a library and the associated cpp header files into the targets folder in Xcode
3. Because you can't use a Bridging-Header file in Frameworks I am trying to figure out how what to do next.
I have seen a few posts about something called an umbrella header but have no idea what that is or what needs to be in it to get this to work. Can someone please explain step by step what I need to do to create this Swift Wrapper and package it up as a framework that can be used by other projects.
Is the umbrella header the main framework header file, in this case the one called FITFramework.h ?
If not how do I create an umbrella header file and where does it need to be?
What should be in the umbrella header file?
FITFramework.h
//
// FITFramework.h
// FITFramework
//
// Created by xxxx xxxxxxx on 7/6/18.
//
#import <UIKit/UIKit.h>
//! Project version number for FITFramework.
FOUNDATION_EXPORT double FITFrameworkVersionNumber;
//! Project version string for FITFramework.
FOUNDATION_EXPORT const unsigned char FITFrameworkVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <FITFramework/PublicHeader.h>
EDIT:
1. How do I expose the Objective-C classes to Swift without using a Bridging-Header file ?
There are a few articles that mention the use of a module.map file but this seems to be to expose the C headers rather than the Objective-C headers to the Swift wrapper function
OK I figured it out - and it was hard to find any good or accurate guides. I will write it up in more detail elsewhere and add a link at some point.
In the meantime - assuming your framework is call XXXFramework - you need to:
create a XXXFrameworkPrivate subdirectory in the XXXFramework folder with a module.modulemap file in it
add the private headers to the module.modulemap file like so
module FitFrameworkPrivate {
header "../XXX.h"
header "../YYY.h"
header "../ZZZ.h"
export *
}
create a XXX.xcconfig file with the following line
SWIFT_INCLUDE_PATHS = $(SRCROOT)/XXXFramework/XXXFrameworkPrivate
set the project configuration to use this config file for both debug and release
add this line to your Swift classes
import XXXFrameworkPrivate
Basically this allows Swift classes to import the headers from the module.modulemap file instead of using a Bridging-Header file which can't be used in a Framework.
Watch out though - I have callbacks between the C++ classes, the Objective-C classes and the Swift classes and this creates a problem if your public class uses a protocol to communicate with one of the private Obj-C classes. To avoid that add another public Swift class that talks to the Swift wrapper and only have that one public.
If anyone has a better way of doing it please let me know.
Thanks a lot! This answer was very useful to me as I had similar issue (to integrate the c fit sdk instead of the c++ fit sdk into a swift framework for macOS and iOS apps)
I'll just add the follow step I had to do, in case it helps someone:
When using the framework in a app that used Pods, I wasn't able to include the "Private" framework (necessary because it contains the c constant converted to swift constant like FIT_MESG_XXX) and the xcconfig are set by the Pods framework. Editing the Pods xcconfig worked, but clearly bad.
Eventually, I figured out you can just create a new xconfig with a #include and use that in the external app where you want to use like so (this is separate from the xcconfig you need to build the framework)
#include "../Pods/Target Support Files/Pods-iOSorMac/Pods-iOSorMacOSApp.debug.xcconfig"
SWIFT_INCLUDE_PATHS="$(SRCROOT)/fit-sdk-swift/RZFitFile/sdk" "$(SRCROOT)/fit-sdk-swift/RZFitFile/src"

How to create a framework with Objectivec files and Swift files

I have a Xcode project with Obiective-c files and swift files, Now i want to create a framework with both classes, Is this possible ?
Yes, this is straightforward and the process is documented by Apple. Please read "Swift and Objective-C in the Same Project" with thought and you'll be wiser (the section "Importing Code from Within the Same Framework Target" covers importing Objective-C in Swift from the same target, and the other way). Briefly…
For framework targets you don't need to create a bridging header to make your Objective-C importable to Swift, you just need to #import those headers you want visible to Swift in the framework's umbrella header. For that to work, the header needs to be marked public.
For Swift to be importable in Objective-C in the same framework target, make sure "Defines Module" is toggled on for the target, and in the files where you need to reference the Swift types, do an import in the style #import <ProductName/ProductModuleName-Swift.h>. If I recall this right, the Swift types / methods / properties you want to access from Objective-C will need to have been declared public for it to be accessible to Objective-C code even if it's in the same target.

Expose Objective-C Framework to Swift

I'm writing a Cocoa Touch Static Library in Objective-C. It will be distributed through Cocoapods and the developers using it should also be able to do this in Swift.
In my test projects the library can be used perfectly in Objective-C, but in Swift I get the compiler error: No such module MyLibrary.
In the Library Target Defines Module is set to YES. According to the Apple guide here, that's the only thing one must do: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
To implement pch file in existing swift project:
File > New > File... (macOS app for your case)
then you can import your Objective-C library like this in your pch file (test.pch is my file)
#ifndef test_pch
#define test_pch
#import <YourLib/YourLibMainClass.h>
// Include any system framework and library headers here that should be included in all compilation units.
// You will also need to set the Prefix Header build setting of one or more of your targets to reference this file.
#endif /* test_pch */
Now you need to tell your project there is a pch file.
Go to your target build setting and make a search for "Prefix Header".
At Apple LLVM 8.0 - Language section add value for Prefix header key (be careful with your pch file project path)
You will could import your library on a swift file
import YouLibrary

Resources