{"id":400,"date":"2014-02-26T10:57:55","date_gmt":"2014-02-26T14:57:55","guid":{"rendered":"http:\/\/www.eqsim.com\/blog\/?p=400"},"modified":"2014-07-01T22:14:38","modified_gmt":"2014-07-02T02:14:38","slug":"how-i-solved-reloading-codeless-wo-abc-swf-files-on-ios-with-adobe-air-4-0","status":"publish","type":"post","link":"http:\/\/www.eqsim.com\/blog\/how-i-solved-reloading-codeless-wo-abc-swf-files-on-ios-with-adobe-air-4-0\/","title":{"rendered":"How I solved reloading codeless (w\/o ABC) SWF files on iOS with Adobe AIR 4.0"},"content":{"rendered":"<p>I have been working on an aviation training app with a great team at <a href=\"http:\/\/www.pegasusinteractive.com\/\">Pegasus Interactive<\/a>. The app\u00a0was originally done in Flash AS 2.0 using <a href=\"https:\/\/code.google.com\/p\/eqsim-state-machine-and-interface-components\/\">my state engine<\/a>, then converted to work in AS 3.0 on iOS and AIR. One thing it does a lot of is load SWF files dynamically. We managed to get the app into the App Store in the middle of last year, while using AIR 3.5. We stayed with AIR 3.5 because AIR 3.6 introduced a restriction, namely that SWF files, even codeless ones (without ABC, or &#8220;ActionScript Byte Code&#8221;), no longer could be reloaded. Seems like this issue has been floating around <a href=\"http:\/\/stackoverflow.com\/questions\/19536000\/load-and-reload-external-swf-in-air-for-ios\">since the latter part of 2013<\/a>. For future updates, I was nervous, because caching SWF files would consume memory fast, and otherwise we would have to do a significant re-architecting of the app, such as allowing e<a href=\"https:\/\/blogs.adobe.com\/airodynamics\/2013\/03\/08\/external-hosting-of-secondary-swfs-for-air-apps-on-ios\/\">xternal loading SWF files<\/a> attached to classes. So I went the easy route, waiting for 3.7 in hopes Adobe would solve the problem.<\/p>\n<p>I saw a glimmer of hope when someone said it was fixed in AIR 3.7+, but that hope was shortlived. I had to face a more severe re-architecting future. The issue finally came to a head earlier this month, when Apple <a href=\"https:\/\/developer.apple.com\/news\/index.php?id=12172013a#top\">stopped accepting apps and updates that used the older SDK&#8217;s<\/a>. Whoops, we were going to have to get through this, now.<\/p>\n<p>This post describes how we have successfully gotten to reload SWF&#8217;s (without code) on AIR 4.0, SWF&#8217;s we were downloading and SWF&#8217;s we had included in our initial distribution. In a nutshell, we needed two methods. The first method was for SWF&#8217;s we downloaded into the cache &#8212; essentially we got a file reference to it, and the reloading worked. Getting the file reference did not work when we tried to reload a SWF that we had packaged with the application&#8211;the second method added a URL variable to the loading, which then worked. I worked with Brandon Krakowsky on this, who helped a lot. I hope it can help someone struggling with this problem, perhaps seeing all the changes for 4.0 we needed to do!<\/p>\n<p>Important: I have not submitted the app to Apple yet, so there is a chance they will reject it for some reason. If they do, I will update this post with that information. [Update: the app was accepted without changes based on what I describe here. Yay!]<\/p>\n<p><!--more--><\/p>\n<p><span style=\"line-height: 1.5em;\">Here are the issues we addressed:<\/span><\/p>\n<ol>\n<li>Getting a SWF to Load the First Time: Getting SWF&#8217;s to load at all, i.e., addressing changes that were made somewhere between AIR 3.5 to 4.0;<\/li>\n<li>Reloading SWF&#8217;s: Loading and reloading SWF files that were downloaded from the Internet; and,<\/li>\n<li>Status Bar Issue: Adjusting the status bar (top 20 pixels) text color on the screen &#8212; it was showing black, but our background was also black.<\/li>\n<\/ol>\n<h3>Getting a SWF to Load the First Time<\/h3>\n<p>The first time Brandon and I moved to AIR 4.0 and tried to load the first SWF (from the app:\/ area, since it was packaged with the app), the SWF file did not load at all &#8212; it got an error about multiple application domains. We looked online to find <a href=\"http:\/\/blogs.adobe.com\/airodynamics\/2012\/11\/09\/packaging-and-loading-multiple-swfs-in-air-apps-on-ios\/\">this article about it<\/a>, and solved the problem by adding the LoaderContext:<\/p>\n<pre>request = new URLRequest(path);\r\nrequest.cacheResponse = false;\r\nrequest.useCache = false;\r\n_lc = new LoaderContext(false, ApplicationDomain.currentDomain, null);\r\nmoduleLoader.load(request, _lc);<\/pre>\n<p>You&#8217;ll also see here that we added\u00a0cacheResponse and useCache options to URLRequest &#8212; I don&#8217;t know if this had an effect, but it seemed safest to use it.<\/p>\n<p>After we had this, then at least we could load a SWF file (without ActionScript Byte Code, I remind you) &#8212; once.<\/p>\n<h3>Reloading SWF&#8217;s<\/h3>\n<p>We were first trying to load the SWF file that happened to be packaged with the app, in a folder called &#8220;modules&#8221;. Using the typical trick for loading web pages without caching, we appended a variable to the URL, which worked:<\/p>\n<pre>path = moduleDirectoryPath + module_folder + \"\/\" + module + \"?nf=\"+getTimer();<\/pre>\n<p>Then using the moduleLoader chunk of code above, it worked. If we did not have the variable at the end, we could load the SWF once, but the second time it gave us a &#8220;cannot reload SWF&#8221; error. Oddly enough, this happened with one of two of our SWF files &#8212; with one SWF file, it allowed us to reload it even without the appended variable. We never found out why.<\/p>\n<p>After our initial success, we tried this with our externally loaded content, which we had of two types: (a) content downloaded into the cacheDirectory (hence a file pointer); and (b) content streamed from the Internet (http). We got an error trying the variable trick with our file pointer, because it could not find the file when it had the appended variable, like &#8220;file:\/\/filename.swf?nf=3322&#8221;. We were a bit stuck.<\/p>\n<p>So on a lark, I removed the appended URL variable, and voila, the SWF loaded (and more importantly, reloaded). It also reloaded for our streamed content (item b, described in the last paragraph). I would prefer not to append the variable on the URL, to have a single approach for loading files, so I tried to get the content from the application folder into a &#8220;file&#8221; type of path to load, trying something like new File(File.applicationDirectory &#8230;) and the like. I got the correct file finally, but in the end, it gave me the reloading error. Very odd, so loading from applicationDirectory was different from loading from cacheDirectory, as far as I could tell.<\/p>\n<p>So I took a step back and applied the dual approach:<\/p>\n<pre>if (loadFromApp) {\r\n   path = moduleDirectoryPath + module_folder + \"\/\" + module + \"?nf=\"+getTimer();\r\n} else {\r\n   path = moduleDirectoryPath + module_folder + \"\/\" + module;\r\n}<\/pre>\n<p>In other words, if I was loading the SWF from the applications\/modules folder, I added the URL variable. If I was loading the SWF from the cache, I did not use it. So the path request to the loader was something like &#8220;\/modules\/&#8230;&#8221; if it was from the application folder (app:\/), but it was &#8220;file:\/\/&#8230;&#8221; if it was from the cache). Here is the code to initially set the path:<\/p>\n<pre>var moduleDirectoryPath = \"\/modules\/\";\r\n\r\nif (externalContent == FROM_CACHE) {\r\n   moduleDirectoryPath = File.cacheDirectory.url + moduleDirectoryPath;\r\n} else if (externalContent == FROM_WEB) {\r\n   moduleDirectoryPath = \"http:\/\/our-online-content.com\" + moduleDirectoryPath;\r\n}<\/pre>\n<p>and then it all worked fine!<\/p>\n<h3>Status Bar Issue<\/h3>\n<p>The last piece of the puzzle was handling the problem of the status bar text color &#8212; by default, it was set to black, but the background was black as well. Our app is not full-screen, so the status bar is showing. I needed to make the text color white (in addition to moving my graphics down about 20 pixels).<\/p>\n<p>This change I made in the manifest file. However, we are using Flash CS6 to produce the ipa, and whenever I saved the iOS Settings, it would overwrite the manifest file! So my manual changes to the manifest file, to add the appropriate lines, kept getting overwritten. The final result (we&#8217;ll skip the pain), was to have my iPhone section as follows (note that our app is just for the iPad, hence the UIDeviceFamily setting):<\/p>\n<pre>&lt;iPhone&gt;\r\n  &lt;requestedDisplayResolution&gt;standard&lt;\/requestedDisplayResolution&gt;\r\n  &lt;InfoAdditions&gt;\r\n    &lt;![CDATA[&lt;key&gt;UIViewControllerBasedStatusBarAppearance&lt;\/key&gt;&lt;false\/&gt;&lt;key&gt;UIStatusBarStyle&lt;\/key&gt;&lt;string&gt;UIStatusBarStyleLightContent&lt;\/string&gt;&lt;key&gt;UIDeviceFamily&lt;\/key&gt;&lt;array&gt;&lt;string&gt;2&lt;\/string&gt;&lt;\/array&gt;]]&gt;\r\n  &lt;\/InfoAdditions&gt;\r\n&lt;\/iPhone&gt;<\/pre>\n<p><span style=\"line-height: 1.5em;\">The key was adding values for UIViewControllerBasedStatusBarAppearance and UIStatusBarStyle. However, when I tried to add them manually to the manifest, and then updating the iOS Settings in Flash CS6, the results came out wrong&#8211;Flash would absorb some of the keys and values, but not all. My solution from trial and error was to enter the following manually into the xml file:<\/span><\/p>\n<pre>&lt;iPhone&gt;\r\n  &lt;requestedDisplayResolution&gt;standard&lt;\/requestedDisplayResolution&gt;\r\n  &lt;InfoAdditions&gt;\r\n    &lt;![CDATA[&lt;key&gt;UIViewControllerBasedStatusBarAppearance&lt;\/key&gt;&lt;false\/&gt;]]&gt;\r\n    &lt;![CDATA[&lt;key&gt;UIStatusBarStyle&lt;\/key&gt;&lt;string&gt;UIStatusBarStyleLightContent&lt;\/string&gt;]]&gt;\r\n    &lt;![CDATA[&lt;key&gt;UIDeviceFamily&lt;\/key&gt;&lt;array&gt;&lt;string&gt;2&lt;\/string&gt;&lt;\/array&gt;]]&gt;\r\n  &lt;\/InfoAdditions&gt;\r\n&lt;\/iPhone&gt;<\/pre>\n<p>Then Flash collapsed the CDATA&#8217;s when it wrote the file (after I saved the fla&#8217;s iOS Settings), into the final result (copied here):<\/p>\n<pre>&lt;iPhone&gt;\r\n  &lt;requestedDisplayResolution&gt;standard&lt;\/requestedDisplayResolution&gt;\r\n  &lt;InfoAdditions&gt;\r\n    &lt;![CDATA[&lt;key&gt;UIViewControllerBasedStatusBarAppearance&lt;\/key&gt;&lt;false\/&gt;&lt;key&gt;UIStatusBarStyle&lt;\/key&gt;&lt;string&gt;UIStatusBarStyleLightContent&lt;\/string&gt;&lt;key&gt;UIDeviceFamily&lt;\/key&gt;&lt;array&gt;&lt;string&gt;2&lt;\/string&gt;&lt;\/array&gt;]]&gt;\r\n  &lt;\/InfoAdditions&gt;\r\n&lt;\/iPhone&gt;<\/pre>\n<p>Quite an adventure, to say the least!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I have been working on an aviation training app with a great team at Pegasus Interactive. The app\u00a0was originally done in Flash AS 2.0 using my state engine, then converted to work in AS 3.0 on iOS and AIR. One thing it does a lot of is load SWF files dynamically. We managed to get\u2026 <span class=\"read-more\"><a href=\"http:\/\/www.eqsim.com\/blog\/how-i-solved-reloading-codeless-wo-abc-swf-files-on-ios-with-adobe-air-4-0\/\">Read More &raquo;<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[41,17,42],"tags":[],"_links":{"self":[{"href":"http:\/\/www.eqsim.com\/blog\/wp-json\/wp\/v2\/posts\/400"}],"collection":[{"href":"http:\/\/www.eqsim.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.eqsim.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.eqsim.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.eqsim.com\/blog\/wp-json\/wp\/v2\/comments?post=400"}],"version-history":[{"count":12,"href":"http:\/\/www.eqsim.com\/blog\/wp-json\/wp\/v2\/posts\/400\/revisions"}],"predecessor-version":[{"id":446,"href":"http:\/\/www.eqsim.com\/blog\/wp-json\/wp\/v2\/posts\/400\/revisions\/446"}],"wp:attachment":[{"href":"http:\/\/www.eqsim.com\/blog\/wp-json\/wp\/v2\/media?parent=400"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.eqsim.com\/blog\/wp-json\/wp\/v2\/categories?post=400"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.eqsim.com\/blog\/wp-json\/wp\/v2\/tags?post=400"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}