Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Improving App Launch Time

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →

Improving App Launch Time

Presented by Prashant Rane

Avatar for Swift India

Swift India

January 25, 2020

More Decks by Swift India

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. What affects launch timing • Pre-Main • Measure using DYLD_PRINT_STATISTICS

    • dylib load time, rebase/binding, objc setup, Static Initializers
  4. 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
  5. 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
  6. What affects launch timing • iOS version, because DYLD version

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

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

    ships with OS • Battery Level (Power Saver mode) • Hardware Capacity • Binary Size
  9. App(Will)DidFinishedLaunching • Singleton initializations (Facebook, Firebase etc?) • DB connection

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

    / Core Data/ Model Layer Initialization • User Location? • Analytics? • Setup Key Window • … many more
  11. ViewRendering (RootVC) • Setup Navigation Container (e.g. TabBarVC) • Prepare

    Datasource/ViewModel/VIPER stack • Time require to layout any Complex View
  12. 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)
  13. Post-Main Use os_signpost for taking accurate measures of a method

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

    which is contributing to launch time. Ex: ViewDidLoad of RootViewController
  15. Dyld: The WorkHorse • Rebase • Adjust pointers inside image

    • Slid = actual_address - preferred_address
  16. 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%)
  17. Code Metric • 12000+ Types (Class, Struct, Enum…) • >

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

    8000 Swift files, ~1000 ObjC files (Pods) • >80 Dynamic Libraries (and growing)
  19. 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
  20. Development Silos • Team Structure: independent development • Abstract Target

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

    in Podfile to make it magical • Complex Inter Target dependancies • Messed up Build Settings per product
  22. Finding a solution • IDEAL: • Reduce internal (by merging)

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

    and external (building inhouse) framework count • Make dependancies as Static Frameworks and link that with Product
  24. 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) •
  25. 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?
  26. Fixing the problem • Find out dependancies in different targets

    • Create a Lite app version to compare app launch time
  27. 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
  28. 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
  29. Problems Faced • Splitting Framework into Static Libs and Resource

    Bundle (DEMO) • Custom script to copy all resource bundles to app
  30. 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”
  31. Problems Faced • Implicit dependancies not working; so as the

    Parallel builds • That needs updating all target’s linker flags
  32. Problems Faced • As Implicit Dependancies were off, we need

    to add all imports explicitly • A script to automate inserting some must have imports
  33. Problems Faced • IntentDefinition was not getting compiled correctly (autogen

    classes were not generated) • Copied the generated files manually
  34. 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
  35. 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
  36. 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)
  37. Problems Faced • A few Crashes due to wrong scheme

    selected for archive (for some products)
  38. 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
  39. Takeaways • Performance is equally important as a feature •

    Should be a Continuous Process (Don’t wait till blast)
  40. 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
  41. 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
  42. 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