0

Answer to my problem:

Because I didn't find an answer to the original problem (namely, exposing the headers of dependencies loaded via CocoaPods) I don't feel like I should post an answer to my own question. However I did find a solution to my specific issue that worked around exposing the headers.

In my problem, I was trying to use Objective Sharpie to create Xamarin bindings for my framework. My framework exposed a class which extended JSONModel, and therefore the JSONModel header files were required in order to build those bindings.

Reading through the Objective Sharpie documentation, I discovered that Clang has a "framework directory" command line argument which tells it which directory to look in for frameworks referenced by the project.

I simply put JSONModel.framework and myframework.framework in the same directory, then when I ran Objective Sharpie I ran it like so:

sharpie bind -sdk ios myframework.framework/Headers/myframework.h -c -F .

This created the bindings successfully.

I did run into one small frustration. I could use Objective Sharpie's -scope command line parameter and it would output an approximately ~1800 line file with only bindings for my own classes. However this file did NOT include definitions for JSONModel, and was not functional in my Xamarin project. If I left off the -scope parameter, it created bindings for everything, including all of Foundation. This created an approximately ~84000 line file.

To work around this I manually copied the JSONModel bindings from the 84000 line file to the 1800 line file, and that worked perfectly.

The Problem:

I have a framework I've written in Objective-C. This framework has 3 dependencies (namely CocoaLumberjack, Google-IMA-iOS-SDK, and JSONModel)

Within my framework, I have a public class defined like so:

Broadcast.h

#import <JSONModel/JSONModel.h>

@interface Broadcast : JSONModel

@property (nonatomic) NSString *title;
@proeprty (nonatomic) NSString *url;

@end

When I build my framework it works just fine. When I import my framework into an Objective-C project (and include the JSONModel framework, as it's a dependency) it works just fine. When I use CocoaPods to install my framework, it works just fine.

The problem came when I tried to create C# bindings (for Xamarin) for my framework using Objective Sharpie

When I ran:

sharpie bind -sdk ios myframework.framework/Headers/myframework.h

I got the error: Cannot find JSONModel.h

Specifically it's looking for JSONModel.h within my framework. This wasn't an issue with CocoaLumberjack or the Google IMA SDK because they were strictly implementation details, and none of my public headers referenced them. However when it comes to JSONModel, its interface is actually part of my public API.

Is there a way to put a copy of JSONModel.h (and all other JSONModel headers) in my output Headers directory? Or, otherwise, some way to tell Objective Sharpie to look in multiple frameworks for the bindings?

Update

A quick update, as I've been continuing to mess with this over the past hour. I was able to get Objective Sharpie to at least run by telling Clang where it could find JSONModel.h and making a tweak to my code:

Broadcast.h

#import <JSONModel.h> // <-----

@interface Broadcast : JSONModel

@property (nonatomic) NSString *title;
@proeprty (nonatomic) NSString *url;

@end

Then when running Objective Sharpie:

sharpie bind -sdk ios -scope myframework.framework/Headers myframework.framework/Headers/myframework.h -c -IJSONModel.framework/Headers

This worked a bit better, but ran into one issue and left me with one question:

  • In the bindings file that was generated, it said typeof(JSONModel) in several places, which was not defined and threw an error. I can get around this by removing the -scope parameter, but this creates a bindings file that's 84000 lines long versus 1800 lines long, and includes bindings for things like AFNetworking, AVPlayer, etc.
  • The reason I imported <JSONModel/JSONModel.h> before is both because this is how I've always seen CocoaPods dependencies imported, and because it was recommended by auto-complete. So why did importing <JSONModel.h> work just as well? Will this break something in the future?

To correct the scope issue without generating an 84000 line bindings file I also tried using scopes like . or "$(pwd)" but nothing worked. It either created an 1800 line file with no definition for JSONModel, or an 84000 line file with definitions for everything

Update 2

While I still don't fully appreciate the difference between <JSONModel/JSONModel.h> and <JSONModel.h>, I did learn something:

  • Either one will allow me to build a .framework file from my framework project directly
  • Only <JSONModel/JSONModel.h> will work when I try to import my framework via CocoaPods inside of an app
  • Only <JSONModel.h> will work when I try to create C# bindings using Objective Sharpie

So that nixes that solution

stevendesu
  • 15,753
  • 22
  • 105
  • 182

1 Answers1

0

Regarding <X/X.h> vs <X.h>

Clang has a built-in concept of "frameworks" (you can specify the framework search directory using the -F flag). When you use the <X/*> import syntax, Clang will search for a framework named X. CocoaPods references imported dependencies by using this -F flag, so you must use this to appropriately import third-party frameworks

Meanwhile the simpler <X.h> import syntax is standard syntax in most C compilers to say "check the header search path for X". Xcode automatically adds everything in your project to this search path, so it tends to work -- but it's not good practice and can lead to things like CocoaPods breaking

Fixing my issue

I never found an automated way to make third-party framework headers public. You can of course use a "Run Script" phase in your build phases to copy the headers from their framework to your own, but this is ugly. What I ended up doing was writing a small Python script to generate my Xamarin bindings which injected all of the JSONModel bindings before my own

stevendesu
  • 15,753
  • 22
  • 105
  • 182
  • This isn't really a good "answer" to this question, it's just the best I've got. If someone can answer the ***actual*** question, I'll gladly accept their solution – stevendesu Dec 06 '19 at 16:30