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:
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.
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.