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
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
#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
You will find the
-scope argument is often optional for cleanly implemented libraries, however there is no harm in explicitly providing it.
If the library's headers import any iOS SDK headers, e.g.
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
#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
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
tools to produce the final binding.
For a thorough description of the binding process, please see our Complete Walkthrough instructions.