{"id":2280,"date":"2014-08-30T20:21:02","date_gmt":"2014-08-30T08:21:02","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=2280"},"modified":"2014-08-30T20:21:02","modified_gmt":"2014-08-30T08:21:02","slug":"oxygene-constructor-magic-on-cocoa","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/2280\/","title":{"rendered":"Oxygene Constructor Magic on Cocoa"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">6<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>Earlier this week I mentioned that I had published my <strong>TXT-2-PARK<\/strong> app for <strong>Android<\/strong> in the <strong>Google Play Store<\/strong>.  Today I published the <strong>iOS<\/strong> version to the <strong>Apple App Store<\/strong> (still awaiting approval at this stage).  As with the <strong>Android<\/strong> version, I implemented the <strong>iOS<\/strong> version using <strong>Oxygene<\/strong>, and things proved a little less straightforward.<\/p>\n<p><!--more--><\/p>\n<p>First of all, there is a <strong>key<\/strong> platform difference that directly affected the implementation of my app.<\/p>\n<p>On <strong>Android<\/strong> (and I believe <strong>Windows Phone<\/strong>, which I have already started on) it is perfectly possible for an application to send an SMS without any involvement from the user.  This however is <em>not<\/em> the case on <strong>iOS<\/strong>.  There are ways to achieve it through private API&#8217;s but these require a jailbroken device.  If you want to publish your app in the <strong>App Store<\/strong> then there is no way around it.<\/p>\n<p>The best you can hope to achieve on <strong>iOS<\/strong> is to pre-fill an SMS message form with the user then required to hit <strong>Send<\/strong> to confirm that the message should be sent.<\/p>\n<p>To achieve this is very straightforward:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n    var sms := new MFMessageComposeViewController;\r\n\r\n    sms.messageComposeDelegate := self;\r\n    sms.recipients  := ['7275'];\r\n    sms.body        := DigitStrip.Code;\r\n\r\n    presentViewController(sms) animated(true) completion(nil); \r\n<\/pre>\n<p>We instantiate an <strong>MSMessageComposeViewController<\/strong>, set the recipients (an array of strings) and set the message body.  We also assign a delegate to handle the completion callback.  We then present the view controller (the built-in SMS composer view).<\/p>\n<p>Instantiation of the view controller is made more straightforward in <strong>Oxygene<\/strong>, thanks to the support for constructor style instantiation using the <strong>new<\/strong> statement.  The first line in the above code is equivalent to the <strong>Objective-C<\/strong>:<\/p>\n<pre class=\"brush: objc; title: ; notranslate\" title=\"\">\r\n  MFMessageComposeViewController sms = [[MFMessageComposeViewController alloc] init];\r\n<\/pre>\n<p>This allocates an instance via the static class method <strong>alloc<\/strong>, and then calls the default initializer, <strong>init<\/strong>, on the resulting instance.<\/p>\n<p>Similarly, the assignment of an array of strings for the recipients is handled for us by the fact that the <strong>Oxygene<\/strong> compiler transparently maps the syntax for an explicit array onto the appropriate code for instantiating a platform appropriate array class and initializing it.<\/p>\n<p>So this simple and intuitive assignment:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n    sms.recipients  := ['7275'];\r\n<\/pre>\n<p>Which corresponds directly to the far more cumbersome <strong>Objective-C<\/strong> code:<\/p>\n<pre class=\"brush: objc; title: ; notranslate\" title=\"\">\r\n  sms.recipients = [NSArray arrayWithObjects:@&quot;7275&quot;, nil];\r\n<\/pre>\n<p>The end result is that the user is presented with the standard iOS SMS message composition UI, with the recipient (7275 \/ PARK) and payment code that they entered already filled in.  They then simply have to confirm that the message should be sent, or cancel it.  Whatever they choose, once they have done this the delegate method is then called in which we can determine what happened.<\/p>\n<p>Declaring that a class implements a delegate protocol is syntactically identical to implementing an interface (for those more familiar with that in <strong>Delphi<\/strong>):<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n   RootViewController = class(UIViewController, IMFMessageComposeViewControllerDelegate)\r\n     ..\r\n     method messageComposeViewController(aController: MFMessageComposeViewController) didFinishWithResult(aResult: MessageComposeResult);\r\n   end;\r\n<\/pre>\n<p>In this instance, the protocol involved has only one method, which we implement to handle the fact that the user has dismissed the SMS view controller, either sending the message or cancelling it.  This demonstrates the support for multi-part method names in <strong>Oxygene<\/strong> (and <strong>RO C#<\/strong>) compilers.  Multi-part method names take a bit of getting used to, but I for one have found myself <em>really<\/em> enjoying the clarity and expressiveness they bring.<\/p>\n<p>The feel very &#8220;Pascally&#8217; somehow.  To me at least.  \ud83d\ude42<\/p>\n<p>Of course, not being content with simply supporting multi-part methods names for <strong>Cocoa<\/strong>, as of the 7.x compilers, <strong>RemObjects<\/strong> provide this support on both <strong>Java<\/strong> and <strong>.NET<\/strong> platforms as well (though with some limitations on .NET, namely that such methods are not consumable by other .NET languages other than those RO compiler).<\/p>\n<p>The implementation of the protocol method is equally straightforward:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n    method RootViewController.messageComposeViewController(aController: MFMessageComposeViewController)\r\n                                       didFinishWithResult(aResult: MessageComposeResult);\r\n    begin\r\n      if (aResult = MessageComposeResult.MessageComposeResultFailed) then\r\n      begin\r\n        var alert := new UIAlertView withTitle('Unexpected Error') message('The SMS message could not be sent.')\r\n                                                                   &amp;delegate(nil) \r\n                                                                   cancelButtonTitle('OK')\r\n                                                                   otherButtonTitles(nil);\r\n        alert.show;\r\n      end\r\n      else\r\n        dismissViewControllerAnimated(true) completion(nil);\r\n    end;\r\n<\/pre>\n<p>If the result of the operation was a failure (the user confirmed that the message should be sent, but something went wrong) then we report this to the user via a <strong>UIAlertView<\/strong> (pop-up message).<\/p>\n<p>The instantiation of the <strong>UIAlertView<\/strong> used to present this message demonstrates yet more of the syntactic sugar that <strong>Oxygene<\/strong> liberally dusts over the <strong>Objective-C<\/strong> API&#8217;s, particularly w.r.t constructors.<\/p>\n<p>In this case, I am not invoking the default initializer (init), but rather one that takes several parameters.  In <strong>Objective-C<\/strong> this would be:<\/p>\n<pre class=\"brush: objc; title: ; notranslate\" title=\"\">\r\n    UIAlertView alert = [[UIAlertView alloc] initWithTitle: .. message: ... ...etc... ];\r\n<\/pre>\n<p>In <strong>Oxygene<\/strong> the <strong>alloc<\/strong> static method call is replace by the <strong>new<\/strong> statement, as before, and on this occasion the specialised initializer is invoked by the special &#8220;<strong>dot-less<\/strong>&#8221; invocation and without the &#8220;<em>init<\/em>&#8221; prefix on the initializer method name:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n    var alert := new UIAlertView withTitle(...) ... etc;\r\n<\/pre>\n<p>The code completion offered by <strong>Oxygene<\/strong> in <strong>Visual Studio<\/strong> ensures that correct initializer methods are shown in this context.  If you find yourself presented with an &#8220;initXXX&#8221; method in a statement involving <strong>new<\/strong>, then you have made a mistake and will end up double-initializing the new instance (which will result in a runtime exception).<\/p>\n<p>As a contrived example of how not to do things:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n   var alert := new UIAlertView().initWithTitle(..) .... ;\r\n<\/pre>\n<p>Without the parentheses after the class name, code completion will <strong>not<\/strong> offer the initializer methods for code completion <em>at all<\/em>, neither with the <strong>init<\/strong> prefix nor without.  If you add these parentheses then you effectively complete the initialization of the new instance, using the <strong>default<\/strong> initializer.  Since initializers are instance methods, code completion will now offer these initializers, complete with their <strong>init<\/strong> prefixes since you are no longer in the special case &#8220;constructor&#8221; mode that would have these removed.<\/p>\n<p>Of course, if you prefer you can also use the more explicit alloc\/init combination directly, even in Oxygene, in which case <strong>new<\/strong> is not involved at all and you invoke the initializers using their full name:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n    var alert := UIAlertView.alloc.initWithTitle(..) .... ;\r\n<\/pre>\n<h2>Tidying up<\/h2>\n<p>So that&#8217;s if there is a problem.  But if the operation was successful or cancelled, we simply dismiss the view controller.  At this point the current view is the application view, since the message compose view has already been dismissed, so the user is returned to the home screen.<\/p>\n<p>As I understand it, Apple take a dim view of an application forcibly exiting under any circumstances by any other means, even if there is no point in the application hanging around further.  Though at least in this case my app is just about as frugal as it is possible to be, w.r.t consuming system resources.  \ud83d\ude42<\/p>\n<h2>Vive La Difference!<\/h2>\n<p>As a result of the imposed SMS composer view on iOS, this means that there is a further, subtle difference between the iOS and <strong>Android<\/strong> versions of the app, one that I chose to introduce.<\/p>\n<p>On <strong>Android<\/strong>, since the app is able to send the SMS directly without user intervention, the app exits immediately that the SMS has been sent.  When confirmation that the SMS was sent by the device, a popup toast message confirms this for the user.  If there is an error, then the popup toast message tells them of that too.<\/p>\n<p>Worth bearing in mind of course is that the user knows full well that my application sends SMS messages.  Not only is it the entire <em>raison d&#8217;etre<\/em> of the application, but &#8220;Sends SMS&#8221; is the one and <strong>ONLY<\/strong> permission that the app requires when it is installed !<\/p>\n<p>In the <strong>iOS<\/strong> app, since the user has <em>already<\/em> had to effectively confirm the sending of the message, I don&#8217;t bother distracting them with a further confirmation of the <em>successful<\/em> sending of the message, only of any <em>problem<\/em> that prevented it from being sent.<\/p>\n<p>Currently this means that if the <strong>Android<\/strong> app experiences a problem sending the TXT then the user will have to re-launch the app and re-enter the code to try again.<\/p>\n<p>But there is another significant difference between the <strong>Android<\/strong> and <strong>iOS<\/strong> platforms which means I don&#8217;t consider this a problem (and actively chose this approach).<\/p>\n<h2>Lean and Agile<\/h2>\n<p>On <strong>Android<\/strong>, the small size of the app and perhaps differences in the way apps are launched, means that the app launches all but instantaneously, so on the probably rare occasions that there are problems sending an SMS, this shouldn&#8217;t be a problem (of course, I can revisit this approach if I get complaints or simply decide to make the change anyway).<\/p>\n<p>On <strong>iOS<\/strong>, the application is significantly larger (<strong>1.9 MB on iOS<\/strong> vs <strong>22 KB on Android<\/strong>) and the <strong>iOS<\/strong> launcher seems to work in a quite different way, presenting a launch screen (which I am sure is subject to a system imposed delay to allow the user to appreciate it!) before presenting the application itself.<\/p>\n<p>Frustratingly, the launch screen also appears to be the mechanism by which iOS determines the screen sizes supported by your app and having this reported correctly to my app is crucial to the scaling of my UI on different devices, so it seems I have to provide launch screens for each device, even if I suspect this is contributing to the startup-time of my app.<\/p>\n<p>Hey ho.  (and if anyone knows any different, I&#8217;d love to hear from you!)<\/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\">6<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> 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 proved a little less straightforward.<\/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":[212,205,4,213,204,180],"tags":[153,183,281,137,282,181,280],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-AM","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":2285,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2285\/","url_meta":{"origin":2280,"position":0},"title":"While I Was Waiting For Apple&#8230;","date":"01 Sep 2014","format":false,"excerpt":"So Saturday morning I finished the iOS version of my application and published it to the App Store. Of all of the app stores, the approval process with Apple evidently takes the longest. So, whilst waiting for my app to be released, I developed (and published) the Windows Phone version\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1199,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1199\/","url_meta":{"origin":2280,"position":1},"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":1845,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1845\/","url_meta":{"origin":2280,"position":2},"title":"Another Great Oxygene Release","date":"03 Oct 2013","format":false,"excerpt":"RemObjects have officially released the September update to Oxygene with the usual round of bug fixes and some dramatic improvements in the tool chain. Cocoa - Principally iOS 7 My focus with Oxygene most recently has been on the Android side. My previous experience with using the Cocoa (iOS) support\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1713,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1713\/","url_meta":{"origin":2280,"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":1503,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1503\/","url_meta":{"origin":2280,"position":4},"title":"Sharing Code Across Platforms in Oxygene","date":"22 Aug 2013","format":false,"excerpt":"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's compiler can now (and will soon) target. But this is not the case. Oxygene has had this capability right from\u2026","rel":"","context":"In &quot;Cooper&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2252,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2252\/","url_meta":{"origin":2280,"position":5},"title":"Making a Noise About on a Thread","date":"13 Aug 2014","format":false,"excerpt":"I'm working on an Android app at the moment, and for a bit of fun I decided to add a startup sound to brighten the day of every user that launches it. Which gives me another opportunity to present some of the advanced language features in Oxygene that make threading\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Audio-Resource.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\/2280"}],"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=2280"}],"version-history":[{"count":4,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2280\/revisions"}],"predecessor-version":[{"id":2284,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2280\/revisions\/2284"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=2280"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=2280"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=2280"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}