Reduce apk size in xamarin.forms - xamarin

I am consuming Web Api services in my app. Though it is app with small functionality such as registration and login then also the size of the release was firstly 55 MB then i searched a bit then i got it down to 22 MB.
My settings are
Linking- SDk assemblies only.
i have tried every other thing such as Deselect Shared Runtime.
Select Pro guard, Generate one package, all type of linking but did not get desired result.
Am i missing something or the app size will be 22 MB.
please suggest a solution

(Copied from : https://montemagno.com/how-to-keep-your-android-app-size-down)
Linking Your Libraries
Xamarin applications use a “linker” in order to reduce your app size.
You can browse through the documentation and find out how this works,
but to simplify things, it uses static analysis to your app to remove
assemblies and types that are not used in your app to bring down your
app size. This is for any Xamarin app, so you should also try this out
in your iOS app because it can reduce your app size in a default
“Hello, World” application from 16MB down to 2.9MB! There are three
settings that you can supply from the projects settings:
Don’t Link will do just that, it won’t link anything and you will be left with All of Mono, mscorlib, Xamarin.Android, and a bunch of
other stuff:
Link SDK assemblies only is your safest bet and should be your default as it will only attempt to strip things out of Xamarin.Android
and any of your third party libraries will not be touched. However, to
really bring down your app size you should try out Link All
Assemblies, as it will investigate everything and bring down your
app size. Be sure to FULLY test your app as it is possible that the
linker may be agressive and strip out something you need, and if that
is the case you can actually use a [Android.Runtime.Preserve] flag or
set a linkskip in your MSBuild to ensure that not all of your
libraries get linked.
So employing this practice with my Bike Now app, which uses Json.NET,
Android Support v4, v7, Google Play Services, and Xamarin.Insights, we
can compare and contrast the app size when we build our app to support
all three ABIs (we will talk about this next!).
Don’t Link: 40.7MB
Link SDK Assemblies Only: 18.7MB
Link All Assemblies: 13MB
As you can see linking correctly can make a huge impact, but we can do
even better!
2. Splitting your APKs
On Android, there are ABIs (Application Binary Interfaces) that you
can support when you ship your application. The most used will be the
armeabi-v7a, however there are still tons of devices that support and
run the old armeabi ABI and even x86 devices as well. So to ensure
your app is reaching the most users you most likely have come into the
project settings and selected every single ABI (I know I do!).
However, for every ABI that you select you are actually bundling a
separate libmonodroid and sgen with your app. Don’t believe me then
rename your .apk to .zip and take a look in the lib folder:
This of course makes sense as you would need a different version of
monodroid and sgen that supports that ABI. The issue is that you now
have all of these libraries bundle into a single APK and your users
will be downloading all of them! The solution for any Android
developer (even Java devs) is to simply split up your APKs and upload
all of them to Google Play! This way you have a smaller app size
across all three APKs. You can do this now with a simple check in your
project options:
Now, instead of just a single APK to upload I have three with
different and smaller sizes (note it will take longer to create your
packages):
armeabi-v7a: 10.2MB
armeabi: 10.3MB
x86: 10.4MB
Notes:
You may need to close XS after selecting check box and ensure this
flag is set in your csproj:
true
Additionally, your new APKs will be in your /bin/Release folder and
will be marked with Signed in their file name.
Keep your users happy and keep down that app size with these quick tips.
Refer to these links:
https://learn.microsoft.com/en-us/xamarin/android/deploy-test/building-apps/build-process
https://montemagno.com/how-to-keep-your-android-app-size-down/
https://forums.xamarin.com/discussion/113229/what-is-the-ideal-apk-size-from-xamarin-forms-and-how-to-reduce-it-without-force-close

To reduce APK file size you need to use ProGuard with visual studio. It will remove all unused code, resources and shrink files, classes, variables names to very short name.
How to use ProGuard you see my another stackverflow question How to use ProgGuard with Xamarin forms.
Another option use linking Android project's properties, use Sdk and User assembly option. It some time remove code which we are actuary using so need to be careful.

Related

Clarifying the "Bundle assemblies into native code" option for xamarin

I'm considering the option "Bundle assemblies into native code" in the properties of my xamarin android project as below:
which has the description
bundles assemblies into a native shared library. This option protects
managed assemblies from examination or tampering by embedding them in
native binaries.
This seems like a great option to me. It makes my code harder to reverse engineer, and makes my apk file smaller as well. However, clearly there are reasons that this option isn't checked by default in Visual Studio.
The three cons I can guess at are:
compile time,
ability to run a profiler (which seems like a dead loss for xamarin
anyway)
potentially that this way of operating might affect error reporting
if you're using some kind of utility like AppCenter.
I'm curious to know what the actual cons might be, and if there is a compelling reason not to have this option as the status quo?
Perhaps as the official document says,there are two limitations.
This option requires an Enterprise license
This option is only available when Use Fast Deployment is disabled
But without Fast Deployment, Xamarin.Android has to build an APK every time there’s change in your project, regardless of size or scope. This feature allows “side-loading” of .NET assemblies to enable a much better experience.

Possible to reduce the size of iOS extensions in Xamarin?

I would like to add several extensions into my iOS application but it seems that once I do, I quickly exceed the 100Mb limit that Apple places on Over-The-Air downloads.
com.apple.usernotifications.content-extension
com.apple.usernotifications.service
com.apple.ui-services
Document picker extensions
Each bare bones extension results in a 6Mb addition to my application.
Since the Xamarin runtime is presumably embedded into each instance, is there any way I can share this runtime among the various extensions?
Is this available as a mtouch compiler flag?
What other options do I have?
Right now I'm asked to add 11 Extensions, and I'm already at 70MB and I haven't even started coding or adding assets yet.
The most important rule about reducing app size for Xamarin.iOS is still valid:
Enable the managed linker for all assemblies (and all extensions).
Additionally Xamarin.iOS will automatically create a single embedded framework for the Mono runtime which will be used by the app and all extensions (you can check this by verifying that yourapp.app/Frameworks/Mono.framework exists).
Also make sure you don't set the deployment target to anything lower than 8.0 (for the main app) unless you really need it (because if you set the deployment target to anything below 8.0 we have to embed the Mono runtime into the main executable, because iOS 7 does not support embedded frameworks).
Unfortunately we're currently not able to share any of the AOT-compiled code from the base class libraries (nor your own code), so for instance mscorlib.dll (and all the AOT-compiled native code) is included both the app and every extension, but this will change in a future release (I'm implementing it right now).

install app on mac with shared libraries outside bundle

I am working on a Qt app with some library dependencies, for which I will have to make an installer.
From everything I read, seems like the best way is to make a bundle app with all library dependencies, and the required Qt frameworks, inside myapp.app/Contents/Frameworks
There are other applications created in parallel... that will get to be deployed on mac as well. They will have the same library dependencies and will be built using the same Qt version.
In that case it makes sense for the libraries and Qt to be installed OUTSIDE the bundle... so both (all) apps have access without having multiple copies of the same libraries.
Does that seem reasonable - and do-able with mac osx concept of bundle islands ? And how would I create such an installer that places libraries outside the app bundle ?
The simplest method of deploying Qt for OS X is to use the macdeployqt command line tool, and you have identified correctly that the normal method is to place the frameworks inside the app bundle, but multiple apps will each have copies of the frameworks.
It is reasonable to suggest moving the Qt frameworks to a separate, external location and linking to that instead. However, you will need to manage the framework carefully, especially when it comes to providing updates and be aware that if the framework is removed or altered, all your applications will fail to load. This, however is the same for any framework dependent application.
The thing to consider is where to place the framework. Normally, external frameworks reside in /Library/Frameworks, but if we all start to use that for Qt, problems may occur when your app is installed and another developer installs their app's frameworks with a different version of the libraries.
Apple defines various 'key directories' for applications and initially, the most likely location would appear to be the "Application Support Directory", but the documentation states that this is for: -
any type of file that supports the app but is not required for the app to run
This location is often used for support files, such as templates for the user to select.
If your application is to be deployed via the Apple Store, I wouldn't be surprised if it is rejected if you use this location. However, you're not using the Apple Store, then you could deploy the frameworks here.
If the Apple Store is your method of deployment, then /Library/Frameworks is probably the only place acceptable for the Qt framework to reside, with the possibility of the problems I've mentioned above.
Alternatively, consider just how many applications you're developing and is it really an issue to bundle the frameworks multiple times against the advantages that it brings, such as allowing the user to cleanly remove the application and all of its dependencies, as well as reducing problems of the framework being altered or removed accidentally?
If you choose to move them externally, you can refer to the answer to this question, which comprehensively explains how to make installer packages, after having updated your binary dependencies on the frameworks with the install_name_tool, as outlined here.

Adding OCHamcrest to an IOS Project

The documentation for the project says just add the framework and the linker flags and you are good to go. Hours and hours of wasted time later, I have figured out that that's not true. If you do that, the project does not see the header files. You have to put the framework somewhere were the compiler will find the headers. In my case, that worked when I dropped the framework into /Developer/Library/Frameworks and then told it to recurse in searching that framework directory (do not fiddle around with the headers search directories).
Then the problem I get is that the link fails with the message:
ld: framework not found OCHamcrestIOS
I noticed that the documentation for the project says that it was updated for Xcode 4. I pulled down the binary of the framework after checking out the code and wasting a ton of time unable to build the IOS version of the framework.
The documentation is here.
I also noticed in that documentation that the cocoa instructions tell you to put a copy files phase into the build. I tried that. Didn't change the outcome.
The last time I fell into a sink hole it was because the library was C++ code. Maybe that's still the problem.
Barring a rapid solution here, I am going to go back to using STAsserts, as sickening as that prospect is, this is far, far worse.
Update: reinstalled Xcode. Still doesn't work. There are cheap ways to make this work, like add the header files to the project. Did a blog post about this that brought out a person with the same experience.
I use a number of frameworks in my projects. Some from other people and some are mine. Looking at the documentation I would suggest that the copy phase stuff is not for iOS development. So I would not do that. I downloaded the latest zip from https://github.com/jonreid/OCHamcrest and it appears to contain a ready to go iOS static library. (Not on my mac so I cannot test to confirm).
Anyway, the way I include static libs is to
Select the project (XCode 4).
Select the target I want to add the library to.
Select the Build phases tab.
Expand Link binary with Libraries.
Click the [+] button to add a framework.
Click the [Add Other ...] button and navigate to the directory containing the <lib>.framework directory and select that.
Thats all. The targets search paths will be updated to include the framework directory and the framework will be listed on the left under the project. Expanding it will show the headers.
The problem you mention sound like a couple of things. Firstly the framework not found sounds like the framework has not been included in the target. When you select the framework in the project list on the left, you should be able to see it's Target Membership displayed on the right. Check it's on for the target you are compiling.
Secondly building frameworks is not a trivial task so don't attempt it unless you have the scripts to do it. I say this because building a iOS static framework means compiling for both simulator and devices, combining the compiled lib files into a universal one, and then storing it and the header in a specific directory strucuture.
The downloaded zip from OCHamcrest though, appears to have the correct OCHamcrestiOS.framework in it. So if you store that directory somewhere and link to it using the steps I've outlined above it should work just fine.
So the solution I adopted for now, after much thrashing around, was to include the framework in the project.
Create a group inside the Xcode project called Third Party.
Link it to a folder called thirdparty.
Go to the Add Files in Xcode and pick the framework.
Drag it over to the dependencies.
Run the tests, they pass!
This is maybe preferable anyway because referencing it in /System/Library/Frameworks would present some versioning issues, and this can be setup very quickly. Furthermore, we have a continuous integration server, and having to get in there and sync versions whenever something changes is not so great.
Thanks for checking it out, Derek.

Best way to install a custom cocoa framework

I have a custom framework that, following the advice in Apple's Framework Programming Guide >> Installing your framework I install in /Library/Frameworks. I do this by adding a Run Script build phase with the following script:
cp -R build/Debug/MyFramework.framework /Library/Frameworks
In my projects I then link against /Library/Frameworks/MyFramework and import it in my classes like so:
#import <MyFramework/MyFramework.h>
This works very well, except that I always see the following message in my debugger console:
Loading program into debugger…
sharedlibrary apply-load-rules all
warning: Unable to read symbols for "/Users/elisevanlooij/Library/Frameworks/MyFramework.framework/Versions/A/MyFramework" (file not found).
warning: Unable to read symbols from "MyFramework" (not yet mapped into memory).
Program loaded.
Apparently, the compiler first looks in /Users/elisevanlooij/Library/Frameworks, can't find MyFramework, then looks in /Library/Frameworks, does find MyFramework and continues on its merry way. So far this has been more of an annoyance than a real problem, but when runnning unit tests, gdb stops on the (file not found) and refuses to continue. I have solved the problem by adding an extra line to the Run Script Phase
cp -R build/Debug/MyFramework.framework ~/Library/Frameworks
but it feels like sello-taping something that shouldn't be broken in the first place. How can I fix this?
In the past months, I've learned a lot more about frameworks, so I'm rewriting this answer. Please note that I'm talking about installing a framework as part of the development workflow.
The preferred location for installing a public framework (i.e. a framework that will be used by more than one of your apps or bundles) is /Library/Frameworks[link text] because "frameworks in this location are discovered automatically by the compiler at compile time and the dynamic linker at runtime."[Framework Programming Guide]. The most elegant way to do this is in the Deployment section of the Build settings.
As you work on your framework, there are times when you do want to update the framework when you do a build, and times when you don't. For that reason, I change the Deployment settings only in the Release Configuration. So:
Double-click on the framework target to bring up the Target info window and switch to the Build tab.
Select Release in the Configuration selectbox.
Scroll down to the Deployment section and enter the following values:
Deployment Location = YES (click the checkbox)
Installation Build Products Location = /
Installation Directory = /Library/Frameworks
The Installation Build Products Location serves as the root of the installation. Its default value is some /tmp directory: if you don't change it to the system root, you'll never see your installed framework since it's hiding in the /tmp.
Now you can work on your framework as you like in the Debug configuration without upsetting your other projects and when you are ready to publish all you need to do is switch to Release and do a Build.
Xcode 4 Warning
Since switching to Xcode 4, I've experienced a number of problems with my custom framework. Mostly, they are linking warnings in GDB that do not really interfere with the usefulness of the framework, except when running the built-in unit-test. I have submitted a technical support ticket to Apple a week ago, and they are still looking into it. When I get a working solution I will update this answer since the question has proven quite popular (1 kViews and counting).
There's not much reason to put a framework into Library/Frameworks, and it's a lot of work: You'd need to either do it for the user in an Installer package, which is a tremendous hassle to create and maintain, or have installation code in your app (which could only install to ~/L/F, unless you expend the time and effort necessary to make your app capable of installing to /L/F with root powers).
Much more common is what Apple calls a “private framework”. You'll bundle this into your application bundle.
Even frameworks intended for general use by any applications (e.g., Sparkle, Growl) are, in practice, built to be used as private frameworks, simply because the “right” way of installing a single copy of the framework to Library/Frameworks is such a hassle.
The conventional way to do this is to have your framework project and its clients share a common build directory. Xcode will search for framework headers and link against framework binaries in the build folder first, before any other location. So an app project that compiles and links against the header will pick up the most-recently-built one, rather than whatever's installed.
You can then remove the cp -r and instead use the Install Location build setting to place your build product in the final location, using xcodebuild install DSTROOT=/ at the command line. But you'll only need to do this when you're finished, not every time you rebuild the framework.
Naturally, when you distribute your framework it should be installed in /Library/Frameworks; however it seems odd to me that you're doing that with the test/debug versions of your framework.
My first instinct would be to install test versions under ~/Library, as it just makes setting up your test and debug environment that much simpler. If possible, I would expect the debug/test framework to be located in the build tree of the version I'm testing, in which case it's installed as a Private Framework for testing purposes. That would make your life much simpler when it comes time to deal with multiple versions of your framework.
Ultimately, it doesn't matter where the framework is located as long as your application or test suite loads the correct version. Choose the location that makes testing/debugging/development easiest.

Resources