I’m trying to integrate Swift into an existing objective-c framework that has public, private and project files. In order for swift to access the project files, I added a modulemap that defines a new module (e.g MyFramework_Internal) by including all the project headers as explained here: http://nsomar.com/project-and-private-headers-in-a-swift-and-objective-c-framework/
That setup is sort of working but one thing I was surprised to see is that now a client can access the internal classes by importing MyFramework_Internal (#import MyFramework_Internal). Is there a way to hide the module since it's only needed by the framework itself?
The modulemap looks like this now:
module MyFramework_Internal {
header "Folder1/Baz.h"
header "Folder1/Folder2/Bar.h"
export *
}
The module file is intended for Objective-C, and Objective-C doesn't have the concept of internal. It only knows public (what's declared in the header files), and private (what's declared only in the implementation files).
Thus, anything that end's up in a module file will be publicly available.
Related
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"
I'm having an issue implementing modules for my particular build configuration. I have a bunch of dynamic frameworks that I would like to add module map files to so that they can be naturally imported into Swift without having to use Objective-C Bridging Header files. The issue that I'm having is as follows:
I have multiple modular frameworks:
Framework.framework (base framework, required by all components)
Framework.Component1.framework
Framework.Component2.framework
... etc
I would like to be able to write a module map for each framework that uses this dot notation for the module naming (for backwards compatibility, among other reasons) but Xcode isn't allowing me to name whole modules with the dot syntax since the dot syntax is reserved for Submodules.
I've tried creating an umbrella framework that contains all of different components and writing a single module map for that, but given that the frameworks are dynamic this necessarily bloats the final binary size if the user isn't using all of the frameworks inside of the umbrella framework.
Are there any solutions that would allow me to use this dot syntax while having separate module map files for each of the frameworks?
Thanks!
Go with the umbrella framework strategy.
Then, for submodules that are "optional" in your umbrella framework (maybe even all or most of them) set the submodule declaration to explicit. This means the consumer must import the submodule explicitly in order for it to be loaded. This is what I've done in the past to import a very large C library into Clang's module system and it worked well enough.
The docs are okay, but not great. However, they do go over this case pretty well: https://clang.llvm.org/docs/Modules.html#submodule-declaration
Situation:
I've got an iOS dyanmic framework written in Swift. I've also got a bunch of classes written in Objective C that I would to use within my Swift classes (some are public, some are private). However, I would like the Objective C classes to not be exposed to projects using my framework.
What I have tried:
Umbrella Header
From what I understand, I should import using #import header.h in my umbrella header file, which is usually FrameworkName.h, and then make sure all the Objective C header files that I wish to include in my Swift classes are to marked as "Public", under Build Phases -> Headers.
Doing this, however, automatically exposes the project using my framework to all the private Objective C classes that the framework uses.
Module Mapping (with separate module)
Because of this, I've looked into using module mapping, which is documented here. I've looked at posts by other users such as this and this, as well as this Github repo.
I successfully got the following working:
//SharedClasses/module.modulemap
module SharedClasses {
}
//SharedClasses/module.private.modulemap
module SharedClasses.Private {
header "header.h"
export *
}
The problem is that in my project (that has this framework imported), this:
import Framework
import Framework.SharedClasses
is allowed, and subsequently the "hidden" Objective C classes are exposed. Perhaps this is just how modules work? Is there a way to make them truly private?
Module Mapping (with framework private module)
Also, I've tried creating a module.private.modulemap file at the root of my framework with the following contents:
explicit module Framework.Private {
header "header.h"
export *
}
and then linking it my target's build settings under MODULEMAP_PRIVATE_FILE. However, when I do import Framework.Private in my framework's Swift classes a compiler error is thrown:
"No such module 'Framework.Private'
I don't understand why this error is occurring.
Module Mapping (with private header)
I noticed that in the Clang docs, a private specifier is mentioned:
A header with the private specifier may not be included from outside the module itself.
My understanding is that since all the Swift classes in my framework are already part of the module Framework, if I create a module.modulemap file with the following:
framework module Framework {
umbrella header "Framework.h"
private header "header.h"
export *
module * { export * }
}
then everything should be working! The Objective C headers are only accessible within the module (i.e. the framework's Swift classes), and aren't exposed to any project using the framework. Cool, but it doesn't work...the compiler just doesn't recognise the Objective C classes. No other errors are thrown, but you can't use the headers, so it's like you didn't include the headers in the first place.
WHY? And what is the private specifier for then?
Soo, reiterating my original question:
Is there any way to import Objective C headers for use in Swift classes, within an iOS dynamic framework, while keeping them private and not accessible from any project using said framework?
Thanks for reading, and sorry for the long post. It's been a long day (and night)...
The quick answer is you can't :) Even if you declare "private" modulemap, it can be always imported by your framework users. Please note, that usually, it is not a concern, especially with open source. You just say "this is an internal module, don't use it".
But (there is always but) - you can have behavior, that effectively works the same - allows you to use your Objective-C classes withing same framework target, without making them public. It works in closed source setup - I have a dynamic framework shipped to external parties, that has a purpose of revealing as less about its construction as possible.
The case a bit too complex to paste everything here. I'm adding a link to my article about the topic. That's a general idea:
Mimic your Objective-C classes with Swift protocols (manual work needed)
In your swift code use these protocols
Expose internally these Swift protocols to Objective-C (manual work needed)
Adopt these protocols by ObjC classes
Create in Swift and expose in ObjC a factory, that will create instances of these protocols
Create in the factory methods, that will allow you to register concrete types, and expose them to ObjC
Register ObjC types from Objective-C code in the Swift factory
Use the factory to instantiate ObjC classes, without actually exposing ObjC to Swift (hurray ;) )
Creating Swift framework with private Objective-C members. The Good, the Bad, and the Ugly
Github example project
You can find some method from this link.
Just create Folder (e.g.PrivateA) with module.modulemap where include all object headers you need to use in your Swift class.
e.g.
module $(ModuleName)Private {
header "header.h"
export *
}
in Swift,
you can use: import $(ModuleName)Private
so, default module exclude these headers when using import $(ModuleName).
From my experiment, test project can also import $(ModuleName)Private, but not found any useful code.
Try this way anyway.
So, I have an iOS app project with a static library as subproject. As found multiple times here on SO, you should set the visibility of the library header files to public/private/project, depending on who should be able to use them.
Based on that, I created one class with a header file that exposes functionality to the app project (or whoever is going to use this library). Naturally, this header file imports a number of headers from other classes inside the library project. As these other header files do not provide functionality that should be exposed to the library's users, i would like to set these to "project", making them invisible to the rest of the world.
However, when i set header files to "project" they don't get copied into any of the private or public header folders. This results in a 'ProjectHeader.h' file not found error when using #import "ProjectHeader.h" in the PublicLibraryClassHeader.h when compiling the app project that uses the library.
So the question is: How can I set header files to "project" in a library project and stil use them from within that library project? Am I misunderstanding the concept of public/private/project header files in static libraries?
The easies way is to convert your static library into framework. Framework is a static library in a specific container, that does all magic for you. Btw, this words about public headers are related to frameworks, not to static libraries.
I have an Objective-C framework (framework A) that exposes some public and some private headers. The public headers are also declared in the framework's umbrella header. I have a second Swift framework (framework B) that links with the Objective-C framework.
Now, if I want to import the public headers of A in B I simply need to do an import A.
But how do I go about importing the private headers?
I know a bridging header is not an option since that's not supported for frameworks. Do I need to somehow create a separate umbrella header for the private headers?
You need to modify framework A, So that it export a private module.
Create a private module map file in A project. This would be something like this:
A/private.modulemap:
explicit module A.Private {
// Here is the list of your private headers.
header "Private1.h"
header "Private2.h"
export *
}
In the "Build Settings" of framework A target, search "Private Module Map File" line, and make that:
$(SRCROOT)/A/private.modulemap
Do not include private.modulemap file in "Compile Sources". That causes unnecessary warnings.
Clean and Build framework A target.
In framework B Swift files. you can import the private module like this:
import A
import A.Private
It is some time since this question was posted. The accepted answer is very good, and as far as I'm concerned it is a common approach.
The problem is, it is not really "private". You can do this inside your framework to access the "private" part:
// Framework A Swift file
import A.Private
But If you use framework A in an app (or you ship it to your client), he can still do:
// Client App Swift file
import A
import A.Private
// access "private" framework methods and classes
I was trying to resolve that, as I had recently a situation when I needed to hide it from users (closed source framework) - I just could not let anyone access it, as it was a threat to SDK integrity.
I found a solution to that problem, but a bit too complex to paste it here as a whole.
I made a post about it no medium. Maybe it will help someone checking that problem, that's the link to my article:
https://medium.com/#amichnia_31596/creating-swift-framework-with-private-objective-c-members-the-good-the-bad-and-the-ugly-4d726386644b
As noticed by Andrzej Michnia in his answer the problem with "private module map" solution is that it is not really completely private and those "private" headers still can be seen by someone as they are still included in our framework. If someone opens compiled framework with such "private" module he will still see all .h files that you hidden.
If we need to hide some objective-c headers in our swift framework completely from other users then another possible method to do that will be just to make them public and remove them from our framework after building the framework manually or with a bash script.
You could create a separate header file for example "InternalHeaders.h" where you import all headers that you do not want to expose. Then import this InternalHeaders.h in public umbrella header of your framework. Make all headers public so that you can compile everything. After you build your framework simply remove "import InternalHeaders.h" from public umbrella header and remove all headers that you do not want to expose manually or with a bash script or in run script build phase and thats it.
Still not a perfect solution but in some cases it might be much easier and faster that writing protocols in swift to match every objective-c interface as proposed in other answer.
My situation may be particular to my setup, but I'll offer it here, in case it helps someone else. I also have an Objective-C framework (framework A) with private headers that I need to use in a Swift framework (framework B) that links it. Some additional details:
Each framework is in a separate project in the workspace
The project uses CocoaPods
The podspec defines the following dependence relationship between the two frameworks:
s.subspec 'FrameworkA' do |cs|
cs.vendored_frameworks = "lib/FrameworkA.framework"
end
s.subspec 'FrameworkB' do |ts|
ts.dependency 'FrameworkA'
ts.vendored_frameworks = "lib/FrameworkB.framework"
end
The solution offered by #rintaro works great for me when running in Xcode, but once the Pod is deployed, FrameworkB is unable to find the private headers using the paths in the private modulemap that lives in FrameworkA. What worked for me was to use a relative path to the PrivateHeaders dir in the private modulemap:
module FrameworkA_Private {
header "../FrameworkA.framework/PrivateHeaders/Private.h"
export *
}
This works in Xcode and in the final product installed using CocoaPods. It's a little bit hacky, since it references a folder in the final build product, and I wouldn't be surprised if there's some other way to tell CocoaPods how to preserve these paths, but whatever it is, I haven't found it. This solves the problem for now.