Growing LINE for iOS

Growing LINE for iOS

Christopher Rogers
LINE App Dev Team4 Senior Software Engineer
https://linedevday.linecorp.com/jp/2019/sessions/C2-3

Be4518b119b8eb017625e0ead20f8fe7?s=128

LINE DevDay 2019

November 21, 2019
Tweet

Transcript

  1. 2019 DevDay Growing LINE for iOS > Christopher Rogers >

    LINE App Dev Team4 Senior Software Engineer
  2. Lines of Code 400,000 800,000 1,200,000 1,600,000 1.0.0 1.2.0 1.3.0

    1.3.4 1.6.1 2.0.2 2.3.1 3.1.0 3.2.1 3.5.0 3.6.5 3.8.2 3.9.3 4.0.1 4.3.1 4.5.0 4.7.1 4.9.1 5.0.2 5.2.1 5.4.0 5.8.0 5.10.0 6.1.1 6.4.0 6.6.1 6.8.5 6.9.2 7.1.2 7.3.0 7.5.0 7.8.0 7.12.0 7.15.0 8.0.0 8.2.1 8.4.1 8.6.0 8.9.0 8.12.1 8.16.1 8.19.0 9.0.2 9.5.0 9.7.1 9.15.1 9.18.0 Objective C Swift
  3. Agenda > Dependencies > Launch Time > Bridging Objective-C &

    Swift > Xcode Project Size > Build Times
  4. Dependencies

  5. Emerging Problems > Linker settings > Library updates > Search

    paths
  6. Ƃ CocoaPods > Builds/cleans alongside project > Integrates well with

    Xcode
  7. Swift

  8. Ƃ Carthage > Pre-builds frameworks > No integration with Xcode

    > Little to no configuration > Two dependency managers…
  9. Hard Problems in Computer Science Caching With Carthage > Remote

    build artifact caching with Rome > Cache poisoning > Local caches often broke
  10. Launch Time

  11. Dynamic Frameworks > Solution: make them static frameworks! > Downsides

    to static frameworks • Cannot bundle resources • Code reuse between executables results in increased binary size > Dynamic linking (usually) happens on launch
  12. Ƃ How To Create > Create dynamic framework > Mach-O

    type: Static Library > Do not embed > Resources
  13. Ƃ Mixing It Up > Public headers become part of

    module alongside Swift > Enable Clang modules if needed • CLANG_ENABLE_MODULES • DEFINES_MODULE • PRODUCT_MODULE_NAME
  14. Ƃ Public Headers > “Headers” Build Phase > File Inspector

    > Target Membership
  15. Ƃ Module Maps > MODULEMAP_FILE

  16. Ƃ Module Maps > MODULEMAP_FILE > umbrella makes directory contents

    part of module
  17. Ƃ Module Maps > MODULEMAP_FILE > umbrella makes directory contents

    part of module > export controls if imported modules are implicitly imported
  18. Launch Time 0 ms 2,000 ms 4,000 ms 6,000 ms

    8,000 ms 10,000 ms 8.4.0 8.5.0 20%ile 50%ile mean 80%ile
  19. Bridging Objective-C & Swift

  20. MyObjCSwiftClass+CategoryName.h Objective-C Categories @class MyObjCSwiftClass; @interface MyObjCSwiftClass (CategoryName)

  21. MyObjCSwiftClass+CategoryName.h Objective-C Categories // import generated header, which contains MyObjCSwiftClass

    #import "LINE-Swift.h" @interface MyObjCSwiftClass (CategoryName)
  22. Bridging Objective-C Categories on Swift Types To Swift > Circular

    dependency • Generated header (MyApp-Swift.h) • Bridging header (MyApp-Bridging-Header.h) Problematic Code
  23. > @objc(MyObjCSwiftClass) class MySwiftClass { } > @class MyObjCSwiftClass; can

    fail to bridge back to MySwiftClass (rare) > My fix (#27682) should land in Swift 5.2 Bridging Swift Classes Back To Swift via Objective-C Bridging Objective-C Categories on Swift Types To Swift > Circular dependency • Generated header (MyApp-Swift.h) • Bridging header (MyApp-Bridging-Header.h) Problematic Code
  24. Module Map Workaround

  25. LINE.modulemap Module Map Settings module LINE { header "LINE-Partial-Swift.h" }

  26. Build Settings Module Map Settings SWIFT_INCLUDE_PATHS = “$(PROJECT_DIR)/LINE/PartialModule"

  27. MyObjCSwiftClass+CategoryName.h Importing in Header // import generated header, which contains

    MyObjCSwiftClass #import "LINE-Swift.h" @interface MyObjCSwiftClass (CategoryName)
  28. MyObjCSwiftClass+CategoryName.h Importing in Header #ifdef __swift__ @import LINE; #else //

    import generated header, which contains MyObjCSwiftClass #import "LINE-Swift.h" #endif @interface MyObjCSwiftClass (CategoryName)
  29. Keeping in Sync { "MessageViewController.swift": [“MessageViewController”], "ShareActivity.swift": ["ShareActivity"], }

  30. Xcode Project Size

  31. Merge Conflicts > Requires understanding Xcode’s project file format Xcode

    Project Size
  32. > Was this a part of the target template? >

    Why was this setting needed? Understanding What Has Been Configured Merge Conflicts > Requires understanding Xcode’s project file format Xcode Project Size
  33. > Was this a part of the target template? >

    Why was this setting needed? Understanding What Has Been Configured Slow > Modifying anything was painfully slow Merge Conflicts > Requires understanding Xcode’s project file format Xcode Project Size
  34. Ƃ XcodeGen > YAML config file > Can write comments

    > Less conflicts > 88k lines of Xcode configuration are now ~1,200 lines of YAML. > Can’t use Xcode to edit project
  35. Build Times

  36. > Xcode sometimes decides to rebuild everything > Incremental builds

    in Swift sometimes too coarse grained Tooling Problems CocoaPods > Unnecessarily rebuilt after cleaning Generated Header (LINE-Swift.h) > Nearly a clean build when modified Problem Areas
  37. Precompiling Generated Header > Mileage may vary > Usually not

    necessary nor recommended Quick Improvements
  38. > Extended type gets marked dirty when extension modified >

    Files marked dirty will propagate dirty flag to all of its extensions > OTHER_SWIFT_FLAGS = -driver-show-incremental Limiting Access to Swift Extensions Precompiling Generated Header > Mileage may vary > Usually not necessary nor recommended Quick Improvements
  39. And Incremental Compilation Swift Extensions class MyClass { func foo()

    { IndexSet().bar() } } extension IndexSet { func bar() {} }
  40. And Incremental Compilation Swift Extensions class MyClass { func foo()

    { IndexSet().bar() } } extension IndexSet { fileprivate func bar() {} }
  41. And Incremental Compilation Swift Extensions class MyClass { func foo()

    { IndexSet().bar() } } extension IndexSet { func bar() {} }
  42. > Including dependencies managed by CocoaPods & Carthage Build &

    Cache Modules With Bazel Split Up Codebase Into Modules > Currently 224 modules > Categorize modules Long-Term Approach
  43. > Specific category for extension-safe modules Code Size for App

    Extensions Understanding Dependency Graph > What is OK to import? > Where to put new code? Module Categories
  44. > Restriction: Cannot import feature-private modules > Small, focused >

    Includes feature-public modules Others Module Naming Convention > Module categories have set prefixes and/or suffixes Feature-Private (Feature) > Restriction: Cannot import other features’ private modules > Example features: Messaging, Timeline, Calls, Pay Module Categories
  45. Ƃ ImportLint

  46. Benefits of Bazel Remote Caching Reproducible Fast

  47. > Reading Package.swift for build settings Build Code From SwiftPM

    in Bazel Replace CocoaPods & Carthage With SwiftPM for Dependency Resolution > Using swift package resolve Building With Bazel
  48. Replace CocoaPods & Carthage With SwiftPM for Dependency Resolution >

    Done, but removed SwiftPM due to reliability issues (mostly with CI builds) (SR-10718) > Now Bazel is downloading hard-coded dependencies Current Status
  49. > Manually created Bazel BUILD files for all dependencies Build

    Code From SwiftPM in Bazel Replace CocoaPods & Carthage With SwiftPM for Dependency Resolution > Done, but removed SwiftPM due to reliability issues (mostly with CI builds) (SR-10718) > Now Bazel is downloading hard-coded dependencies Current Status
  50. > Manually created Bazel BUILD files for all dependencies Build

    Code From SwiftPM in Bazel Bonus: Move Code Generation to Bazel > Mostly done > No longer have to codegen unnecessarily Replace CocoaPods & Carthage With SwiftPM for Dependency Resolution > Done, but removed SwiftPM due to reliability issues (mostly with CI builds) (SR-10718) > Now Bazel is downloading hard-coded dependencies Current Status
  51. Clean Build Times No Cache With Cache 0 6m 12m

    18m 24m 30m Bazel Xcode
  52. Improve Developer Experience > Code viewer & breakpoints working in

    Xcode with Tulsi > Code completion is not complete > Making modifications to code built by Bazel not great Remaining Issues
  53. > Split up codebase into more modules > Needed for

    true gains in incremental build times Build More in Bazel Improve Developer Experience > Code viewer & breakpoints working in Xcode with Tulsi > Code completion is not complete > Making modifications to code built by Bazel not great Remaining Issues
  54. > Split up codebase into more modules > Needed for

    true gains in incremental build times Build More in Bazel Cache Reusability > Some differences between machines prevent build cache reuse > Mostly resolved Improve Developer Experience > Code viewer & breakpoints working in Xcode with Tulsi > Code completion is not complete > Making modifications to code built by Bazel not great Remaining Issues
  55. Thank You Also Check Out 15:30-15:40 Short Track - “Faster

    iOS Builds With Bazel”