Xcode: use a static library in a framework? - ios

I have a framework that links against two static libraries. Each static library is a target and the framework is a target. The framework wraps the static libraries and clients of the framework only care about calling framework code (which in turn may call code from each static library). Is is possible to have the framework include the required headers and object files? When I build I get the infamous Include of non-module header inside framework module error. Each static library exports its headers and I've added the headers as public headers in the framework. Still no luck (and I would think there's a solution that doesn't require this).

Each static library must export a module.modulemap file with its headers. Contrary to popular wisdom, once this is complete, you need not add a bunch of headers to the Public section of the framework's Headers build phase in order to alleviate the error. The process is described in detail here: https://bjhomer.com/2015/05/03/defining-modules-for-static-libraries/

Related

Include of non-modular header inside framework error

I realize this is a common error and has been asked several times but I am unable to resolve it after trying several different things based on others' answers.
I am creating a framework project (Obj-c) which uses another third-party framework. In MyFramework's umbrella header, I have the import statement of third-party framework. When I packaged my framework and included it in another iOS project, it fails to build with this error.
Things I tried:
Turned ON "Allow non-modular includes in Framework modules"
Added the umbrella header of the third-party framework as a "Public" header
Please advise what is missing here. Thanks in advance!
Here's the Exact Error:
Include of non-modular header inside framework module
'MyFramework.TestManager':
'/TestApp/WindowsAzureMessaging.framework/Headers/WindowsAzureMessaging.h'
Also, from the path it looks like it is trying to search for it in the TestApp project, whereas it should refer from the Framework.
I imported the Azure Messaging Framework in the Umbrella header as shown below:
#import <WindowsAzureMessaging/WindowsAzureMessaging.h>
I was able to reproduce your error. I looks like the WindowsAzureMessaging header and all other headers it refers are not made to be used in a modular framework umbrella header, because it uses "user" imports instead of "system" imports relative to the framework, and also it doesn't have the modulemap file.
You have several options:
adapt their code and make it build as a module (make it "modular").
not include it in the umbrella header (avoid referencing things from it there), but link it and use directly in the app and your framework.
instead of using the file framework as a separate entity, you could take (copy) their source code (m and h files) and compile into your framework, and then expose some headers as your own headers.
I think that option 1 is the right way to go. It is not hard, and if you manage to do that, think about making a pull request for their repo, because this is going to benefit everyone.

xcode static library public/private/project header files

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.

How to import private framework headers in a Swift framework?

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.

How to access variables from iOS static library in client application?

I am builiding libraries in iOS, for that i want to access variables from my iOS static library. To do this, is it necessary to declare variables used in my library class as public variables?, so that I can access variables from my library class from another application?
If so, how to declare variables as public in library class? If not, is there any other method to access variables from library in client application?
If the headers are copied in the Copy Files build phase, then their symbols will be available to the client application which links your static library. This build phase is created automatically for you when Xcode creates the static iOS library project.
If you wish to exercise more control over which headers are public and which are private, then you'll need to create separate copy headers build phases for public and private headers and manually move header files between those build phases. You'll also need to adjust the header search paths. This blog post may help with those implementation details.

How to create static library from an existing framework in iOS?

I have been provided with a framework by a third party vendor for an iPhone hardware accessory. So I have a folder like Device.framework. Inside that folder is a binary file and a set of .h files. There are instructions for how to add this to an iOS project and use the classes contained within. However, I'm actually using MonoTouch and want to use a static library.
Is there a way to create a static library that makes all the classes from the framework available in the static library? So in my MonoTouch project I would link in the static library and have access to that framework.
A *.framework is simply a package containing: the static library, headers, associated meta data. Copy and paste the .framework and extract the static *.a file and related header files.
Then it's simply a matter of using the MonoTouch btouch tool to bind the static library for use in your MonoTouch project. There is a great example of how to bind a native library to MonoTouch on Github. With guidance on targeting simulator + device and using the LinkWith attribute to embed the static library in a single *.dll:
https://github.com/xamarin/monotouch-samples/tree/master/BindingSample
Also, make sure to check out the btouch Reference documentation here:
http://docs.xamarin.com/ios/advanced_topics/binding_objective-c_types
Rename that binary file to Device.a. You can do that as the framework you mention is not done by Apple, hence it has to be a static library and not a dynamic one.
Make sure your project links that library (Device.a).
Include the headers in your project and reference them where appropriate.

Resources