Share via


Advanced (manual) Real-World Example

This example uses the POP library from Facebook.

This section covers a more advanced approach to binding, where we will use Apple's xcodebuild tool to first build the POP project, and then manually deduce input for Objective Sharpie. This essentially covers what Objective Sharpie is doing under the hood in the previous section.

 $ git clone https://github.com/facebook/pop.git
Cloning into 'pop'...
   _(more git clone output)_

$ cd pop

Because the POP library has an Xcode project (pop.xcodeproj), we can just use xcodebuild to build POP. This process may in turn generate header files that Objective Sharpie may need to parse. This is why building before binding is important. When building via xcodebuild ensure you pass the same SDK identifier and architecture that you intend to pass to Objective Sharpie (and remember, Objective Sharpie 3.0 can usually do this for you!):

$ xcodebuild -sdk iphoneos9.0 -arch arm64

Build settings from command line:
    ARCHS = arm64
    SDKROOT = iphoneos8.1

=== BUILD TARGET pop OF PROJECT pop WITH THE DEFAULT CONFIGURATION (Release) ===

...

CpHeader pop/POPAnimationTracer.h build/Headers/POP/POPAnimationTracer.h
    cd /Users/aaron/src/sharpie/pop
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/Users/aaron/bin::/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/git/bin:/Users/aaron/.rvm/bin"
    builtin-copy -exclude .DS_Store -exclude CVS -exclude .svn -exclude .git -exclude .hg -strip-debug-symbols -strip-tool /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/strip -resolve-src-symlinks /Users/aaron/src/sharpie/pop/pop/POPAnimationTracer.h /Users/aaron/src/sharpie/pop/build/Headers/POP

...

** BUILD SUCCEEDED **

There will be a lot of build information output in the console as part of xcodebuild. As displayed above, we can see that a "CpHeader" target was run wherein header files were copied to a build output directory. This is often the case, and makes binding easier: as part of the native library's build, header files are often copied into a "publicly" consumable location which can make parsing easier for binding. In this case, we know that POP's header files are in the build/Headers directory.

We are now ready to bind POP. We know that we want to build for SDK iphoneos8.1 with the arm64 architecture, and that the header files we care about are in build/Headers under the POP git checkout. If we look in the build/Headers directory, we'll see a number of header files:

$ ls build/Headers/POP/
POP.h                    POPAnimationTracer.h     POPDefines.h
POPAnimatableProperty.h  POPAnimator.h            POPGeometry.h
POPAnimation.h           POPAnimatorPrivate.h     POPLayerExtras.h
POPAnimationEvent.h      POPBasicAnimation.h      POPPropertyAnimation.h
POPAnimationExtras.h     POPCustomAnimation.h     POPSpringAnimation.h
POPAnimationPrivate.h    POPDecayAnimation.h

If we look at POP.h, we can see it is the library's main top-level header file that #imports other files. Because of this, we only need to pass POP.h to Objective Sharpie, and clang will do the rest behind the scenes:

$ sharpie bind -output Binding -sdk iphoneos8.1 \
    -scope build/Headers build/Headers/POP/POP.h \
    -c -Ibuild/Headers -arch arm64

Parsing Native Code...

Binding...
  [write] ApiDefinitions.cs
  [write] StructsAndEnums.cs

Binding Analysis:
  Automated binding is complete, but there are a few APIs which have
  been flagged with [Verify] attributes. While the entire binding
  should be audited for best API design practices, look more closely
  at APIs with the following Verify attribute hints:

  ConstantsInterfaceAssociation (1 instance):
    There's no fool-proof way to determine with which Objective-C
    interface an extern variable declaration may be associated.
    Instances of these are bound as [Field] properties in a partial
    interface into a near-by concrete interface to produce a more
    intuitive API, possibly eliminating the 'Constants' interface
    altogether.

  StronglyTypedNSArray (4 instances):
    A native NSArray* was bound as NSObject[]. It might be possible
    to more strongly type the array in the binding based on
    expectations set through API documentation (e.g. comments in the
    header file) or by examining the array contents through testing.
    For example, an NSArray* containing only NSNumber* instances can
    be bound as NSNumber[] instead of NSObject[].

  MethodToProperty (2 instances):
    An Objective-C method was bound as a C# property due to
    convention such as taking no parameters and returning a value (
    non-void return). Often methods like these should be bound as
    properties to surface a nicer API, but sometimes false-positives
    can occur and the binding should actually be a method.

  Once you have verified a Verify attribute, you should remove it
  from the binding source code. The presence of Verify attributes
  intentionally cause build failures.

  For more information about the Verify attribute hints above,
  consult the Objective Sharpie documentation by running 'sharpie
  docs' or visiting the following URL:

    http://xmn.io/sharpie-docs

Submitting usage data to Xamarin...
  Submitted - thank you for helping to improve Objective Sharpie!

Done.

You will notice that we passed a -scope build/Headers argument to Objective Sharpie. Because C and Objective-C libraries must #import or #include other header files that are implementation details of the library and not API you wish to bind, the -scope argument tells Objective Sharpie to ignore any API that is not defined in a file somewhere within the -scope directory.

You will find the -scope argument is often optional for cleanly implemented libraries, however there is no harm in explicitly providing it.

Tip

If the library's headers import any iOS SDK headers, e.g. #import <Foundation.h>, then you will need to set the scope otherwise Objective Sharpie will generate binding definitions for the iOS SDK header that was imported, resulting in a huge binding that will likely generate errors when compiling the binding project.

Additionally, we specified -c -Ibuild/headers. Firstly, the -c argument tells Objective Sharpie to stop interpreting command line arguments and pass any subsequent arguments directly to the clang compiler. Therefore, -Ibuild/Headers is a clang compiler argument that instructs clang to search for includes under build/Headers, which is where the POP headers live. Without this argument, clang would not know where to locate the files that POP.h is #importing. Almost all "issues" with using Objective Sharpie boil down to figuring out what to pass to clang.

Completing the Binding

Objective Sharpie has now generated Binding/ApiDefinitions.cs and Binding/StructsAndEnums.cs files.

These are Objective Sharpie's basic first pass at the binding, and in a few cases it might be all you need. As stated above however, the developer will usually need to manually modify the generated files after Objective Sharpie finishes to fix any issues that could not be automatically handled by the tool.

Once the updates are complete, these two files can now be added to a binding project in Visual Studio for Mac or be passed directly to the btouch or bmac tools to produce the final binding.

For a thorough description of the binding process, please see our Complete Walkthrough instructions.