Symptoms
- App size is bigger than the over-the-air download limit and forces iOS users to download the app via WiFi.
- The universal binary of the app or single slices are bigger than allowed from the Apple submission guidelines.
Cause
My app is bigger than the over-the-air download limit on iOS and bigger than the main application bundle size on tvOS.
The otool report shows that single binary slices (32bit, 64bit) or the universal binary are bigger than the allowed limits from the Apple submission guidelines.
Resolution
IL2CPP converts ahead-of-time (AOT) C# code into C++ code which will be compiled into the final binary of your app using XCode and LLVM. This can lead to bigger universal binaries or app slices (32bit,64bit). Your code can be optimised depending on several factors. Read the following to gain insight in what you can do to improve your build size:
- If you make a universal build, there is a 32bit slice and a 64bit slice, which contains the exact same executable for 2 different architectures so nearly doubled size.
- IL2CPP produced bigger builds even if you just compare ARMv7 Mono with ARMv7 IL2CPP; this had to do with how we dealt with the type metadata. It was resolved in Unity 4.6.4p2 and improved further up to 4.7.0f1 and 5.4.0f1.
Read the details for app store submission here.
-
For apps whose
MinimumOSVersion
is less than 7.0: maximum of 80 MB for the total of all__TEXT
sections in the binary. -
For apps whose
MinimumOSVersion
is 7.x through 8.x: maximum of 60 MB per slice for the__TEXT
section of each architecture slice in the binary. -
For apps whose
MinimumOSVersion
is 9.0 or greater: maximum of 500 MB for the size of the Mach-O binary file.
- Build your app in release mode. Do not use debug mode, as it does not represent your final app. Ensure you use correct optimisation, more information can be found here: http://docs.unity3d.com/Manual/iphone-playerSizeOptimization.html.
- Archive your app in XCode and export the app with your deployment certificate to an .ipa.
- Use the estimate button to get the estimated size of your app to ensure if you are above or below 100MB or the overall size of 4GB.
Note: since XCode 6.3 there is no estimate button anymore. You can calculate it by using following formula, but be aware that the compression coefficient might vary:
app_store_size = sizeof(.ipa/(zip with exec_binary removed)) + sizeof(codesegment(exec_binary)) + 0.2 * sizeof(datasegment(exec_binary))
The formula is coming from broader knowledge that only the code segment gets encrypted and the data segment compression ratio can be verified pretty easy.
Extract the data segment from executable via dd command (you can specify byte offset + length) and then try to compress it. The code segment gets scrambled to look like a perfect noise. You need to descramble it with a decryption key before executing. iTunes/App Store app manages the keys. That's why we add code segment as whole without adjusting for compression ratio. - Once you have created your .ipa (Which should have nearly the same size as the estimated size button in XCode returns) and navigate the Terminal.app to the folder the .ipa was generated in and run otool.
- Use otool to extract the output you need to see if you are close to the the 80MB limit.
otool -l <your_app_name>.app/<your_app_name> or size <your_app_name>.app/<your_app_name> - Now you can retrieve the output depending on the architecture (armv7 + a section arm64 if it's a universal build) Gather the information for armv7 (LC_SEGMENT) and do the same for arm64 if applicable (LC_SEGMENT_64)
- Locate the LC_SEGMENT with segname __TEXT and take the filesize
code segment size = 30474240 ~= 30MB - Locate the LC_SEGMENT with segname __DATA and take the filesize
data segment size (mostly metadata) = 10420224 ~= 10 MB
architecture armv7:
code segment size = 30474240 ~= 30MB
data segment size (mostly metadata) = 10420224 ~= 10 MB
segment + data segment = 30 + 10 = 40MB
architecture arm64:
code segment size = 25559040 ~= 26MB
data segment size (mostly metadata) = 17727488 ~= 18 MB
segment + data segment = 26 + 18 = 44MB
Apple uses for their check armv7(code segment)+arm64 (code segment) which results in a otool report of 30MB + 26MB = 56MB in this example which is below the 80MB for <7.0 and 30MB & 26MB are each below 60MB >=7.0 for a universal build. - Locate the LC_SEGMENT with segname __TEXT and take the filesize
iOS devices using iOS 9.0 and above will download only the slice (32 or 64) depending on their needs. Apple splits the binary and creates a separate package, so those platforms e.g. iPhone 6s should have a smaller Download Size without you doing anything to your game. This is completely unrelated to ODR or Bitcode. Devices running below iOS 9.0 will still need to download the Universal build however.
You can analyse scripts even more and deeply investigate script contributions to build size. You can inspect the executable size and the contributions that scripts have with the MapFileParser utility to get some information. The tool processes the map file generated by Xcode when you build the app (it won't be generated when archiving the app). The -stats flag is not implemented in 5.2.4p1 and below. You can find the MapFileParser in the Unity.app directory or in your generated XCode project root directory. The your_map_file.map should be found in the Derived Data folder of the Xcode linker output.
The full path would be similar to Library/Developer/Xcode/DerivedData/Unity-iPhone-glmxdxebssyebsfcbtobeuasetge/Build/Intermediates/Unity-iPhone.build/<mode>-iphoneos/Unity-iPhone.build/alpin-LinkMap-normal-<architecture>.txt
You can analyse the map file using following command in the Terminal.app
<Contents/Tools/MapFileParser/>MapFileParser -format=Clang <[YourAppName]-LinkMap-normal-[Architecture].txt> -stats
Analysing the names of the functions is the best way to make the distinction between different code (user scripts, static libraries). Code generated from the managed assemblies will show up with _m suffixes e.g:
_TestScript_Start_m4164746442: 10 bytes
_TestScript_Update_m263972995: 10 bytes
_TestScript_ctor_m922641354: 24 bytes
This might help you to understand your code, plugins or engine code even better.
- Read more about Optimizing iOS & tvOS apps with app thinning and IL2CPP
- Read more about IL2CPP here
-
Read more about IL2CPP in the IL2CPP blog posts
-
Read more about iOS 64bit support
- Read move about IL2CPP Build Size improvement
This article applies to Unity versions 5.2.0p1 and higher, XCode 7 and higher, iOS 9.0 and higher
Comments
0 comments
Article is closed for comments.