How to Reduce AIR app file size? - ios

I've uploaded an AIR-built .ipa file to Apple. From Apple: "The app binary listed below was 8 MB when you submitted it, but will be 20 MB once processed for the App Store"
So I'm trying to trim it. I've gotten rid of all unused .as classes and all unused assets. Does anyone have any clever way of reducing file size further? One way I thought of is this:
If in class A I have:
[Embed(source="assets/newButtons/purple/purpleHomeSmall.png")]
and then I re-embed it in class B with the same code, does that mean it is embedded twice with twice the file size? If so, how would I embed it in one class but have access to it in other classes?
Edit: I've discovered that if you have assets in your project that you don't use, the compiler STILL ADDS them. So you have to get rid of them before making a release build.

Instead of embedding the source why not just load it from URI at runtime?
Create an abstract bitmap class that builds a bitmap from the URI and places it on stage.
This way you can ensure the bitmap is only stored once and is loaded lazily.
Update
you can add a folder in the air for iOS settings dialog.
Place the images you want and then:
var imageLoader:Loader = new Loader();
var theURL:String = "string to your uri can be local or web";
var imageRequest = new URLRequest(theURL);
imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
imageLoader.load(imageRequest);
function onComplete(evt:Event)
{
addChild(imageLoader.content);
}

One option i have used is to compile the ipa with only the most necessary assets and then when the user first initializes the app have it download and store further assets in the application storage (this could be done with a single zip-file and the FZip class: http://codeazur.com.br/lab/fzip/)
This way the app only has to download the assets once (on first run) and they are immediately available in the future from the local system.

Instead of embedding images in each source file, what I do is create a class in the default package, where I place all my images as static public variables. Something like this:
package {
public class Icons{
[Embed(source="assets/icon1.png")] [Bindable] static public var icon1:Class;
}
}
Whenever I need the icon, I just use Icons.icon1.

You cannot ! Your app will be more than 20Mb and its like that. Because the need of Adobe AIR runtime, embedded in your IPA.
So its useless to try to optimize your code, asset .... should chase adobe for this job !
My application: prestadget is 800Ko as APK (w/o AIR runtime) but 21Mo as IPA ;-( same code

Related

How to copy from Storage to FileSystemStorage in Codenameone and display in BrowserComponent

I've been reading a lot of StackOverflow posts that discuss copying data from FileSystemStorage to Storage in CodenameOne, such as described in this answer from Shai, as seen below:
InputStream stream =
FileSystemStorage.getInstance().openInputStream(i);
OutputStream out =
Storage.getInstance().createOutputStream("MyImage");
Util.copy(stream, out);
Util.cleanup(stream);
Util.cleanup(out);`
I've been trying to do the reverse: save from Storage to FileSystemStorage in order to show a PDF in the BrowserComponent (while using iOS), but have not been able to do so. I need to show the PDF within the app (so I don't want to use Display.getInstance().execute()).
Basically, I'm trying to dynamically populate a Container with whatever files the user selects-- I am using the FileChooser library for CN1 from Steve Hannah. (Disclaimer: I have made slight modifications to this library as it used in the app I'm working on-- HOWEVER, when I choose images with this library and pull them from Storage to an Image via InputStream, they display perfectly in an ImageViewer so I know that all files are being saved correctly in Storage.)
Here is my code (with help from Steve Hannah's comment on GitHub):
//fileLocation and fileName are slightly different but both end with file extension
File file = new File(fileToUpload.getFileName());
FileSystemStorage fss = FileSystemStorage.getInstance();
InputStream is = Storage.getInstance().createInputStream(fileToUpload.getLocation());
OutputStream os = fss.openOutputStream(file.getAbsolutePath());
Util.copy(is, os);
ToastBar.Status status = ToastBar.getInstance().createStatus();
String message = file.exists() + " " + file.isFile() + file.getAbsolutePath();
status.setMessage(message);
status.setExpires(3000);
status.show();
NativeLogs.getNativeLogs();
if (Display.getInstance().getPlatformName().equals("ios")) {
//Log.p("in ios !!!!");
BrowserComponent browserComponent = new BrowserComponent();
browserComponent.setURL(file.getPath());
horizontalContainer.add(browserComponent);
}
The ToastBar displays true and true for file.exists() and file.isFile().
I stipulate iOS because as far as I've seen while researching previewing PDFs within an app, I've seen that Android needs to have a different implementation, like adding a NativeInterface with an Android library. I also saw in different answers on the Google Group that this functionality (using browserComponent to view PDFs) is only available for iOS and not on the simulator. In the simulator, I see a blank space. My iPhone just freezes and/or crashes after displaying the ToastBar (and I work on a Windows machine, so not much ability to see native logs....)
What can I do to access the file and show it in the BrowserComponent?
Thank you!
Simple solution -- the file had a space in it (eg. "Test page.pdf") and didn't show! When I used files that didn't have spaces this worked and after removing spaces in the file names, thankfully everything worked. I'll have to add code to handle this scenario.
Thanks for your help!

How to set path to picture in folder in device in android appcelerator app

So I have app where I want to let users to share screenshot of score to facebook etc... I'm using SocialShare widget. In documentation it says to set path to image like this: "image:fileToShare.nativePath", but I'm not really sure how to set it. Another problem is that I need to share picture that has always different name, it saves screenshots with names like tia7828157.png,tia107997596.png... in folder in device internal memory in pictures/enigmania/ I'm new to appcelerator, so I dont know if there is something like wildcard I could use for this? Thanks for any help.
This is my code so far which I know is wrong, I know the widget works because it shares text without problem:
function shareTextWidget(e){
// share text status
var socialWidget=Alloy.createWidget('com.alcoapps.socialshare');
socialWidget.share({status:"Enigmania kvíz",androidDialogTitle:"hoho",image:test.png/pictures/enigmania});
}
You should use Ti.Filesystem class methods/properties to get the path of any file located on internal or external storage.
Also aware of the permissions of reading storage on Android 6+. Use Storage Permissions before accessing any file on Android 6+.
Simple code snippet to create a directory on internal storage at this location: pictures/enigmania and then write an image file of captured view in this directory.
function shareTextWidget(e){
var directory = Ti.Filesystem.getFile(Ti.Filesystem.externalStorageDirectory, 'pictures/enigmania');
!directory.exists() && directory.createDirectory();
var fileToShare = Ti.Filesystem.getFile(directory.resolve(), 'screen.jpg');
fileToShare.write($.SCREENSHOT_VIEW.toImage()); // write the blob image to created file
var socialWidget=Alloy.createWidget('com.alcoapps.socialshare');
socialWidget.share({status:"Enigmania kvíz",androidDialogTitle:"hoho",image:fileToShare.nativePath});
}
This code should work without any issues.
Note that $.SCREENSHOT_VIEW is the View ID for which you will take screenshot, so it depends on you how you maintain your View positions in order to capture correct screenshot, but point is to use Ti.UI.View toImage() method to capture the screenshot of particular view.
Let me know if this works for you or not, else we can look into other methods by getting your exact requirements. Good Luck!!!!

Adobe air on iOS - FileReference Download Error

When I use the Download method of the FileReference class, everything works fine on Desktop and Android, but I get an error on iOS.
This is the code:
var req = new URLRequest(url);
var localRef:FileReference = new FileReference();
localRef.download(req);
On iOS I'm getting an Alert:
Download Error
File downloads not supported.
I already tried to NavigateToUrl() and it asks save the file in Dropbox or another App.
How can I fix this error?
You shouldn't use FileReference on mobile (or AIR, in general, though it does open the Load/Save dialog on desktop so there can be some use there). You should instead use File and FileStream, which give you far more control over the file system.
In this case, you could try to use File.download() and save it to File.applicationStorageDirectory, but I don't know if it will have any difference since it extends FileReference.
What I generally do is use URLStream instead of URLLoader. It gives you access to the raw bytes of the file you are downloading and then use File and FileStream
So something like (and this is untested off the top of my head, though I have used similar in the past):
var urlStream:URLStream = new URLStream();
urlStream.addEventListener(Event.COMPLETE, completeHandler);
urlStream.load(new URLLoader('url');
function completeHandler(e:Event):void {
var bytes:ByteArray = new ByteArray();
urlStream.readBytes(bytes);
var f:File = File.applicationStorageDirectory.resolvePath('filename');
var fs:FileStream = new FileStream();
fs.open(f, FileMode.WRITE);
fs.writeBytes(bytes);
fs.close();
}
Now, obviously, there is a lot more you want to account for (errors, progress, etc). That should be enough to point you in the right direction, however.
It's possible to create a full download manager using this method (something I did for an iOS project two years ago), since you can save as-you-go to the file system rather than waiting until Event.COMPLETE fires (using the ProgressEvent.PROGRESS event). That allows you to avoid having a 500MB file in memory, something most devices can't handle.

Xamarin iOS swap image in bundle at runtime

I want to allow my app to download images from the web to be placed in /images and then displayed in the UI - this is to allow branding specific images to be downloaded for a multitenant app.
The app downloads the image fine and places the image into the /Images folder
However when trying to load the image using UIImage.FromFile it always returns null - even though I know full well the image is there!
I know UIImage.FromBundle caches stuff so I chose to use FromFile but this doesn't seem to work?!
Is there something I need to do to make this work? Or am I pushing my luck a little?
I am using the following to create the file path:
public static UIImage SafeClaim_Logo = UIImage.FromFile(NSBundle.MainBundle.BundlePath +"/Images/Logo.png");
The following is the code I use to download and save the file - the file path from the above is the same below when I inspect it
HttpClient client = new HttpClient ();
var clientResponse = await client.GetByteArrayAsync(url);
using (FileStream fs = new FileStream(NSBundle.MainBundle.BundlePath + path, FileMode.OpenOrCreate))
{
var bytes = clientResponse;
fs.Write(bytes, 0, bytes.Length);
}
The app bundle is read-only so you can't change or add something in there at runtime.
All content created at runtime should be stored in your documents or cache folders.
You can get the documents folder by calling:
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
One good place to store this kind of files is the Library/ folder. On iOS 7 you can access it using:
var libraryFolderPath = Path.GetFullPath(
Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.MyDocuments), "..", "Library"));
On iOS 8:
var url = NSFileManager.DefaultManager.GetUrls (
NSSearchPathDirectory.LibraryDirectory, NSSearchPathDomain.User) [0];
var libraryFolderPath = url.Path;
The reason is explained by Xamarin documentation:
The Library directory is a good place to store files that are not
created directly by the user, such as databases or other
application-generated files. The contents of this directory are never
exposed to the user via iTunes.
You can create your own subdirectories in Library; however, there are
already some system-created directories here that you should be aware
of, including Preferences and Caches.
The contents of this directory (except for the Caches subdirectory)
are backed up by iTunes. Custom directories that you create in Library
will be backed up.
If you need to enable iTunes file sharing and expose the content of the Documents/ folder, your files are not visible to the user.

Adobe AIR 3.8 Flash Builder 4.6 for iOS - Secondary SWF with Actionscript works on fast packaging but fails on Releast Build

I have a simple Flex Mobile project that just allows user to swap between 2 different .swf games. I load the .swf with the following code:
private function loadFile(f:String):void{
var _urlRequest:URLRequest = new URLRequest(f);
var _loader:Loader = new Loader();
var _lc:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain, null);
_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onSWFLoaded);
_loader.load(_urlRequest, _lc);
txt.text="loading";
// add loader to container
grp.addChild(_loader);
}
private function onSWFLoaded(e:Event):void {
// status text to show it loaded
txt.text="loaded!";
}
Then on user button click I just do:
loadFile("file1.swf");
file1.swf is packaged into the build. I run this on debug under fast packaging on my iPod Touch and everything works like a charm, but when I do an export release build, my status text still says "loaded", but the swf loads very strangely - as if there's some code that is failing to run properly.
Since it works on fast packaging with no issue, what would be the difference between release build and fast build that can cause code in child swfs to fail?
Any ideas would be appreciated.
Thanks.
Solved the issues based on comments from here:
http://forums.adobe.com/message/5398137
Using var file:File = File.applicationDirectory.resolvePath("file1.swf"); and adding the Flex Compiler option "swf-version=19" fixed the issue for me. Not sure which one of the two changes fixed it but it all works now.

Resources