7

I'm trying to create a mixed ObjC-Swift framework. But I'm losing a lot of hairs trying to make a private module for my Swift files.

I followed some steps in iOS mixed dynamic framework - bridge objc headers with private module combined with some steps in https://stackoverflow.com/a/36878037/749438

Basically I have a module.modulemap now which has the following content:

module MyCore {    //Adding 'framework' before module is giving me an "Umbrella not found"-error :/
umbrella header "MyCore.h"

export *
module * { export * }

explicit module MyCorePrivate {
    header "MyCore_PrivateUmbrella.h"
    export *
}

}

The MyCore_PrivateUmbrella.h imports all headers I want to privately expose to my Swift code. The reason behind it is just that it's easier to include 1 header in the module then all the to-be-exposed headers (since you need to include the specific paths to the headers...).

My build settings look like:

  • DEFINES_MODULE: YES
  • PRODUCT_MODULE_NAME: MyCore
  • CLANG_ENABLE_MODULES: YES
  • SWIFT_OBJC_INTERFACE_HEADER_NAME: MyCore-Swift.h
  • SWIFT_INCLUDE_PATHS: path to the directory of the module.modulemap

and last but not least; ALWAYS_SEARCH_USER_PATHS is set to NO

In my Swift files I import the module using import MyCore.MyCorePrivate. This works as expected and I can use my code from Objective-C.
Building the project gives me an error like this (the black bars only hide the project name and path to the file):

what is this error?

Now clicking the error brings me to the generated MyCore-Swift.h where the @import MyCore.MyCorePrivate is seemingly wrong.

I've got no idea as of why it's wrong, neither do I know how to fix this. Especially since it's a file generated by XCode.

Does anyone knows what's going down here?

jscs
  • 63,694
  • 13
  • 151
  • 195
Gerald Eersteling
  • 1,244
  • 14
  • 28
  • Are you distributing a closed source framework? If not then it makes no sense to have a private swift module as swift code has access levels internally – Avba Jun 28 '16 at 15:52
  • 1
    That's the plan. The framework will be distributed where I'd like to keep (most of) the headers private. – Gerald Eersteling Jun 28 '16 at 15:59

1 Answers1

2

You will need to modify the resulting framework after building it:

1) Don't create any private modules. Revert back to default settings.

2) Any code you want to expose to swift add to your framework header and make sure the headers are set as public in the build section or else swift code wont have access. (Use the <> syntax)

3) Any code from swift to objc make public.

4) Compile you project

5) go to your framework build directory (i.e MyFramework.framework)

6) open the framework header file in the headers directory of the framework (Myframework.h file)

7) Delete all the import statements that should have been private from the framework header

8) Delete all the .h files for the headers that should have been private from the headers directory ( you removed the import statements from the main framework header)

9) go to the .module file and remove the swift modules section

The module file should be very bare bones:

framework module MyFramework { 
umbrella header "MyFramework.h" export * module * {export *}}
Avba
  • 14,822
  • 20
  • 92
  • 192
  • Also notice that if you are distributing your closed source framework for others to use during their development, you will need to create a universal binary so that your framework will work on their simulator and on their devices. – Avba Jun 28 '16 at 16:21
  • I tried doing this (actually was the first thing I did when starting to migrate to Swift), but I keep getting the _"Include of non-modular header inside framework module"_ error; the error eventually led me to the solution as I posted in my question. I also checked every `#import` in my project. I made sure I only imported necessary files (such as protocols) in the headers, forwarding the others with `@class `. – Gerald Eersteling Jun 29 '16 at 07:34
  • I have this solution in production shipped frameworks. You must have a mistake somewhere. Run a test on an empty framework project with dummy files following these steps. – Avba Jun 29 '16 at 07:40
  • I just made _everything_ public, getting kinda annoyed. Now the only error (the non-modular one) is coming from `sqlite3.h`, how can I make that one public as well? – Gerald Eersteling Jun 29 '16 at 07:42
  • My god, it finally worked. I just made an new project, and followed your steps. I've got absolutely no idea why this works now, since I already did the same things before. Maybe it's because I added the single class that imported the sqlite3 as the very last to the new project? Maybe it is because XCode hates me? Maybe it is because the moon is round? I don't have a clue. – Gerald Eersteling Jun 29 '16 at 08:06