{"id":1503,"date":"2013-08-22T09:23:36","date_gmt":"2013-08-21T21:23:36","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=1503"},"modified":"2013-08-22T09:23:36","modified_gmt":"2013-08-21T21:23:36","slug":"sharing-code-across-platforms-in-oxygene","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/1503\/","title":{"rendered":"Sharing Code Across Platforms in Oxygene"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">5<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>There seems to be a perception among some people that Delphi is in the unique position of allowing developers to share and re-use code across the various platforms that it&#8217;s compiler can now (and will soon) target.  But this is not the case.  <strong>Oxygene<\/strong> has had this capability right from the start.<\/p>\n<p><!--more--><\/p>\n<p>To exercise this, at an admittedly rudimentary level so far, I created a simple application for <em>iOS<\/em> using <strong>Oxygene Nougat<\/strong>.  The application is very simple, consisting of two edit fields and a button.  You enter two numbers into the fields, press the button and the application pops up a message with the sum of those two numbers.<\/p>\n<p>I then created the same application using <strong>Oxygene Cooper<\/strong>, for <em>Android<\/em>.<\/p>\n<p>Clearly the amount of &#8220;business logic&#8221; involved is minimal.  The vast majority of this simple application resides in the UI.  But the object of this exercise was to explore the sharing of generic, non-platform-specific code between different <strong>Oxygene<\/strong> projects.<\/p>\n<p>First, here is my <strong>Adder<\/strong> class that will be shared across the platforms:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n\r\nnamespace Deltics.Adder;\r\n\r\ninterface\r\n\r\n  type\r\n    Adder = public class\r\n    public\r\n      property A: Integer;\r\n      property B: Integer;\r\n      property Sum: Integer read A + B;\r\n    end;\r\n\r\nimplementation\r\n\r\nend.\r\n<\/pre>\n<p>As I say, this is hardly rocket science.  But something worth pointing out here is the efficiency of the <strong>Oxygene<\/strong> syntax.<\/p>\n<p>First, to declare a <em>read-write<\/em> property, such as the <strong>A<\/strong> and <strong>B<\/strong> properties in this case, you do not need to erect the backing field and specify the read\/write accessors.  Simply declare the property and the type.  You can of course specify <em>read-only<\/em> properties, and we have one here also which demonstrates another efficiency.<\/p>\n<p>The <strong>Sum<\/strong> property is a <em>read-only<\/em> property which returns the result of the addition of the <strong>A<\/strong> and <strong>B<\/strong> properties.  Such relationships can be expressed directly with <em>read expressions<\/em>.  There is no need to implement a separate property read accessor method.<\/p>\n<p>The other thing to note is that &#8220;units&#8221; in <em>Oxygene<\/em> are declared as members of a <strong>namespace<\/strong>.  i.e. rather than namespaces being fabricated from arbitrarily unique unit names, they are a formal part of the language.  There is no need for each unit to have a unique &#8220;unit name&#8221; &#8211; the filename distinguishes them as separate files after all.<\/p>\n<p>Just for a fun comparison, here&#8217;s the equivalent <strong>Delphi<\/strong> class:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n\r\nunit Deltics.Adder;\r\n\r\ninterface\r\n\r\n  type\r\n    TAdder = class\r\n    private\r\n      fA: Integer;\r\n      fB: Integer;\r\n      function get_Sum: Integer;\r\n    public\r\n      property A: Integer read fA write fA;\r\n      property B: Integer read fA write fA;\r\n      property Sum: Integer read get_Sum;\r\n    end;\r\n\r\nimplementation\r\n\r\n  function TAdder.get_Sum: Integer;\r\n  begin\r\n    result := fA + fB;\r\n  end;\r\n\r\nend.\r\n<\/pre>\n<p>Just as Oxygene is a &#8220;better C#&#8221;, it&#8217;s also a &#8220;better Delphi&#8221;.  imho.  \ud83d\ude42<\/p>\n<p>But back to Oxygene.<\/p>\n<p>I won&#8217;t bother you with the details of the applications themselves.  Suffice to say that in each case I constructed the UI programmatically, as an exercise primarily in forcing myself to actually write some platform code as a learning exercise.  But it should be noted that despite perceptions to the contrary in some quarters, <strong>Oxygene<\/strong> <em>does<\/em> have GUI building tools.  Or rather, it works alongside the existing GUI building tools provided for each platform.<\/p>\n<p>Did somebody say &#8220;native&#8221; ?<\/p>\n<p>But I digress.  Again.  \ud83d\ude42<\/p>\n<p>In terms of solution structure (something that I expect to evolve as I gain experience with multiple platforms and <em>Visual Studio<\/em> solution concepts in general) I arrived at the following on disk:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n   Adder                    [Solution folder]\r\n      Deltics.Adder.pas\r\n      iAdder                [iOS project folder]\r\n      org.me.adder          [Android project folder]\r\n<\/pre>\n<p>The <strong>Deltics.Adder<\/strong> unit (I think it is correct to still call it a unit even though the code is not part of a formal unit declaration as such) is added to each of the <strong>iAdder<\/strong> and <strong>org.me.adder<\/strong> projects using &#8220;Add Link&#8221; (i.e. reference) so that each project references this common file rather than having separate copies (which irritatingly is the default behaviour in <em>Visual Studio<\/em> when adding an existing file to a project).<\/p>\n<p>So, now to those projects.  Here is the code from the <em>iOS<\/em> GUI that employs my simple <strong>Adder<\/strong> class:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\nmethod RootViewController.btnSumPressed;\r\nvar\r\n  adder: Adder;\r\n  msg: UIAlertView;\r\nbegin\r\n  adder := new Adder;\r\n\r\n  adder.A := textA.text.integerValue;\r\n  adder.B := textB.text.integerValue;\r\n\r\n  \/\/ Initialise our Alert View Window with options\r\n  msg := UIAlertView.alloc.initWithTitle('The Sum of the Parts is...')\r\n                           message(adder.Sum.stringValue)\r\n                           delegate(self)\r\n                           cancelButtonTitle('OK')\r\n                           otherButtonTitles(NIL);\r\n                           \r\n  msg.show;\r\nend;\r\n<\/pre>\n<p>And here is the equivalent code from the Android GUI:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\nmethod MainActivity.ButtonOnClick(v: View);\r\nvar\r\n  myAdder: Adder;\r\n  msg: AlertDialog;\r\nbegin\r\n  myAdder := new Adder;\r\n  myAdder.A := Integer.parseInt(textA.Text.toString);\r\n  myAdder.B := Integer.parseInt(textB.Text.toString);\r\n\r\n  msg := new AlertDialog.Builder(self).create;\r\n  msg.Title   := 'The Sum of the Parts is...';\r\n  msg.Message := myAdder.Sum.toString;\r\n  msg.show();\r\nend;\r\n<\/pre>\n<p>Now I should mention that the <strong>AlertDialog<\/strong> code here is incomplete.  As written above it currently presents a dialog with no buttons, meaning that I have to use the <em>Android<\/em> &#8220;back&#8221; hardware key to dismiss it, but the point of this post is the sharing of code between platforms not the platform UI concerns that I am still learning (I have to say that so far <em>iOS<\/em> has been the more pleasant experience, by far).<\/p>\n<p>Of course this does raise the interesting point that on <em>iOS<\/em> there is no hardware &#8220;back button&#8221; which in turn raises the question, just how exactly does anyone think they can realistically create one app from one set of source that will behave <em>properly<\/em> on different platforms that themselves <em>work differently<\/em> ?<\/p>\n<p>This is of course why I chose <em>Oxygene<\/em>.  Yes, it is an inconvenience having to create platform specific GUI&#8217;s.  But it is also in my view <em>necessary<\/em>, and to think otherwise is frankly delusional.<\/p>\n<p>But back to the issue of the day.<\/p>\n<p>Let us compare the usage of the shared &#8220;business object&#8221;.  First, the setting of the <strong>A<\/strong> and <strong>B<\/strong> properties:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n  \/\/ Nougat - iOS\r\n  adder.A := textA.text.integerValue;\r\n  adder.B := textB.text.integerValue;\r\n\r\n  \/\/ Cooper - Android\r\n  myAdder.A := Integer.parseInt(textA.Text.toString);\r\n  myAdder.B := Integer.parseInt(textB.Text.toString);\r\n<\/pre>\n<p>This illustrates some pretty fundamental platform differences between <em>iOS<\/em> and <em>Android<\/em>, each requiring different incantations to obtain an integer from the corresponding string representation.  In the case of <em>iOS\/Cocoa<\/em>, the <strong>string<\/strong> type itself has a method to achieve this &#8211; <strong>integerValue<\/strong>.  In the case of <em>Android\/Java<\/em>, we use the <strong>parseInt<\/strong> method of the <em>Integer<\/em> type.<\/p>\n<p>Of perhaps more interest is what happens when we are working with the <strong>Sum<\/strong> property, to obtain the string representation of its integer value:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n  \/\/ Nougat - iOS\r\n  message(adder.Sum.stringValue)\r\n\r\n  \/\/ Cooper - Android\r\n  msg.Message := myAdder.Sum.toString;\r\n<\/pre>\n<p>Here we something very strange.  In each case, we are working with a <strong>string<\/strong> type property of a class which was not written specifically for any particular platform.  Yet in each case the mechanism for obtaining the string representation of that integer is different and each appears to be a method of the <strong>string<\/strong> type itself.<\/p>\n<p>How can the same string type have different methods ?  And if it is the same type, why can we not use the same method in each case ?<\/p>\n<p>The answer of course is that it is not the same type at all.  Both <em>Android<\/em> and <em>iOS<\/em> have types which correspond to a &#8220;String&#8221; type, and <strong>Oxygene<\/strong> maps these onto our property type.  It is a String but when required it is an <strong>NSString<\/strong> or a <strong>java.lang.String<\/strong>.<\/p>\n<p>Although the mechanism involved is different, this effect is essentially the same as the approach in <strong>Delphi<\/strong> taken with the <strong>String<\/strong> type, where even with all of the additional RTTI that makes <strong>String<\/strong> so useful in <strong>Delphi<\/strong>, it is also a pointer to a simple null terminated string (<strong>PChar<\/strong>) that can be used directly (in most cases) with the platform API &#8211; i.e. <strong>Win32<\/strong>.<\/p>\n<p>The <em>Spirit of Delphi<\/em> lives on.  Just these days, not in Delphi itself.<\/p>\n<p>Something else that is going on that is less obvious is that when compiled for <em>Android<\/em>, my <strong>Adder<\/strong> class extends the <em>Java<\/em> <strong>Object<\/strong> class.  When compiled for <em>iOS<\/em> the same class extends the <em>Cocoa<\/em> <strong>NSObject<\/strong>.<\/p>\n<p>Did someone say &#8220;native&#8221; ?  \ud83d\ude09<\/p>\n<p>At this point I should apologise to anyone at <strong>RemObjects<\/strong> if my understanding of what is going on is not accurate.<\/p>\n<p>Some people will be aghast at the implications of this.  This means after all that when you are writing the code for a business object that will be shared across different platforms you must be careful to avoid any platform specific incantations in that code.  That could be difficult when even such things as simple as converting between string and integer representations are platform specific operations.<\/p>\n<p>This is where <strong>Sugar<\/strong> comes in, and where the concept of &#8220;mapped types&#8221; in <strong>Oxygene<\/strong> comes to our aid.<\/p>\n<p>That will be the feature in my next post on <strong>Oxygene<\/strong>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">5<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> There seems to be a perception among some people that Delphi is in the unique position of allowing developers to share and re-use code across the various platforms that it&#8217;s compiler can now (and will soon) target. But this is not the case. Oxygene has had this capability right from the start.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[205,4,204,180],"tags":[153,94,137,181],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-of","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":1199,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1199\/","url_meta":{"origin":1503,"position":0},"title":"Oxygene &#8220;Nougat&#8221; is Mac and iOS !","date":"06 Sep 2012","format":false,"excerpt":"Marc Hoffman has confirmed that \"Nougat\" is to Mac\/iOS as \"Cooper\" was to Java. \u00a0Some have speculated that this will be based on Mono, but Oxygene has had Mono covered for some time already, so I strongly doubt that this is the case. Far more likely is that just as\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2416,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2416\/","url_meta":{"origin":1503,"position":1},"title":"Relight My Fire","date":"01 May 2016","format":false,"excerpt":"I'm not sure how many more song inspired Fire references I can keep coming up with, but here at least is one more. In response to my previous post Wouter commented that he had a much simpler cross platform solution to the random number problem: Which is very pithy but\u2026","rel":"","context":"In &quot;Cooper&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1624,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1624\/","url_meta":{"origin":1503,"position":2},"title":"Exploring Listeners With Oxygene","date":"16 Sep 2013","format":false,"excerpt":"Part 2 in a short series demonstrating the development of a simple camera app for Android using Oxygene. In the previous instalment we looked at the basic framework of our app. For this instalment I was going to show how to implement the camera preview or viewfinder for this instalment,\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1713,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1713\/","url_meta":{"origin":1503,"position":3},"title":"How to Call Java Code from an Oxygene Android Application","date":"20 Sep 2013","format":false,"excerpt":"Lachlan just posted a link to a post on Google+ (also available as a PDF) demonstrating how to call Java from Delphi XE5. I was shocked at both the amount and the nature of the code involved. It is long, convoluted and ugly stuff (nb. that isn't a criticism of\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2280,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2280\/","url_meta":{"origin":1503,"position":4},"title":"Oxygene Constructor Magic on Cocoa","date":"30 Aug 2014","format":false,"excerpt":"Earlier this week I mentioned that I had published my TXT-2-PARK app for Android in the Google Play Store. Today I published the iOS version to the Apple App Store (still awaiting approval at this stage). As with the Android version, I implemented the iOS version using Oxygene, and things\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1457,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1457\/","url_meta":{"origin":1503,"position":5},"title":"Learning Cocoa with Oxygene and Objective-C","date":"11 Aug 2013","format":false,"excerpt":"When XE4 came out and the full extent of the bait and switch perpetrated by Embarcadero became clear, I decided that my money was better spent with RemObjects and their Oxygene product. But it has only been this past weekend that I finally found some \"quality time\" to spend with\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-08-11-at-17.39.14-.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1503"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=1503"}],"version-history":[{"count":4,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1503\/revisions"}],"predecessor-version":[{"id":1507,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1503\/revisions\/1507"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=1503"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=1503"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=1503"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}