Facebook.app for iOS [v. 88.0] cleans up duplicates

Posted: April 15th, 2017 | Author: | Filed under: Analysis, Debugging, iOS, Programming | Tags: , | No Comments »

This post follows up the Analysis of the Facebook.app for iOS [v. 87.0].
The version 88.0 of the Facebook.app has now been released:

Download Info

As you can see from the smaller download size, the duplicated resources have been removed. This is confirmed by looking at the app content using GrandPerspective:

Facebook 88.0 content

Only some really small resources escaped the cleanup. The ‘FBFacecastTipJarResources’ resources are indeed still duplicated. Example:

  • Facebook.app/Frameworks/FBSharedFramework.framework/FBFacecastTipJarResources/tip3b.json.gz
  • Facebook.app/Frameworks/FBSharedFramework.framework/tip3b.json.gz

Analysis of the Facebook.app for iOS [v. 87.0]

Posted: April 10th, 2017 | Author: | Filed under: Analysis, Debugging, iOS, Programming | Tags: , | 16 Comments »

6 months ago I analyzed the version 66.0 of the Facebook.app for iOS:

The version 66.0 was a 165 MB app on an iPad Air 2 (64-bit). It was a monolithic app with its main binary being more than 100 MB.

The version 87.0 is now available: 253 MB on the same iPad Air 2 with only 64-bit code. In just 6 months, the Facebook.app size grew by 88 MB!

Download Info

Let’s see what changed…

App content

Looking at the app content of the version 87.0 using GrandPerspective gives a good overview:

Facebook 87.0 content

Here is how the content of the version 66.0 looked like:

Facebook 66.0 content

As you can see, the Facebook.app has been completely restructured:

  • the main binary is now only 19 MB
  • there is a huge 136 MB framework ‘FBSharedFramework’
  • there is another 26 MB framework called ‘FBNotOnStartupPathFramework’

Duplicated resources

One of the reason the app size grew is due to multiple copies of the same resources inside the app.


You can actually see in the previous screenshot 3 copies of the same file ‘unetshallow_init.pb’:

  • Facebook.app/Frameworks/FBNotOnStartupPathFramework.framework/opticalflow_resource/unetshallow_init.pb
  • Facebook.app/Frameworks/FBSharedFramework.framework/opticalflow_resource/unetshallow_init.pb
  • Facebook.app/opticalflow_resource/unetshallow_init.pb


Keeping only one copy would save 7.2 MB.


Similarly the DataFiles folder appears 3 times. Keeping a single copy would save around 2 MB:

  • Facebook.app/DataFiles/
  • Facebook.app/Frameworks/FBNotOnStartupPathFramework.framework/DataFiles/
  • Facebook.app/Frameworks/FBSharedFramework.framework/DataFiles/



Some resources called ‘FBFacecastTipJarResources’ appear to be duplicated no less than 6 times in the app! The ‘FBFacecastTipJarResources’ resources only take 150 KB on disk but 6 times makes 900 KB.

One example:

  • Facebook.app/FBFacecastTipJarResources/sent.m4a
  • Facebook.app/Frameworks/FBNotOnStartupPathFramework.framework/FBFacecastTipJarResources/sent.m4a
  • Facebook.app/Frameworks/FBNotOnStartupPathFramework.framework/sent.m4a
  • Facebook.app/Frameworks/FBSharedFramework.framework/FBFacecastTipJarResources/sent.m4a
  • Facebook.app/Frameworks/FBSharedFramework.framework/sent.m4a
  • Facebook.app/sent.m4a

Other duplicated resources

There are a bunch of other duplicated resources, amongst them:

  • modelMetaData.bin: 4 * 1 MB
  • schemaMetaData.bin: 4 * 830 KB
  • FBCommunicationSoundKit.bundle: 3 * 741 KB
  • MNSounds.bundle: 3 * 528 KB
  • FBSoundControllerResources: 3 * 500 KB
  • RelaySchema.json: 3 * 319 KB
  • libPhoneNumber.bundle: 3 * 172 KB
  • CACerts.plist: 3 * 168 KB
  • FBFacecastBroadcastKitResources: 3 * 98 KB
  • Montserrat-SemiBold.ttf: 3 * 70 KB
  • ReactMobileConfigMetadata.json: 3 * 33 KB
  • FBEntityCardsModuleResources: 3 * 33 KB
  • add-photo@2x.jpg: 3 * 29 KB
  • FBFacecastWithKitResources: 3 * 20 KB
  • FBNativeArticleEngagementActionsResources: 3 * 20 KB
  • FBFeedbackReactionsKitResources: 3 * 12 KB

These listed resources count for 15.5 MB. By removing the duplicated resources, you could save at least 10 MB.

Duplicated images

The Facebook.app contains 3 assets.car files:

  • Facebook.app/Assets.car: 11.3 MB for 2267 items
  • Facebook.app/Frameworks/FBNotOnStartupPathFramework.framework/Assets.car: 10.3 MB for 2126 items
  • Facebook.app/Frameworks/FBSharedFramework.framework/Assets.car: 9.5 MB for 1972 items


All the 1972 images in FBSharedFramework are inside the main Assets.car and also part of the FBNotOnStartupPathFramework Assets.car. So there are 3 times the same 1972 images taking 3 * 9.5 MB = 28.5 MB.

Keeping a single set of these 1972 images would save 19 MB.

The remaining images in the main Assets.car (2267 – 1972 = 295) and in the FBNotOnStartupPathFramework Assets.car (2126 – 1972 = 154) are unique.

New localizations

The Facebook.app gained 5 new localizations, increasing the app size by 4.2 MB:
hi.lproj (1.3 MB)
hr.lproj (692 KB)
hu.lproj (750 KB)
ro.lproj (709 KB)
sk.lproj (713 KB)

Also each localization got a new 12 KB file called ‘AdsCountriesConfig.json’. This adds 336 KB.


When analyzing the version 66.0 I completely missed some amusing Objective-C interfaces, protocols and methods:

@protocol FBDeprecatedAppModule_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
@protocol FBLoginFacilitatingAppModule
@interface FBTimelineModule : FBNativeAppModule_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
@interface FBNotificationsModule : FBNativeAppModule_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
@interface FBProductionLockoutModule : FBNativeAppModule_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
@interface FBSearchModule : FBNativeAppModule_DO_NOT_USE_OR_YOU_WILL_BE_FIRED

__RODATA segment

Although the main binary is much smaller than the App Review limitation, the Facebook.app still uses a __RODATA segment containing sections generally found inside the __TEXT segment. For more information about it, please look at the previous post http://blog.timac.org/?p=1303.


Between version 66.0 and 87.0, the Facebook.app has been completely restructured. The main -and only- binary has been split in several frameworks.

It appears however that during this process a couple of resources have been unnecessarily duplicated:

  • opticalflow_resource: 3 * 3.6 MB
  • DataFiles: 3 * 1 MB
  • images: 3 * 9.5 MB
  • FBFacecastTipJarResources: 750 KB
  • Other duplicated resources: 15.5 MB

Removing the duplicated resources would save at least 40 MB. This partially explains why the app size has increased by 90 MB.

Update 15.04.2017: Facebook.app for iOS [v. 88.0] cleans up duplicates

Apple’s use of Swift in iOS 10.1 and macOS 10.12

Posted: November 1st, 2016 | Author: | Filed under: Analysis, iOS, macOS, Programming, Swift | 5 Comments »

Swift has been announced at the WWDC 2014, more than 2 years ago. Most of the sample code projects from Apple are now written in Swift. But does Apple use Swift in iOS 10.1 and macOS 10.12.1?

How to detect if a binary is using Swift?

A naïve approach would be to check if an app contains the Swift libraries in its Frameworks folder: libswiftCore.dylib, libswiftFoundation.dylib, …

Here is the content of the Frameworks folder of the MRT.app on macOS 10.12.1
/System/Library/CoreServices/MRT.app/Contents/Frameworks/ :


However this is not a good approach since iOS and macOS contain a private copy of the Swift libraries in /System/Library/PrivateFrameworks/Swift/ . Several apps in iOS and macOS link directly to these system libraries.

Here is the content of the Frameworks folder of the PIPAgent.app on macOS 10.12.1
/System/Library/CoreServices/PIPAgent.app/Contents/Frameworks/ :


A much better approach is to check whether a binary links to a Swift library. This can easily be done with the command line tool ‘otool’ using the -L option:

-L Display the names and version numbers of the shared libraries that the object file uses, as well as the shared library ID if the file is a shared library.

When running this command on the PIPAgent application:

otool -L /System/Library/CoreServices/PIPAgent.app/Contents/MacOS/PIPAgent | grep swift

you would get the following output:

/System/Library/PrivateFrameworks/Swift/libswiftAppKit.dylib (compatibility version 1.0.0, current version 800.8.18)
/System/Library/PrivateFrameworks/Swift/libswiftCore.dylib (compatibility version 1.0.0, current version 800.8.18)
/System/Library/PrivateFrameworks/Swift/libswiftCoreData.dylib (compatibility version 1.0.0, current version 800.8.18)
/System/Library/PrivateFrameworks/Swift/libswiftCoreGraphics.dylib (compatibility version 1.0.0, current version 800.8.18)
/System/Library/PrivateFrameworks/Swift/libswiftCoreImage.dylib (compatibility version 1.0.0, current version 800.8.18)
/System/Library/PrivateFrameworks/Swift/libswiftDarwin.dylib (compatibility version 1.0.0, current version 800.8.18)
/System/Library/PrivateFrameworks/Swift/libswiftDispatch.dylib (compatibility version 1.0.0, current version 800.8.18)
/System/Library/PrivateFrameworks/Swift/libswiftFoundation.dylib (compatibility version 1.0.0, current version 800.8.18)
/System/Library/PrivateFrameworks/Swift/libswiftIOKit.dylib (compatibility version 1.0.0, current version 800.8.18)
/System/Library/PrivateFrameworks/Swift/libswiftObjectiveC.dylib (compatibility version 1.0.0, current version 800.8.18)

Building a script

Using the otool command line tool, it is easy to write a bash function that tells if a file is a binary linked to the Swift libraries:

# Function to check if a file (passed as argument $1) is using Swift
# It returns the number of occurrences of the string 'swift'
# from the output of otool
isFileUsingSwift ()
	otool -L $1 2>/dev/null | grep -o swift | wc -l

The processFile bash function takes a file as parameter and will print its path if it’s a binary linked to the Swift libraries:

# Function to process a file (passed as argument $1).
# It calls the function isFileUsingSwift() to determine
# if this is a binary using Swift and in this case
# print the path of this file.
processFile ()
	isFileUsingSwift=$( isFileUsingSwift $1 )
 	if [ ${isFileUsingSwift} != 0 ]
 		# We found a binary using Swift
	 	echo "   $1"

Looping through all the files of a folder is now a single line:

find ${PATH_TO_CHECK} -type f -exec bash -c 'processFile "$0"' {} \;

Final script

Below is the complete bash script that loops through all the files of a folder and print the paths of all the binaries found that use Swift.
Note: You can download the complete script here.


# Bash script that loops through all the files of a folder and
# print the paths of all the binaries found that use Swift
# Created by Alexandre Colucci on 01.11.2016
# http://blog.timac.org/?p=1398

# Force expand a wildcard pattern into the list of matching pathnames
shopt -s nullglob

# Function to print the usage
printUsage ()
	echo "Usage: detectSwift.sh PATH"
	echo "PATH: Folder to search for binaries using Swift"
	echo ""
	echo "Examples:"
	echo "  detectSwift.sh /System/Library"
	echo "  detectSwift.sh /System"
	echo "  detectSwift.sh /"
	echo ""
	echo "Note: run as root in order to avoid permission issues."
	echo ""

# Function to check if a file (passed as argument $1) is using Swift
# It returns the number of occurrences of the string 'swift'
# from the output of otool
isFileUsingSwift ()
	otool -L $1 2>/dev/null | grep -o swift | wc -l

# Function to process a file (passed as argument $1).
# It calls the function isFileUsingSwift() to determine
# if this is a binary using Swift and in this case
# print the path of this file.
processFile ()
	isFileUsingSwift=$( isFileUsingSwift $1 )
 	if [ ${isFileUsingSwift} != 0 ]
 		# We found a binary using Swift
	 	echo "   $1"

# Check if the script was called with the expected usage
	exit 1

# Get the folder path

echo ""
echo "Start time:"
echo ""
echo "Apps using Swift in ${PATH_TO_CHECK}"

# Export the functions so that the subshell inherits them
export -f isFileUsingSwift
export -f processFile

# Find all the regular files in all subdirectories
# and call for each the function processFile()

find ${PATH_TO_CHECK} -type f -exec bash -c 'processFile "$0"' {} \;

# Finalizing
echo ""
echo "Completed at:"
echo ""

Running the script

The script is really slow: for each regular file it will create a subshell, call otool, grep and wc.
Running this script on the iOS 10.1 filesystem takes around 30 minutes.

For macOS 10.12.1, running the script on / takes dozen of hours. I recommend to only run this script on /System, /Applications and /usr. Processing these 3 folders in parallel will take around 2 hours.

Apple’s use of Swift in iOS 10.1

Running the script on iOS 10.1 (14B72c) of an iPhone 7 Plus will give you this list of binaries:


You will get these additional binaries from the dyld shared cache:


Note that you will get a similar output for an iPad except that the Calculator.app is not available.

Apple’s use of Swift in macOS 10.12.1

Running the script on macOS 10.12.1 will give you this list of binaries:


Note that you will get a lot of matches in Xcode 8.1. If you exclude the various toolchains and platform SDKs, you will get:



Apple’s use of Swift in iOS 10.1 and macOS 10.12.1 is extremely limited.
On iOS 10.1 there are only 2 apps and 2 private frameworks using Swift:

  • Calculator.app (iPhone only)
  • Music.app
  • UpNextWidget.framework
  • CoreKnowledge.framework

On macOS 10.12.1 the list of apps using Swift is limited to:

  • Console
  • swift
  • swiftc
  • usernoted
  • Dock.app
  • MRT.app
  • NotificationCenter.app
  • OSDUIHelper.app
  • PIPAgent.app
  • (Xcode.app)

Analysis of the Facebook.app for iOS

Posted: October 18th, 2016 | Author: | Filed under: Analysis, Debugging, iOS, Programming | Tags: , , , , | 1 Comment »

Did you ever wonder why the Facebook.app for iOS is such a big download? This post tries to give some answers. The version 66.0 (released on 7 October 2016) was analyzed on an iPad Air 2 (64-bit).

Here is what you see when downloading Facebook on an iPad Air 2:

Download Info

App content

A scan of the content of the Facebook app using GrandPerspective gives already a good overview:


As you can see more than 100 MB are taken by the binary itself. The images (Assets.car), the localizations and other resources are only responsible for a small part of the size.

One note: The Facebook app seems to use the open source JSC framework. Unless there is a technical reason to use it, Facebook could remove this framework and use instead the JavaScriptCore framework built in iOS. JavaScriptCore is available on iOS 7 and later and Facebook targets iOS 8 and later.



One important point to note is that the Facebook binary I downloaded only contains 64-bit code. There is no 32-bit code. Facebook uses the Slicing mechanism which is described by Apple here:

Slicing is the process of creating and delivering variants of the app bundle for different target devices. A variant contains only the executable architecture and resources that are needed for the target device.



Let’s look at the Facebook binary. The entropy graph generated by Hopper gives a good idea of the content:


The red part (with biggest entropy) is actual code while the green and orange parts are other sections (strings, constants, …). Just by looking at the entropy graph, we know that around 50% of the binary is code.



By looking at the sections of the binary we indeed see that the (__TEXT, __text) section is more than 50 MB. Here is the list of sections sorted by size:

(__TEXT, __text): 56.60 MB
(__DATA, __objc_const): 16.57 MB
(__RODATA, __cstring): 5.02 MB
(__RODATA, __objc_methname): 4.55 MB
(__DATA, __const): 4.46 MB
(__RODATA, __gcc_except_tab): 3.87 MB
(__RODATA, __objc_methtype): 2.66 MB
(__DATA, __objc_data): 2.41 MB
(__DATA, __cfstring): 2.20 MB
(__DATA, __data): 1.66 MB
(__TEXT, __unwind_info): 1.58 MB
(__RODATA, __objc_classname): 1.42 MB
(__RODATA, __const): 1.40 MB
(__DATA, __bss): 0.89 MB
(__DATA, __objc_selrefs): 0.80 MB
(__DATA, __common): 0.54 MB
(__DATA, __objc_ivar): 0.38 MB
(__DATA, __objc_classlist): 0.24 MB
(__DATA, __objc_classrefs): 0.18 MB
(__DATA, __objc_superrefs): 0.13 MB
(__TEXT, __const): 0.07 MB
(__DATA, __objc_protolist): 0.06 MB
(__TEXT, __stubs): 0.02 MB
(__TEXT, __stub_helper): 0.02 MB
(__DATA, __la_symbol_ptr): 0.01 MB
(__DATA, __got): 0.01 MB
(__TEXT, __ustring): 0.01 MB
(__DATA, fbsessiongks): 0.01 MB
(__TEXT, __eh_frame): 0.01 MB
(__DATA, __mod_init_func): 0.00 MB
(__DATA, __objc_protorefs): 0.00 MB
(__DATA, __objc_catlist): 0.00 MB
(__DATA, __objc_nlclslist): 0.00 MB
(__DATA, FBInjectable): 0.00 MB
(__DATA, __mod_term_func): 0.00 MB
(__DATA, __objc_imageinfo): 0.00 MB


__RODATA segment

If you read carefully the list of sections, you did notice that the Facebook.app has a __RODATA segment containing sections generally found inside the __TEXT segment. The reason for this oddity is to work around an App Review limitation. As you can read on the App Review page:

Each Mach-O executable file (for example, app_name.app/app_name) must not exceed these limits:

  • 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 400 MB for the size of the Mach-O binary file.


Facebook targets iOS 8.0 and later and as such its __TEXT segment should not exceed 60 MB. But if you sum up the __TEXT and __RODATA segments, you end up with more than 77 MB:

Total size of the __TEXT sections: 58.3 MB
Total size of the __RODATA sections: 18.9 MB

Facebook avoids this limitation by moving some if the __TEXT sections into the read only __RODATA segment. Implementing this trick is really simple: you just need to add a linker flag to rename the chosen sections. And it appears you need absolutely nothing at runtime: the renamed sections will be found automatically. This linker flag is described in the ld man page:

-rename_section orgSegment orgSection newSegment newSection
Renames section orgSegment/orgSection to newSegment/newSection.

You could use it to rename the (__TEXT, __cstring) section to (__RODATA, __cstring) by simply adding this line into the Other Linker Flags (OTHER_LDFLAGS):


You can download a sample macOS Xcode project here. If you load the original binary (without the section rename) using MachOView, you will see the (__TEXT, __cstring) section:

Standard Section

After adding the linker flag and recompiling, a (__RODATA, __cstring) section will be visible and the binary will run as expected:

Renamed Section

Note that this workaround is only needed as long as Facebook supports iOS 8. When targeting iOS 9 or later, the limitation is 400 MB which should be enough without splitting the __TEXT segment.


Third party libraries

So what does the code contain? Sadly I could not find a license/readme file containing the list of third party libraries. Also the Info view in Settings.app > Facebook > Settings > Infos is desperately empty – this seems like a Facebook.app bug.

By looking at the class names, I could build a list of third party libraries used. This list is most likely incomplete but hopefully correct:

  • AsyncDisplayKit: http://asyncdisplaykit.org
    keeps even the most complex user interfaces smooth and responsive
  • Bolts-ObjC: https://github.com/BoltsFramework/Bolts-ObjC
    Bolts is a collection of low-level libraries designed to make developing mobile apps easier
  • CocoaAsyncSocket: https://github.com/robbiehanson/CocoaAsyncSocket
    Asynchronous socket networking library for Mac and iOS
  • CocoaLumberjack: https://github.com/CocoaLumberjack/CocoaLumberjack
    A fast & simple, yet powerful & flexible logging framework for Mac and iOS
  • ComponentKit: http://componentkit.org
    ComponentKit takes a functional, declarative approach to building UI
  • EGODatabase: https://github.com/enormego/egodatabase
    EGODatabase is a thread-safe Objective-C SQLite wrapper with full support for asynchronous SQLite calls as well as built in NSOperationQueue support
  • FLAnimatedImage: https://github.com/Flipboard/FLAnimatedImage
    Performant animated GIF engine for iOS
  • FXBlurView: https://github.com/nicklockwood/FXBlurView
    UIView subclass that replicates the iOS 7 realtime background blur effect, but works on iOS 5 and above
  • Google Toolbox for Mac: https://github.com/google/google-toolbox-for-mac
    Google Toolbox for Mac
  • JSONKit: https://github.com/johnezang/JSONKit
    Objective-C JSON
  • libphonenumber: https://github.com/iziz/libPhoneNumber-iOS
    iOS port from libphonenumber
  • OAuth2Client: https://github.com/nxtbgthng/OAuth2Client
    Client library for OAuth2
  • OCMock: http://ocmock.org
    Mock objects for Objective-C
  • pop: https://github.com/facebook/pop
    An extensible iOS and OS X animation library, useful for physics-based interactions.
  • nighthawk-webrtc: https://github.com/ceaglest/nighthawk-webrtc
    Custom builds of WebRTC for iOS
  • React Native: https://facebook.github.io/react-native/
    Build Native Mobile Apps using JavaScript and React
  • ZipArchive: https://github.com/ZipArchive/ZipArchive
    ZipArchive is a simple utility class for zipping and unzipping files on iOS and Mac
  • ZipZap: https://github.com/pixelglow/ZipZap
    zip file I/O library for Mac OS X and iOS


Objective-C awards

The awards for the best Objective-C classes is attributed to:


The award for the longest Objective-C method is attributed to:

- (void)[FBComposerCompositionStateMutation matchBootstrap:



The Facebook app is a complex app and not just a simple web view. It contains a lot of third party libraries and even splits its __TEXT segment in order to not reach the 60 MB limit imposed by Apple.

Update 10.04.2017:
There is a newer blog post about the version 87.0:
Analysis of the Facebook.app for iOS [v. 87.0]