{"id":3668,"date":"2013-03-25T17:45:25","date_gmt":"2013-03-26T00:45:25","guid":{"rendered":"http:\/\/blog.xamarin.com\/?p=3668"},"modified":"2019-07-03T10:12:00","modified_gmt":"2019-07-03T17:12:00","slug":"producing-better-bindings-3-selectors","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/xamarin\/producing-better-bindings-3-selectors\/","title":{"rendered":"Producing Better Bindings #3: Selectors"},"content":{"rendered":"<p>\t\t\t\tThis blog post is about producing better bindings of Objective-C libraries for <a href=\"http:\/\/xamarin.com\/monotouch\">Xamarin.iOS<\/a> and <a href=\"http:\/\/xamarin.com\/mac\">Xamarin.Mac<\/a>. Read the series <a href=\"\/producing-better-bindings-for-xamarin.ios-and-xamarin.mac\/\">introduction<\/a> to get a better idea why this is important and how it can save you time and headaches.<\/p>\n<p><strong>What can go wrong ?<\/strong><\/p>\n<p>Binding selectors is largely done using the <code>[Export(\"\")]<\/code> attribute. As such, it shares a lot of similarities with the\u00a0<a href=\"\/producing-better-bindings-2-fields\/\"><code>[Field]<\/code> attribute<\/a>\u2014including the main cause of issues: <strong>typos<\/strong>.<\/p>\n<p>In fact, the all time <em>favorite<\/em>\u00a0issue is likely the infamous missing colon at the selector&#8217;s end (when parameters are used). For example, the following Objective-C method declaration:<\/p>\n<pre class=\"lang:objc\">\n-(void) step: (ccTime) dt;\n<\/pre>\n<p>should be translated to:<\/p>\n<pre class=\"lang:csharp decode:true\">\n[Export (&quot;step:&quot;)] \/\/ without the ':' this method won't work\nvoid Step (float deltaTime);\n<\/pre>\n<p>However, it is also a common error to forget the <code>[Static]<\/code> attribute when a selector starts with a plus sign (<code>+<\/code>)\u00a0character. The Objective-C static method declaration:<\/p>\n<pre class=\"lang:objc\">\n+(id) actionWithDuration:(ccTime)duration angle:(float)angle;\n<\/pre>\n<p>should be translated to:<\/p>\n<pre class=\"lang:csharp decode:true\">\ninterface CCRotateBy {\n\t[Static] \/\/ easy to forget, unusable without it!\n\t[Export (&quot;actionWithDuration:angle:&quot;)]\n\tCCRotateBy Create (float duration, float deltaAngle);\n\t\/\/ ...\n}\n<\/pre>\n<p>A less common issue is\u00a0<em>wrong<\/em> builds. Your bindings might include selectors that are not part of the native library binary (e.g. missing source files, wrong defines\u2026) even if they are documented and present in the header files.<\/p>\n<p>Finally, some library updates might introduce breaking changes, including removed API entry points. If you only bind new APIs, you might be shipping old, missing, APIs without realizing it.<\/p>\n<p><strong>What can we check for ?<\/strong><\/p>\n<p>Quite a few things, including:<\/p>\n<ul>\n<li><strong>Static selectors<\/strong>: One test case checks every type, inheriting from <code>NSObject<\/code>, inside your assembly and tries every <code>[Export]<\/code> on static methods. If <code>respondsToSelector:<\/code> returns <code>false<\/code> then an error is raised.<\/li>\n<li><strong>Instance selectors<\/strong>: A similar test case does the same for every instance method and tries all <code>[Export]<\/code> selectors. If <code>instancesRespondToSelector:<\/code> returns <code>false<\/code> then an error is raised.<\/li>\n<li><strong>Missing setters<\/strong>: In general, we can only test what&#8217;s present. If something is missing then we have no way to test it. In this case we can stretch the limits to <em>try<\/em> to find some missing property setters. If a property has a getter <code>Foo<\/code> then we check if a selector exists with the <em>default<\/em> selector name, i.e. &#8220;<code>setFoo:<\/code>&#8220;. If this selector exists and the property does not have a setter then an error is raised.<\/li>\n<\/ul>\n<p><strong>Why is this important ?<\/strong><\/p>\n<p>The binding consumer expects public APIs to exist (i.e. not throw a missing selector exception) in the bound library. However, without testing every method\/property bound you cannot be sure if they <em>really<\/em> exist or not outside your .NET assembly. You can avoid many headaches for yourself and other binding consumers in exchange for a few extra &#8216;<code>:<\/code>&#8216;.<\/p>\n<p><strong>How to fix issues ?<\/strong><\/p>\n<p>Most cases are fixed by verifying the selector name. If it exists <em>as-is<\/em> then check if you&#8217;re not missing a <code>[Static]<\/code> attribute (or if you have one where none is needed). Again, the library&#8217;s documentation and\/or header files should have all the information you need to fix them.<\/p>\n<p>One quite important exception is the Objective-C Protocol References. The protocols are similar to .NET interfaces, \u00a0but not quite. The major difference is that many members are not, in Objective-C, required to be implemented. However, to create your bindings you <strong>might<\/strong> have to bind them inside a base class, without being sure every subclasses will provide them\u2014not ideal, but the alternative would be worse.<\/p>\n<p>To let the test run with those special cases, you can override the <a href=\"https:\/\/github.com\/mono\/maccore\/blob\/master\/tests\/bindings\/ApiSelectorTest.cs\">fixture<\/a>&#8216;s <code>Skip(Type,string)<\/code> method. E.g. for <a href=\"https:\/\/github.com\/mono\/monotouch-bindings\/blob\/master\/cocos2d\/binding-test\/BindingSelectorTest.cs\">cocos2d test fixture<\/a> I needed to consider two special cases:<\/p>\n<pre class=\"lang:csharp decode:true\">\n\t[TestFixture]\n\tpublic class BindingSelectorTest : ApiSelectorTest {\n\t\tprotected override bool Skip (Type type, string selectorName)\n\t\t{\n\t\t\tswitch (selectorName) {\n\t\t\t\/\/ CCRGBAProtocol\n\t\t\tcase &quot;doesOpacityModifyRGB&quot;:\n\t\t\tcase &quot;opacityModifyRGB:&quot;:\n\t\t\t\tswitch (type.Name) {\n\t\t\t\tcase &quot;CCAtlasNode&quot;:\n\t\t\t\tcase &quot;CCLabelBMFont&quot;:\n\t\t\t\tcase &quot;CCLayerColor&quot;:\n\t\t\t\tcase &quot;CCMenu&quot;:\n\t\t\t\tcase &quot;CCMenuItemLabel&quot;:\n\t\t\t\tcase &quot;CCMenuItemSprite&quot;:\n\t\t\t\tcase &quot;CCMenuItemToggle&quot;:\n\t\t\t\tcase &quot;CCSprite&quot;:\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t\/\/ CCTargetedTouchDelegate\n\t\t\tcase &quot;ccTouchMoved:withEvent:&quot;:\n\t\t\tcase &quot;ccTouchEnded:withEvent:&quot;:\n\t\t\tcase &quot;ccTouchCancelled:withEvent:&quot;:\n\t\t\tcase &quot;ccTouchesBegan:withEvent:&quot;:\n\t\t\tcase &quot;ccTouchesMoved:withEvent:&quot;:\n\t\t\tcase &quot;ccTouchesEnded:withEvent:&quot;:\n\t\t\tcase &quot;ccTouchesCancelled:withEvent:&quot;:\n\t\t\t\treturn type.Name == &quot;CCLayer&quot;;\n\t\t\t}\n\t\t\treturn base.Skip (type, selectorName);\n\t\t}\n\t}\n<\/pre>\n<p>Another less common issue is that some types might act as a proxy to another type. In such cases,\u00a0<code>respondToSelector<\/code> for <code>foo<\/code> might return <code>false<\/code> but calling it <strong>will<\/strong> work. That behaviour is rarely documented and it&#8217;s often best to write some <em>traditional<\/em> unit tests for such cases. The same override can be used to exclude this case too.<\/p>\n<p><strong>What&#8217;s missing ?<\/strong><\/p>\n<p>With the notable exception of <em>most<\/em> missing setters, the test fixture cannot detect missing selectors: only the <code>[Export(\"\")]<\/code> selectors are validated.<\/p>\n<p><b>Read the rest of the series:<\/b><\/p>\n<ul>\n<li><a href=\"\/producing-better-bindings-for-xamarin.ios-and-xamarin.mac\/\">Introduction<\/a><\/li>\n<li><a href=\"\/producing-better-bindings-1-constructors\/\">Part 1: Constructors<\/a><\/li>\n<li><a href=\"\/producing-better-bindings-2-fields\/\">Part 2: Fields<\/a><\/li>\n<li><a href=\"\/producing-better-bindings-3-selectors\/\">Part 3: Selectors<\/a><\/li>\n<li><a href=\"\/producing-better-bindings-4-signatures\/\">Part 4: Signatures<\/a><\/li>\n<\/ul>\n<p><a href=\"http:\/\/forums.xamarin.com\/discussion\/2433\/producing-better-bindings\">Discuss this post on the Xamarin forums<\/a>\t\t<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This blog post is about producing better bindings of Objective-C libraries for Xamarin.iOS and Xamarin.Mac. Read the series introduction to get a better idea why this is important and how it can save you time and headaches. What can go wrong ? Binding selectors is largely done using the [Export(&#8220;&#8221;)] attribute. As such, it shares [&hellip;]<\/p>\n","protected":false},"author":5741,"featured_media":39167,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[303],"tags":[4],"class_list":["post-3668","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ios","tag-xamarin-platform"],"acf":[],"blog_post_summary":"<p>This blog post is about producing better bindings of Objective-C libraries for Xamarin.iOS and Xamarin.Mac. Read the series introduction to get a better idea why this is important and how it can save you time and headaches. What can go wrong ? Binding selectors is largely done using the [Export(&#8220;&#8221;)] attribute. As such, it shares [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/3668","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/users\/5741"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/comments?post=3668"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/3668\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media\/39167"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media?parent=3668"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/categories?post=3668"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/tags?post=3668"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}