Slide 1

Slide 1 text

Intro @the69geeks

Slide 2

Slide 2 text

Exception Type: EXC_CRASH (SIGKILL) Exception Codes: 0x0000000000000000, 0x0000000000000000 Exception Note: EXC_CORPSE_NOTIFY Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d Termination Description: SPRINGBOARD, process-launch watchdog transgression: com.go-jek.xxxxx.develop exhausted real (wall clock) time allowance of 20.00 seconds | ProcessVisibility: Unknown | ProcessState: Running | WatchdogEvent: process-launch | WatchdogVisibility: Foreground | WatchdogCPUStatistics: ( | "Elapsed total CPU time (seconds): 27.590 (user 27.590, system 0.000), 69% CPU", | "Elapsed application CPU time (seconds): 0.128, 0% CPU" | ) Triggered by Thread: 0

Slide 3

Slide 3 text

Improving iOS App Launch Time A discussion about how we approached this problem

Slide 4

Slide 4 text

Agenda • What affects launch timing (Measure and Improve) • Why did we faced the issue • How we zeroed on the approach to fix (Pre-Main only) • How we fixed this problem • Key takeaway from the journey

Slide 5

Slide 5 text

App Launch Times • Cold Launch • Warm Launch • Hot Launch

Slide 6

Slide 6 text

What affects launch timing • Pre-Main • Post-Main

Slide 7

Slide 7 text

What affects launch timing • Pre-Main • Measure using DYLD_PRINT_STATISTICS

Slide 8

Slide 8 text

What affects launch timing • Pre-Main • Measure using DYLD_PRINT_STATISTICS • dylib load time, rebase/binding, objc setup, Static Initializers

Slide 9

Slide 9 text

What affects launch timing • Pre-Main • Measure using DYLD_PRINT_STATISTICS • dylib load time, rebase/binding, objc setup, Static Initializers across code • Post-Main • Measure using Instruments

Slide 10

Slide 10 text

What affects launch timing • Pre-Main • Measure using DYLD_PRINT_STATISTICS • dylib load time, rebase/binding, objc setup, Static Initializers across code • Post-Main • Measure using Instruments • Time between call to main() and ability to handle touch

Slide 11

Slide 11 text

What affects launch timing • iOS version, because DYLD version ships with OS

Slide 12

Slide 12 text

What affects launch timing • iOS version, because DYLD version ships with OS • Battery Level (Power Saver mode)

Slide 13

Slide 13 text

What affects launch timing • iOS version, because DYLD version ships with OS • Battery Level (Power Saver mode) • Hardware Capacity

Slide 14

Slide 14 text

What affects launch timing • iOS version, because DYLD version ships with OS • Battery Level (Power Saver mode) • Hardware Capacity • Binary Size

Slide 15

Slide 15 text

Post-Main • Time Spent In: • app(Will)DidFinishedLaunching methods • loadView/viewWill(Did)Appear/draw methods

Slide 16

Slide 16 text

App(Will)DidFinishedLaunching • Singleton initializations (Facebook, Firebase etc?) •

Slide 17

Slide 17 text

App(Will)DidFinishedLaunching • Singleton initializations (Facebook, Firebase etc?) • DB connection / Core Data/ Model Layer Initialization •

Slide 18

Slide 18 text

App(Will)DidFinishedLaunching • Singleton initializations (Facebook, Firebase etc?) • DB connection / Core Data/ Model Layer Initialization • User Location?

Slide 19

Slide 19 text

App(Will)DidFinishedLaunching • Singleton initializations (Facebook, Firebase etc?) • DB connection / Core Data/ Model Layer Initialization • User Location? • Analytics? •

Slide 20

Slide 20 text

App(Will)DidFinishedLaunching • Singleton initializations (Facebook, Firebase etc?) • DB connection / Core Data/ Model Layer Initialization • User Location? • Analytics? • Setup Key Window • … many more

Slide 21

Slide 21 text

ViewRendering (RootVC) • Setup Navigation Container (e.g. TabBarVC)

Slide 22

Slide 22 text

ViewRendering (RootVC) • Setup Navigation Container (e.g. TabBarVC) • Prepare Datasource/ViewModel/VIPER stack •

Slide 23

Slide 23 text

ViewRendering (RootVC) • Setup Navigation Container (e.g. TabBarVC) • Prepare Datasource/ViewModel/VIPER stack • Time require to layout any Complex View

Slide 24

Slide 24 text

ViewRendering (RootVC) • Setup Navigation Container (e.g. TabBarVC) • Prepare Datasource/ViewModel/VIPER stack • Time require to setup View (complex layout) • Any other work done in ViewDid(Will)Load (Auth, StateRestoration etc)

Slide 25

Slide 25 text

Post-Main Use os_signpost for taking accurate measures of a method which is contributing to launch time. Ex: ViewDidLoad of RootViewController

Slide 26

Slide 26 text

Post-Main Use os_signpost for taking accurate measures of a method which is contributing to launch time. Ex: ViewDidLoad of RootViewController

Slide 27

Slide 27 text

POI Details

Slide 28

Slide 28 text

Post-Main Measurement Demo

Slide 29

Slide 29 text

Skipped Post-Main • Overall gains < 10% • Efforts >>

Slide 30

Slide 30 text

Focused on Pre-Main

Slide 31

Slide 31 text

~19 seconds Before Optimization (iPhone6s 12.3.1)

Slide 32

Slide 32 text

~7.5 Seconds After Optimization (iPhone6s 12.3.1)

Slide 33

Slide 33 text

Pre-Main Time exec() to main() Better Guide: https://developer.apple.com/videos/play/wwdc2016/406

Slide 34

Slide 34 text

Dyld: The WorkHorse • Load dylibs • Fix Ups • Objc Setup • Initializer

Slide 35

Slide 35 text

Dyld: The WorkHorse • Load dylibs • Kernel loads Dyld

Slide 36

Slide 36 text

Dyld: The WorkHorse • Load dylibs • Kernel loads Dyld • Dyld loads dylibs

Slide 37

Slide 37 text

Dyld: The WorkHorse • Load dylibs • Fix Ups • Bind all dylibs together

Slide 38

Slide 38 text

Dyld: The WorkHorse • Fix Ups • Rebase • Adjust pointers inside image

Slide 39

Slide 39 text

Dyld: The WorkHorse • Rebase • Adjust pointers inside image • Slid = actual_address - preferred_address

Slide 40

Slide 40 text

Dyld: The WorkHorse • Bindings • Adjust pointers outside image • Find symbol and malloc

Slide 41

Slide 41 text

Dyld: The WorkHorse • Load dylibs • Fix Ups • Objc Setup

Slide 42

Slide 42 text

Dyld: The WorkHorse • Load dylibs • Fix ups • Objc Setup • Initializer

Slide 43

Slide 43 text

DYLD_PRINT_STATISTICS Total pre-main time: 249.96 milliseconds (100.0%) dylib loading time: 191.67 milliseconds (76.6%) rebase/binding time: 15.74 milliseconds (6.2%) ObjC setup time: 9.54 milliseconds (3.8%) initializer time: 33.00 milliseconds (13.2%) slowest intializers : libSystem.B.dylib : 2.49 milliseconds (0.9%) libBacktraceRecording.dylib : 5.08 milliseconds (2.0%) libMainThreadChecker.dylib : 14.41 milliseconds (5.7%) libViewDebuggerSupport.dylib : 7.90 milliseconds (3.1%)

Slide 44

Slide 44 text

Why did we faced this issue?

Slide 45

Slide 45 text

Code Metric • 12000+ Types (Class, Struct, Enum…)

Slide 46

Slide 46 text

Code Metric • 12000+ Types (Class, Struct, Enum…) • > 8000 Swift files, ~1000 ObjC files (Pods)

Slide 47

Slide 47 text

Code Metric • 12000+ Types (Class, Struct, Enum…) • > 8000 Swift files, ~1000 ObjC files (Pods) • >80 Dynamic Libraries (and growing)

Slide 48

Slide 48 text

Development Silos • Team Structure: independent development

Slide 49

Slide 49 text

Development Silos • Team Structure: independent development • Abstract Target in Podfile to make it magical

Slide 50

Slide 50 text

target 'Vendor' do dynamic_frameworks internal_dynamic_frameworks special_dynamic_frameworks inherit! :search_paths target 'Framework1' do inherit! :search_paths end target 'Framework2' do inherit! :search_paths end end target 'DemoApp' do static_frameworks host_only_static_libs dynamic_frameworks internal_dynamic_frameworks special_dynamic_frameworks end

Slide 51

Slide 51 text

Development Silos • Team Structure: independent development • Abstract Target in Podfile to make it magical • Complex Inter Target dependancies

Slide 52

Slide 52 text

Development Silos • Team Structure: independent development • Abstract Target in Podfile to make it magical • Complex Inter Target dependancies • Messed up Build Settings per product

Slide 53

Slide 53 text

Finding a solution • IDEAL: • Reduce internal (by merging) and external (building inhouse) framework count

Slide 54

Slide 54 text

Finding a solution • IDEAL: • Reduce internal (by merging) and external (building inhouse) framework count • Make dependancies as Static Frameworks and link that with Product

Slide 55

Slide 55 text

Finding a solution • IDEAL: • Reduce internal (by merging) and external (building inhouse) framework count • Make dependancies as Static Frameworks and link that with Product • Create create an Umbrella Framework from all Dependancies (Dynamic Framework) •

Slide 56

Slide 56 text

Finding a solution • IDEAL: • Reduce internal (by merging) and external (building inhouse) framework count • Make dependancies as Static Frameworks and link that with Product • Create create an Umbrella Framework from all Dependancies (Dynamic Framework) • All Deps: Static library?

Slide 57

Slide 57 text

Fixing the problem • Find out dependancies in different targets

Slide 58

Slide 58 text

Fixing the problem • Find out dependancies in different targets • Create a Lite app version to compare app launch time

Slide 59

Slide 59 text

Fixing the problem • Find out dependancies in different targets • Create a Lite app version to compare app launch time • Prepare a checklist during the process of migration

Slide 60

Slide 60 text

Fixing the problem • Find out dependancies in different targets • Create a Lite app version to compare app launch time • Prepare a checklist during the process of migration • Automate whatever we can from Checklist

Slide 61

Slide 61 text

Problems Faced • Splitting Framework into Static Libs and Resource Bundle (DEMO) • Custom script to copy all resource bundles to app

Slide 62

Slide 62 text

Problems Faced • CocoaPods + New Build System issues Build system information error: Multiple commands produce '/Users/Ben/Library/ Developer/Xcode/DerivedData/myApp- awbgyiqiegnrtdayecemnuyfqnbc/Build/Products/Debug- iphonesimulator/myApp.app': 1) Target 'myApp' has create directory command with output '/Users/Ben/Library/Developer/Xcode/DerivedData/myApp- awbgyiqiegnrtdayecemnuyfqnbc/Build/Products/Debug- iphonesimulator/myApp.app' 2) That command depends on command in Target 'myApp': script phase “[CP] Copy Pods Resources”

Slide 63

Slide 63 text

Problems Faced • Implicit dependancies not working; so as the Parallel builds • That needs updating all target’s linker flags

Slide 64

Slide 64 text

Problems Faced • As Implicit Dependancies were off, we need to add all imports explicitly • A script to automate inserting some must have imports

Slide 65

Slide 65 text

Problems Faced • IntentDefinition was not getting compiled correctly (autogen classes were not generated) • Copied the generated files manually

Slide 66

Slide 66 text

Problems Faced • R.Swift was not working with Static Libs + Resource Bundle model • Run the r.swift script from resource bundle and add generated files to library • Later replaced R.swift with inhouse code

Slide 67

Slide 67 text

Problems Faced • Change in Framework name affected DLS components in IB files (ModuleName for a class) • XIB files using component from another framework, and now the name of framework is changed

Slide 68

Slide 68 text

Problems Faced • Archive failed couple of times: • One header for static library was in copied in sources • ResourceBundle executable was not code signed (Demo the plist)

Slide 69

Slide 69 text

Problems Faced • A few Crashes due to wrong scheme selected for archive (for some products)

Slide 70

Slide 70 text

DEMO

Slide 71

Slide 71 text

Open Issues • Required to Drop Support for Armv7 ld: section __bundle (address=0x06748000, size=735061037) would make the output executable exceed available address range for architecture armv7 • A bit longer build times even after switching to New Build System • Pods still add copy resources step in static lib • Incremental builds are not building Delta changes only

Slide 72

Slide 72 text

Takeaways • Performance is equally important as a feature

Slide 73

Slide 73 text

Takeaways • Performance is equally important as a feature • Should be a Continuous Process (Don’t wait till blast)

Slide 74

Slide 74 text

Takeaways • Performance is equally important as a feature • Should be a Continuous Process (Don’t wait till blast) • Measure what matters (part of CI?) • Using XCUITest

Slide 75

Slide 75 text

Takeaways • Performance is equally important as a feature • Should be a Continuous Process (Don’t wait till blast) • Measure what matters (part of CI?) • Using XCUITest • Automate things which developer won’t be touching • Deployment target, swift version, optimization level etc

Slide 76

Slide 76 text

Thank You • https://developer.apple.com/videos/play/wwdc2016/406/ • https://useyourloaf.com/blog/slow-app-startup-times/ • https://github.com/leavez/cocoapods-static-swift-framework • https://blog.automatic.com/how-we-cut-our-ios-apps- launch-time-in-half-with-this-one-cool-trick-7aca2011e2ea • https://medium.com/@kolyuchiy/a-record-breaking-story-of- boosting-the-launch-time-of-mail-rus-email-app-for- ios-59a266dd288a