Our Flutter app is so big on iOS, it is 5 times Google Chrome (and some Apple App Store Connect publication tips)

Flutter

At jod.li, we started developing Flutter apps in June 2019 (9 months ago). We needed a cross-platform mobile development solution. We chose Flutter because it is evolving fast, yet, it is stable enough for robust development. There is a large community. Also, with Google as the underlying force behind, things are less likely to go wrong. Finally, we tried several other solutions without being convinced and sometimes even being very disappointed (several months of development on a non-Flutter solution and after an update, we ended up with an unrecoverable amount and complexity of compilation errors).

So, Flutter seemed like a good choice.

An example app

We have grown enough knowledge in Flutter to develop side projects in a few days as we need it. During the covid-19 pandemic, the cryptocurrencies are going crazy. Thus, not having a notification on Binance when a price reaches a certain threshold appeared like a problem easy to fix with a Flutter app.

We have thus developed an app that does just that relying on the Binance API.

App size

Came the time of publication on the Google Play Store. The Android App ends up with a very reasonable size smaller than 7 MB.

However, the iOS app once installed on an iPhone takes 407.1 MB. This is 5 times the 84.2 MB the Google Chrome app takes on the same iPhone. This is of course not reasonable.

This is a time when we realise that we may have taken – again – the wrong road for several months with Flutter. All of this because of this final app size on iOS.

What does internet say about this?

The most interesting we found is here:

https://github.com/flutter/flutter/issues/47101#issuecomment-567522077

It says that the embedded Flutter framework file (the vast majority of the bytes in the app) is going to be reduced in the app before reaching the user mobile devices. Well, we’d like to see this happening before we pay the 100 bucks to Apple for the publication.

Preparing the app for the store

So to get closer to the store reality, we follow the iOS Flutter publication guidelines:

https://flutter.dev/docs/deployment/ios#review-xcode-project-settings

We clean Xcode and Flutter, close Xcode and then:

flutter build ios

Open Xcode and select:

  • Runner scheme
  • Generic iOS Device Destination
  • Runner target
  • Optimisation for size
  • Product > Archive
  • Show archive in Finder

The archive size is even bigger than the debug app size on the iPhone: 577.4 MB

Let’s go further anyway.

Deploy to Apple app store

We pay the 100 bucks for a deployment in the Apple app store.

Few hours later, we receive the confirmation email and create the app in the app store.

Small trick, the store icon cannot contain any alpha layer both in the app store and in the app archive (1024×1024 px).

Back in Xcode, at the archive validation step, there is indeed a mention that the bitcode will not be included and that Swift symbols can be striped.

Since the archive is extremely big, the uploading takes ages with an annoying forever 100% progress bar.

Permission and other issues

After all this time uploading, the store sends a rejection email with the following permission related issues:

ITMS-90683: Missing Purpose String in Info.plist - Your app's code references one or more APIs that access sensitive user data. The app's Info.plist file should contain a NSContactsUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. Starting Spring 2019, all apps submitted to the App Store that access user data are required to include a purpose string. If you're using external libraries or SDKs, they may reference APIs that require a purpose string. While your app might not use these APIs, a purpose string is still required. You can contact the developer of the library or SDK and request they release a version of their code that doesn't contain the APIs. Learn more (https://developer.apple.com/documentation/uikit/core_app/protecting_the_user_s_privacy).

ITMS-90683: Missing Purpose String in Info.plist - Your app's code references one or more APIs that access sensitive user data. The app's Info.plist file should contain a NSCalendarsUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. Starting Spring 2019, all apps submitted to the App Store that access user data are required to include a purpose string. If you're using external libraries or SDKs, they may reference APIs that require a purpose string. While your app might not use these APIs, a purpose string is still required. You can contact the developer of the library or SDK and request they release a version of their code that doesn't contain the APIs. Learn more (https://developer.apple.com/documentation/uikit/core_app/protecting_the_user_s_privacy).

ITMS-90683: Missing Purpose String in Info.plist - Your app's code references one or more APIs that access sensitive user data. The app's Info.plist file should contain a NSAppleMusicUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. Starting Spring 2019, all apps submitted to the App Store that access user data are required to include a purpose string. If you're using external libraries or SDKs, they may reference APIs that require a purpose string. While your app might not use these APIs, a purpose string is still required. You can contact the developer of the library or SDK and request they release a version of their code that doesn't contain the APIs. Learn more (https://developer.apple.com/documentation/uikit/core_app/protecting_the_user_s_privacy).

ITMS-90683: Missing Purpose String in Info.plist - Your app's code references one or more APIs that access sensitive user data. The app's Info.plist file should contain a NSMotionUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. Starting Spring 2019, all apps submitted to the App Store that access user data are required to include a purpose string. If you're using external libraries or SDKs, they may reference APIs that require a purpose string. While your app might not use these APIs, a purpose string is still required. You can contact the developer of the library or SDK and request they release a version of their code that doesn't contain the APIs. Learn more (https://developer.apple.com/documentation/uikit/core_app/protecting_the_user_s_privacy).

ITMS-90683: Missing Purpose String in Info.plist - Your app's code references one or more APIs that access sensitive user data. The app's Info.plist file should contain a NSSpeechRecognitionUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. Starting Spring 2019, all apps submitted to the App Store that access user data are required to include a purpose string. If you're using external libraries or SDKs, they may reference APIs that require a purpose string. While your app might not use these APIs, a purpose string is still required. You can contact the developer of the library or SDK and request they release a version of their code that doesn't contain the APIs. Learn more (https://developer.apple.com/documentation/uikit/core_app/protecting_the_user_s_privacy).

Though you are not required to fix the following issues, we wanted to make you aware of them:

ITMS-90078: Missing Push Notification Entitlement - Your app appears to register with the Apple Push Notification service, but the app signature's entitlements do not include the "aps-environment" entitlement. If your app uses the Apple Push Notification service, make sure your App ID is enabled for Push Notification in the Provisioning Portal, and resubmit after signing your app with a Distribution provisioning profile that includes the "aps-environment" entitlement. Xcode does not automatically copy the aps-environment entitlement from provisioning profiles at build time. This behavior is intentional. To use this entitlement, either enable Push Notifications in the project editor's Capabilities pane, or manually add the entitlement to your entitlements file. For more information, see https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/HandlingRemoteNotifications.html#//apple_ref/doc/uid/TP40008194-CH6-SW1.

ITMS-90683: Missing Purpose String in Info.plist - Your app's code references one or more APIs that access sensitive user data. The app's Info.plist file should contain a NSLocationAlwaysUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. Starting Spring 2019, all apps submitted to the App Store that access user data are required to include a purpose string. If you're using external libraries or SDKs, they may reference APIs that require a purpose string. While your app might not use these APIs, a purpose string is still required. You can contact the developer of the library or SDK and request they release a version of their code that doesn't contain the APIs. Learn more (https://developer.apple.com/documentation/uikit/core_app/protecting_the_user_s_privacy).

ITMS-90683: Missing Purpose String in Info.plist - Your app's code references one or more APIs that access sensitive user data. The app's Info.plist file should contain a NSLocationWhenInUseUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. Starting Spring 2019, all apps submitted to the App Store that access user data are required to include a purpose string. If you're using external libraries or SDKs, they may reference APIs that require a purpose string. While your app might not use these APIs, a purpose string is still required. You can contact the developer of the library or SDK and request they release a version of their code that doesn't contain the APIs. Learn more (https://developer.apple.com/documentation/uikit/core_app/protecting_the_user_s_privacy).

Here is an interesting link on the permissions topic.

To summarise, The app requires permissions that are not needed for its execution. For Apple not to reject the build, we need to mention those permissions as not needed in the Podfile:

  • NSContactsUsageDescription
  • NSCalendarsUsageDescription
  • NSAppleMusicUsageDescription
  • NSMotionUsageDescription
  • NSSpeechRecognitionUsageDescription
  • NSLocationAlwaysUsageDescription
  • NSLocationWhenInUseUsageDescription

Therefore, we added the following to the Podfile:

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      # Here are some configurations automatically generated by flutter

      # You can remove unused permissions here
      # for more infomation: https://github.com/BaseflowIT/flutter-permission-handler/blob/develop/ios/Classes/PermissionHandlerEnums.h
      # e.g. when you don't need camera permission, just add 'PERMISSION_CAMERA=0'
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
        '$(inherited)',

        ## dart: PermissionGroup.calendar
         'PERMISSION_EVENTS=0',

        ## dart: PermissionGroup.reminders
         'PERMISSION_REMINDERS=0',

        ## dart: PermissionGroup.contacts
         'PERMISSION_CONTACTS=0',

        ## dart: PermissionGroup.camera
         'PERMISSION_CAMERA=0',

        ## dart: PermissionGroup.microphone
         'PERMISSION_MICROPHONE=0',

        ## dart: PermissionGroup.speech
         'PERMISSION_SPEECH_RECOGNIZER=0',

        ## dart: PermissionGroup.photos
         'PERMISSION_PHOTOS=0',

        ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
         'PERMISSION_LOCATION=0',

        ## dart: PermissionGroup.notification
         'PERMISSION_NOTIFICATIONS=0',

        ## dart: PermissionGroup.mediaLibrary
         'PERMISSION_MEDIA_LIBRARY=0',

        ## dart: PermissionGroup.sensors
         'PERMISSION_SENSORS=0'
      ]

    end
  end
end

We also need to find a solution regarding the push notifications which we don’t use either. Add file ios/Runner/Runner.entitlements with this content:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>aps-environment</key>
	<string>Write here your reason for using push notifications.</string>
</dict>
</plist>

Clean, build, archive, validate, upload,… again.

After some complaints because iPad requires all orientations for multitasking, we adapt configuration in Xcode to force fullscreen (and disable multitasking) and start again:

Invalid Bundle. iPad Multitasking support requires these orientations: 'UIInterfaceOrientationPortrait,UIInterfaceOrientationPortraitUpsideDown,UIInterfaceOrientationLandscapeLeft,UIInterfaceOrientationLandscapeRight'. Found 'UIInterfaceOrientationPortrait' in bundle ...

We also got the build number error although no app was actually successfully published yet (we just increase the build number to 1.0.1):

ERROR ITMS-90189: "Redundant Binary Upload. You've already uploaded a build with build number '1.0.0' for version number '1.0.0'. Make sure you increment the build string before you upload your app to App Store Connect. Learn more in Xcode Help (http://help.apple.com/xcode/mac/current/#/devba7f53ad4)."

Nearly 2 hours of upload later, finally some good news:

We complete the submission on the Apple App Store Connect site.

Publication

6 hours later, the app is in review and 30 minutes later, the app is ready for sale.

Few hours later, the app is published on the app store and the resulting app size is a very reassuring 25.2 MB.

Conclusion

We should not worry too much about the app size before it is published. Once published, the app size is reduced considerably (archive 577 MB -> iPhone from app store 25 MB).

This first Flutter iOS Apple App Store Connect publishing made us aware of the following traps:

  • App store icon cannot contain transparency layer (1024×1024).
  • The archive is so big that the publication takes a very long time. Thus, automating this step is highly desirable.
  • When using the permission handler, include all the non-needed permissions in the Podfile.
  • With Flutter, always include a ios/Runner/Runner.entitlements file for the push notification permissions even if not used.
  • If not all orientations are supported on iPad, disable multitasking by supporting only fullscreen.
  • Always increase build number even if no application version was published.

Leave a Reply

Your email address will not be published. Required fields are marked *