TobiasBuchholz / Plugin.Firebase

Wrapper around the native Android and iOS Firebase Xamarin SDKs
MIT License
211 stars 49 forks source link

App size increases a lot when using Plugin.Firebase #115

Closed tranb3r closed 1 year ago

tranb3r commented 1 year ago

When I add a package reference to Plugin.Firebase, my app size increases a lot. It looks like the size increase is mainly due to Facebook assemblies being added to the apk. However, I'm not using Facebook at all. Is there any solution to strip the code and assemblies that are not used by the app? Or maybe there is a way to make Facebook functionalities optional? Thanks

tranb3r commented 1 year ago

I forgot to mention that dotnet app trimming is enabled by default when building in release. But the trimmer seems to consider that Facebook assemblies should not be removed.

tranb3r commented 1 year ago

Seems related to this question : https://github.com/TobiasBuchholz/Plugin.Firebase/issues/95#issuecomment-1384913266

TobiasBuchholz commented 1 year ago

Since the linker doesn't seem to remove the facebook dependencies from the code when it's not used at all, I'm considering to remove the facebook package alltogether. Furthermore I would make the method SignInWithCredentialAsync(AuthCredential credential) public, so anybody who still wants to use Sign-in via facebook can readd the facebook package and sign in like this:

public async Task<IFirebaseUser> SignInWithFacebookAsync()
{
    try {
        var credential = await _facebookAuth.GetCredentialAsync(ViewController);
        return await CrossFirebaseAuth.Current.SignInWithCredentialAsync(credential);
    } catch(NSErrorException e) {
       // handle exception
    }
}

What do you guys think about that?

tranb3r commented 1 year ago

I've done a simple test: add Xamarin.Facebook.Android to a brand new maui app. The linker does not remove the facebook libraries. So it confirms the issue is not caused by the Plugin.Firebase implementation of Facebook auth. I'll open an issue in the FacebookComponents repo, but considering the lack of activity in this repo, I don't think it's going to be fixed ever.

The solution you propose is a bit radical, but it's totally fine with me (I'm not using Facebook anyway). Maybe you could put the Facebook code (and dependencies) in a separate nuget?

tranb3r commented 1 year ago

https://github.com/xamarin/FacebookComponents/issues/248

vhugogarcia commented 1 year ago

Since the linker doesn't seem to remove the facebook dependencies from the code when it's not used at all, I'm considering to remove the facebook package alltogether. Furthermore I would make the method SignInWithCredentialAsync(AuthCredential credential) public, so anybody who still wants to use Sign-in via facebook can readd the facebook package and sign in like this:

public async Task<IFirebaseUser> SignInWithFacebookAsync()
{
    try {
        var credential = await _facebookAuth.GetCredentialAsync(ViewController);
        return await CrossFirebaseAuth.Current.SignInWithCredentialAsync(credential);
    } catch(NSErrorException e) {
       // handle exception
    }
}

What do you guys think about that?

Thanks @TobiasBuchholz for this amazing plugin! splitting the packages is a FANTASTIC idea because, it will help developers to just add the required services from Firebase and avoid increased app sizes and removed unnecessary dependencies. The packages naming can be something like:

TobiasBuchholz commented 1 year ago

My basic idea was to do it like the Xamarin.Essentials package, to include everything at once and let the linker do the work, but since there seem to be all sorts of problems I guess you are right with your suggestion, so I'll make a new version of the plugin as soon as I find time for it :)

tranb3r commented 1 year ago

On the contrary, I really like the idea of a single nuget as it is right now. Like it was said before, the linker should work, assuming the plugin is implemented like Xamarin.Essentials, and the dependencies are compliant with the linker. There is an issue specific to Facebook SDK, which is not removed by the linker, and this justifies extracting Facebook code and maybe creating a package for it. But, for the other Firebase dependencies, are you sur you need to split the package? Maybe the linker just works...

tranb3r commented 1 year ago

Just to clarify my point, I've done a simple test:

Which means, if Plugin.Firebase can be modified to behave like Xamarin.Essentials, it should be possible to keep a single nuget and let the linker get rid of unused Firebase dependencies (except Facebook SDK which needs a specific treatment as already mentionned).

angelru commented 1 year ago

Just to clarify my point, I've done a simple test:

  • when building an app referencing Plugin.Firebase, all Firebase dependencies are included in the final apk. The linker does not remove them.
  • when building an app referencing directly all Firebase dependencies, no Firebase dependency is included in the final apk (except Facebook SDK). The linker correctly remove them.

Which means, if Plugin.Firebase can be modified to behave like Xamarin.Essentials, it should be possible to keep a single nuget and let the linker get rid of unused Firebase dependencies (except Facebook SDK which needs a specific treatment as already mentionned).

and how do you make it behave like Essentials?

tranb3r commented 1 year ago

No idea. I just remember that at some point they redesigned Xamarin.Essentials to make sure the linker keeps only the code that is used by the app. So I assume it should be possible to do the same here, since this plugin is also essentially a group of independant features that can be enabled or not on init. @TobiasBuchholz suggested it, maybe he knows more about it.

TobiasBuchholz commented 1 year ago

All methods, properties, fields, events, structs, and even classes that are not explicitly referenced in the project code will get removed by the linker, so I think the problem is that too much code is entangled in the plugin, e.g. because of the initialization process through the CrossFirebaseSettings or because of mixed extension classes, so it can't be stripped because it has somewhere some references. I think the best approach would be to separate the features into single nuget packages, as suggested already, so the entanglement won't be possible anymore. After all features are extracted into single packages, there could be an additional one that references them alltogether, so we could still have a single nuget package for all the features next to the separated packages.

tranb3r commented 1 year ago

So, if I understand your proposal, the additional nuget that references all the feature nugets would have no code, but just references to other packages? Do you think the linker would work, if an app references this additional nuget, but does not use all features? Or in this case, the app should not reference the additional nuget, but only the required features nugets? Or maybe we'll just see after the split is done...

TobiasBuchholz commented 1 year ago

Do you think the linker would work, if an app references this additional nuget, but does not use all features?

That's my assumption, but I don't know for sure...so yeah, we will see :)

tranb3r commented 1 year ago

Just to clarify my point, I've done a simple test:

  • when building an app referencing Plugin.Firebase, all Firebase dependencies are included in the final apk. The linker does not remove them.
  • when building an app referencing directly all Firebase dependencies, no Firebase dependency is included in the final apk (except Facebook SDK). The linker correctly remove them.

Which means, if Plugin.Firebase can be modified to behave like Xamarin.Essentials, it should be possible to keep a single nuget and let the linker get rid of unused Firebase dependencies (except Facebook SDK which needs a specific treatment as already mentionned).

I've just realized I've been doing it wrong. The default mode for the dotnet trimmer only trims SDK assemblies. In order to trim all assemblies, you need to set TrimMode=full.

When trimming all assemblies, the result is perfectly fine. Only a few things remain from Facebook and Firebase when you just reference Plugin.Firebase (with no call to the code at all). Of course, someone should probably double-check my conclusions. But my feeling is that it's probably good enough for most people, and it definitely does not justify splitting the plugin.

I'm gonna close this issue, since the initial problem of app size increase is actually solved if the trimmer is properly configured.

hattmann commented 1 year ago

Hello!

Using the Plugin.Firebase and deploying to Appstore Connect causes the following Error Message from Apple:

" ... contains bitcode. "!

image

Using "Plugin.Firebase.Temp" fixed this issue.

Will the TrimMode=full Parameter remove also the bitcode from the .IPA File?