$30 off During Our Annual Pro Sale. View Details »

大規模なアプリのマルチモジュール構成の実践

giginet
PRO
September 19, 2021

 大規模なアプリのマルチモジュール構成の実践

giginet
PRO

September 19, 2021
Tweet

More Decks by giginet

Other Decks in Technology

Transcript

  1. ࣗݾ঺հ
    • @giginet


    • iOSςοΫϦʔυ@ Cookpad


    • ίϛολʔ fastlane/XcodeGen/CarthageͳͲ


    • ٕज़ސ໰ @ taskey, MoneyForward

    View Slide

  2. View Slide

  3. େن໛ͳΞϓϦͷϚϧνϞ
    δϡʔϧߏ੒ͷ࣮ફ
    iOSDC 2021 9/19 Track A


    @giginet
    iOSDC 2021

    View Slide

  4. ͜ͷτʔΫʹ͍ͭͯ
    • ୈ1ষɿ2019೥ࠒ͔ΒΫοΫύουϨγϐΞϓϦͷϚϧνϞδϡʔϧ
    ԽΛਐΊ͖ͯͯ͠͹Β͘ܦͬͨͷͰݱঢ়Λ঺հ


    • ୈ2ষɿଞͷϓϩδΣΫτ͕େن໛ͳϦΞʔΩςΫνϟΛ࣮ફ͢Δʹ
    ͸Ͳ͏ͨ͠ΒΑ͍͔

    View Slide

  5. 5
    View
    Model
    ViewController
    🔥ϩδοΫΛ࣋ͭ7JFX
    XXXManager
    🔥γϯάϧτϯͷσʔλιʔε
    🔥.BTTJWF7JFX$POUSPMMFS
    🔥ڞ௨Խ͞ΕͯංେԽͨ͠Ϟσϧ

    🔥σουίʔυ
    🔥ີ݁߹
    🔥7JFXͷܧঝɺ࢖͍ճ͠
    🈲
    🔥৮ͬͯ͸
    ͍͚ͳ͍΍ͭ
    🌀
    🔥ࠞಱ
    ϚϧνϞδϡʔϧԽҎલ

    View Slide

  6. ΫοΫύουΞϓϦ
    • 50 Releases / year


    • 15 developers / release


    • 40 ~ 50 PR / release


    • 328,000 lines

    View Slide

  7. 2019೥ࠒͷঢ়گͱ໨త
    • ։ൃ࣌ͷϏϧυ଴ͪΛݮΒ͍ͨ͠


    • ࠩ෼Ϗϧυʹ΋1ʙ2෼ͷ଴ͪ࣌ؒ


    • ػೳؒͷґଘ΍෭࡞༻ΛݮΒ͍ͨ͠


    • ςετهड़Λ؆ུԽ͍ͨ͠


    • ObjCͳͲͷݹ͍࣮૷ΛӅṭ͍ͨ͠

    View Slide

  8. View Slide

  9. • https://fortee.jp/iosdc-japan-2021/proposal/6b6fb68b-a45a-4878-a8b9-0dd5ed90fc39
    ΫοΫύουͷΤϯδχΞ͕ޠΔɺڊେͰྺ࢙͋ΔΞϓϦʹ͓͚Δഁ
    յͱ૑଄ - ϩάϛʔTech
    https://logmi.jp/tech/articles/321186

    View Slide

  10. ΞʔΩςΫνϟ
    • ΫϦʔϯΞʔΩςΫνϟ͸͋Δఔ౓ಋೖࡁΈʢಋೖ͞Ε͍ͯͳ͍ͱ͜
    Ζ΋͋ͬͨʣ


    • VIPERΛ࠾༻ɺΞϓϦέʔγϣϯ૚ͱυϝΠϯ૚ʹ෼͔ΕΔ


    • ֤ը໘ΛVIPERγʔϯͱݺͿ

    View Slide

  11. View Slide

  12. Feature A Feature B Feature C
    7*1&3γʔϯ

    View Slide

  13. ݱࡏͷΫοΫύουΞϓϦ

    View Slide

  14. CookpadCore
    Cookpad
    ɾɾɾ
    Cookpad Tsukuru
    Feature A Feature B Feature C
    🔥

    View Slide

  15. CookpadCore
    Cookpad
    ɾɾɾ
    Cookpad Tsukuru
    Feature A Feature B Feature C
    🔥
    $PSFϞδϡʔϧ
    ந৅ԽͷͨΊͷΠϯλʔϑΣΠε
    ɾΞϓϦ಺Ͱڞ௨ͯ͠࢖͏6*ίϯϙʔ
    ωϯτͷఏڙ

    View Slide

  16. CookpadCore
    Cookpad
    ɾɾɾ
    Cookpad Tsukuru
    Feature A Feature B Feature C
    🔥
    'FBUVSF.PEVMF
    ڞ௨ͷυϝΠϯ૚Λ͍͔࣋ͭͭ͘ͷ
    7*1&3γʔϯΛଋͶͨϞδϡʔϧ

    View Slide

  17. CookpadCore
    Cookpad
    ɾɾɾ
    Cookpad Tsukuru
    Feature A Feature B Feature C
    🔥
    7*1&3γʔϯ
    'FBUVSF.PEVMF

    View Slide

  18. CookpadCore
    Cookpad
    ɾɾɾ
    Cookpad Tsukuru
    Feature A Feature B Feature C
    🔥
    ΞϓϦέʔγϣϯ
    "QQMJDBUJPO5BSHFUʹ૬౰
    શͯͷґଘΛ஌ΕΔ

    View Slide

  19. CookpadCore
    Cookpad
    ɾɾɾ
    Cookpad Tsukuru
    Feature A Feature B Feature C
    🔥
    $PPLQBE
    7*1&3Խ͞Ε͍ͯΔ͕ϞδϡʔϧԽ͞
    Ε͍ͯͳ͍γʔϯͷू߹

    View Slide

  20. CookpadCore
    Cookpad
    ɾɾɾ
    Cookpad Tsukuru
    Feature A Feature B Feature C
    🔥
    5TVLVSV
    ࠞಱ

    View Slide

  21. CookpadCore
    Cookpad
    ɾɾɾ
    Cookpad Tsukuru
    Feature A Feature B Feature C
    🔥

    View Slide

  22. Feature A Feature B

    Feature Moduleؒ͸ґଘΛ࣋ͯͳ͍
    ॥؀ࢀরΛආ͚ΔͨΊ


    Feature Aͷ࣋ͭγʔϯ͔ΒFeature Bͷ࣋ͭ
    γʔϯʹભҠ͍ͨ͠৔߹ͳͲʹࠔΔ

    View Slide

  23. CookpadCore
    Feature A Feature B
    Environment
    Cookpad
    ᶃ&OWJSPONFOU΁ͷ໰͍߹Θͤ
    ᶄґଘͷ஫ೖ
    ᶅґଘͷऔಘ

    View Slide

  24. Environment
    • શͯͷґଘΛ࣋ͭProtocol


    • CoreϞδϡʔϧʹ͍Δʢ=͢΂͔ͯΒࢀরՄೳʣ


    • ΞϓϦέʔγϣϯλʔήοτͰ࣮૷ͯ͠ґଘΛ஫ೖ


    • ֤γʔϯ͸ੜ੒࣌ʹEnvironmentΛ΋Β͏


    • ෭࡞༻΍ґଘʹ͸Environmentܦ༝Ͱ৮Δ͜ͱͰɺ؀ڥʹΑͬͯڍಈ
    Λࠩ͠ସ͑Δ͜ͱ͕Ͱ͖Δ

    View Slide

  25. import Foundation
    import UIKit
    public protocol Environment
    {

    var client: ServiceClient { get
    }

    var pvLogger: PVLogger { get
    }

    var userFeatures: UserFeatures { get
    }

    var activityLogger: ActivityLogger { get
    }

    //


    }

    CookpadCore
    ґଘΛQSPUPDPMԽ

    View Slide

  26. final class CookpadEnvironment: Environment
    {

    var client: ActivityLogger
    {

    CookpadActivityLogger(
    )

    }

    //


    }

    Cookpad
    ΞϓϦέʔγϣϯλʔήοτͰ
    ۩ମతͳ࣮૷Λฦ͢

    View Slide

  27. environment.activityLogger.post(eventName: “tap_button”)
    Feature A
    'FBUVSF.PEVMF͔Β
    FOWJSPONFOUܦ༝Ͱར༻Ͱ͖Δ

    View Slide

  28. 28
    Environment
    CookpadEnvironment StubbableEnvironment
    Cookpad Tests
    Network Logger ը໘ભҠ

    View Slide

  29. let viewController = RecipeDetails.RecipeDetailsViewController(recipeID:
    42
    )

    present(viewController, animated: true, completion: nil
    )

    Feature A
    3FDJQF%FUBJMTϞδϡʔϧ಺ͷ
    γʔϯ͸'FBUVSF"Ϟδϡʔϧ͔
    Β͸ࢀরͰ͖ͳ͍

    View Slide

  30. DescriptorͱResolver
    • Environment͕࣋ͭґଘղܾͷ࢓૊Έ


    • Descriptor͸ͦͷ࣮૷Λࣔ͢ϚʔΧʔ


    • CoreϞδϡʔϧʹ͍Δɻʢ=͢΂͔ͯΒࢀরՄೳʣ


    • EnvironmentʹDescriptorΛ౉ͯ͠ɺܕফڈ͞Ε࣮ͨଶΛ΋Βͬͯ͘
    Δ࢓૊Έ = ResolverͱݺΜͰ͍Δ

    View Slide

  31. import Foundation
    import UIKit
    public protocol Environment
    {

    var client: ServiceClient { get
    }

    var pvLogger: PVLogger { get
    }

    var userFeatures: UserFeatures { get
    }

    var activityLogger: ActivityLogger { get
    }

    //


    func resolve(_ descriptor: Descriptor) ->
    Descriptor.Outpu
    t

    }

    CookpadCore
    3FTPMWFS
    %FTDSJQUPSΛड͚औͬͯ
    ࣮૷Λฦ͢ϝιου
    %FTDSJQUPS
    ࣮૷Λࢦࣔ͢͠ϚʔΧʔ

    View Slide

  32. public struct RecipeDetailsDescriptor: TypedDescriptor
    {

    public typealias Output = UIViewControlle
    r

    public var recipeID: Int6
    4

    public init(recipeID: Int64)
    {

    self.recipeID = recipeI
    D

    }

    }

    CookpadCore %FTDSJQUPS
    ࣮૷Λࢦࣔ͢͠ϚʔΧʔ

    View Slide

  33. final class CookpadEnvironment: Environment
    {

    public func resolve(_ descriptor: Descriptor)


    -> Descriptor.Output
    {

    switch descriptor
    {

    case let d as ViewDescriptor.RecipeDetailsDescriptor
    :

    return RecipeDetailsViewBuilder.build(with: d, environment: self)


    as! Descriptor.Outpu
    t

    }

    }

    }

    Cookpad
    %FTDSJQUPS͝ͱʹ࣮૷Λฦͯ͠
    ΞοϓΩϟετ͢Δ

    View Slide

  34. let viewController: UIViewController =
    environment.resolve(ViewDescriptor.RecipeDetailsDescriptor(recipeID: 42)
    )

    viewController.present(viewController, animated: true, completion: nil
    )

    Feature A
    3FTPMWFSܦ༝ͰऔΓग़͢͜ͱͰɺผͷϞδϡʔϧ
    ಺ͷγʔϯ΋6*7JFX$POUSPMMFSͱͯ͠औಘͰ͖Δ

    View Slide

  35. View Slide

  36. CookpadCore
    FeatureASandbox
    Feature A
    SandboxΞϓϦ

    View Slide

  37. SandboxΞϓϦ
    ΞϓϦશମ

    View Slide

  38. ػೳ͝ͱʹಈ࡞͢ΔϛχΞϓϦͰϓϨϏϡʔαΠΫϧΛര଎ʹͨ͠࿩
    https://fortee.jp/iosdc-japan-2021/proposal/6b6fb68b-a45a-4878-a8b9-0dd5ed90fc39

    View Slide

  39. ϚϧνϞδϡʔϧԽͷޮՌ
    • ϛχΞϓϦ(Sandbox)ͷ੔උʹΑΔ։ൃମݧ(DX)ͷ޲্


    • Feature ModuleؒͷӨڹΛݮΒͤͨ


    • ಛఆϞδϡʔϧ಺ͰͷΈͷSwiftUIಋೖͳͲɺϞδϡʔϧ಺Ͱͷ௅ઓ͕
    ͠΍͘͢ͳͬͨ

    View Slide

  40. ·ͱΊ
    • Ϟδϡʔϧ෼཭։࢝લͷ໨తͷఆٛ͸ॏཁ


    • ໨త͕ఆ·͍ͬͯΕ͹ࣗͣͱ෼཭ํ๏͕ܾ·Δ


    • վળͷঢ়گΛఆྔԽͯ͠؂ࢹ


    • ґଘղܾͷ࢓૊Έ͸೉͍͠


    • ࣮ࡍʹ෼཭͕ਐΜͩΒ͔֬ʹޮՌ͕͋ͬͨʢΑ͔ͬͨʣ

    View Slide

  41. ࣮ફతϚϧνϞδϡʔϧ

    View Slide

  42. Ϟδϡʔϧ෼཭Λਁಁͤ͞Δʹ͸
    • શͯΛҰ౓ʹҠߦ͠ͳ͍


    • ݀Λ࠹͙


    • ։ൃऀ͕໎͏఺ΛݮΒ͢

    View Slide

  43. શͯΛҰ౓ʹҠߦ͠ͳ͍
    ෦෼తͳҠߦΛҙࣝͨ͠ઃܭ

    View Slide

  44. ෦෼తͳҠߦΛҙࣝͨ͠ઃܭ
    • ݹ͍࣮૷ΛҠߦ͠ͳͯ͘΋৽ن࣮૷ͷΈΛϞδϡʔϧ෼཭Ͱ͖ΔΑ͏
    ʹҙࣝͯ͠ઃܭ


    • protocolʹΑΔந৅Խ


    • protocolΛऔΓग़ͤΔResolver

    View Slide

  45. ྫ1ɿݹ͍γϯάϧτϯͷ࣮૷ͷར༻
    • ݹ͍࣮૷͸protocolΛCoreϞδϡʔϧʹఆٛ͠ɺFeature Module͔
    Β͸protocolͱͯ͠ར༻͢Δ


    • ྫ͑͹LegacyActivityLogger.sharedΛந৅Խͯ͠environment͔Βఏ
    ڙ͢Δ


    • ͜͏͢Δ͜ͱͰɺLegacyActivityLoggerΛมߋͤͣʹ֤γʔϯ͔Β͸
    ࣮૷ΛӅṭͰ͖Δ

    View Slide

  46. import Foundation
    public protocol ActivityLogger
    {

    func post(eventName: String, payload: [String: Any]?
    )

    }

    public protocol Environment {
    var activityLogger: ActivityLogger { get
    }

    //


    }

    CookpadCore

    View Slide

  47. import CookpadCor
    e

    private struct CookpadActivityLogger: ActivityLogger
    {

    func post(eventName: String, payload: [String: Any]?)
    {

    LegacyActivityLogger.shared.post(eventName: eventName,


    payload: payload
    )

    }

    }

    final class CookpadEnvironment: Environment
    {

    var activityLogger: ActivityLogger
    {

    return CookpadActivityLogger(
    )

    }

    }

    Cookpad ΞϓϦέʔγϣϯλʔήοτ͔
    Βಈ͔ͤͳ͍ݹ͍࣮૷Λϥοϓ
    ͢Δ

    View Slide

  48. environment.activityLogger.post(eventName: “search”
    ,

    payload: ["keyword": "όλʔείονγφϞϯύΠ"]
    )

    Feature A
    'FBUVSF.PEVMF͔Β
    FOWJSPONFOUܦ༝Ͱར༻Ͱ͖Δ

    View Slide

  49. ྫ2ɿݹ͍ViewController΁ͷભҠ
    • Resolverͷ࢓૊Έ͸ݹ͍࣮૷ͷӅṭʹ΋໾ཱͭ


    • ΫϦʔϯΞʔΩςΫνϟԽ͞Ε͍ͯͳ͍ݹ͍ը໘͕͋ͬͨͱͯ͠΋ɺ
    ResolverΛ࣮૷͢Ε͹৽͍࣮͠૷͔Βಉ͡Α͏ʹར༻Ͱ͖Δ


    • ݹ͍ը໘͸Ϟδϡʔϧ෼཭͠ͳͯ͘΋ɺΞϓϦέʔγϣϯλʔήοτ
    ͔Β஫ೖ͢Ε͹ྑ͍

    View Slide

  50. final class LegacyViewBuilder: ViewBuilder
    {

    func build(environment: Environment) -> UIViewController
    {

    // VIPERԽ͞Ε͍ͯͳ͍Objective-CͰ࣮૷͞Εͨݹ͍ViewController
    let legacyViewController = CKDLegacyViewController(
    )

    return legacyViewControlle
    r

    }

    }

    Cookpad
    ΫϦʔϯΞʔΩςΫνϟԽ͞Ε͍ͯͳ͍ݹ͍ը໘΋
    7JFX#VJMEFSͰϥοϓ͢Δͱ3FTPMWFSܦ༝ͰऔಘͰ͖Δ

    View Slide

  51. ৽ن࣮૷͕࣮֬ʹҠߦ͞ΕΔΑ͏ʹ͢Δ
    ݀Λ࠹͙

    View Slide

  52. ৽ن࣮૷͕࣮֬ʹҠߦ͞ΕΔΑ͏ʹ͢ΔͨΊʹ
    • ݹ͍࣮૷͸Ұ୴ఘΊΔ


    • ৽͍࣮͠૷ʹ͍ͭͯίʔυੜ੒ػͳͲΛ४උ͠ɺ؆୯ʹϞδϡʔϧ͕
    ੜ੒Ͱ͖ΔΑ͏ʹ͢Δ

    View Slide

  53. View Slide

  54. View Slide

  55. Ϟδϡʔϧͷੜ੒
    • yonaskolb/XcodeGenΛར༻


    • XcodeGenͷTarget TemplateػೳͰॊೈʹTargetΛ૿΍ͤΔ


    • XcodeGenͷProject SpecΛੜ੒͢Δ࢓૊Έ(XcodeGenGen)

    View Slide

  56. targetTemplates
    :

    FeatureModule
    :

    platform: iOS
    type: framework
    sources
    :

    - ${frameworkName}
    settings
    :

    base
    :

    PRODUCT_BUNDLE_IDENTIFIER: com.cookpad.${frameworkName}
    MACH_O_TYPE: staticlib
    info
    :

    path: ${frameworkName}/Info.plist
    dependencies
    :

    - sdk: Foundation.framework
    - sdk: UIKit.framework
    - target: CookpadCor
    e

    prebuildScripts
    :

    - name: SwiftLint
    path: "configs/scripts/swift_lint_module.sh"
    ม਺

    View Slide

  57. --
    -

    targets
    :

    MyAwesomeModule
    :

    templates
    :

    - FeatureModule
    templateAttributes
    :

    frameworkName: MyAwesomeModule
    ม਺
    ܧঝݩͷ
    ςϯϓϨʔτ

    View Slide

  58. γʔϯͷੜ੒
    • yonaskolb/GenesisΛར༻


    • ςϯϓϨʔτ͔Βίʔυੜ੒Λߦ͏ϢʔςΟϦςΟ


    • ؆୯ͳઃఆϑΝΠϧͰର࿩తͳΠϯλʔϑΣΠεΛ࡞ΕΔ

    View Slide

  59. import CookpadCore
    import UIKit
    public final class {{ name }}ViewController: UIViewController, {{ name }}ViewContract
    {

    private var presenter: {{ name }}PresenterContract
    !

    private let environment: Environmen
    t

    init(environment: Environment)
    {

    self.environment = environmen
    t

    super.init(nibName: nil, bundle: nil
    )

    }

    @available(*, unavailable
    )

    required init?(coder: NSCoder)
    {

    fatalError("init(coder:) has not been implemented"
    )

    }

    func inject(presenter: {{ name }}PresenterContract)
    {

    self.presenter = presente
    r

    }

    override public func viewDidLoad()
    {

    super.viewDidLoad(
    )

    // Insert code here to connect presenter
    }

    ม਺

    View Slide

  60. # name must be given as options to Genesis
    options
    :

    - name: name
    question: VIPER scene name?
    description: new VIPER scene name to generate(e.g. RecipeDetails).
    type: string
    required: true
    - name: module
    question: Destination target?
    description: Target destination name to generate new VIPER scene. (e.g.
    RecipeDetails)
    type: string
    required: true
    files
    :

    - template: VIPER/FeatureModule/Regular/Contract.stencil
    path: "{{ module }}/Application/{{ name }}/{{ name }}Contract.swift"
    - template: VIPER/FeatureModule/Regular/ViewController.stencil
    path: "{{ module }}/Application/{{ name }}/{{ name }}ViewController.swift"
    - template: VIPER/FeatureModule/Regular/Wireframe.stencil
    path: "{{ module }}/Application/{{ name }}/{{ name }}Wireframe.swift



    ࣭໰ͷ౴͑Λม਺ʹ֨ೲͰ͖Δ
    ͲͷςϯϓϨʔτΛͲ͜ʹੜ੒͢Δ͔ࢦఆ

    View Slide

  61. ։ൃऀ͕໎͏఺ΛݮΒ͢
    ৘ใͷڞ༗ͱؒҧ͑ͳ͍࢓૊ΈԽ

    View Slide

  62. ։ൃऀ͕໎͏఺ΛݮΒ͢
    • υΩϡϝϯτ΍τϥϒϧγϡʔςΟϯάͳͲͷϦιʔεΛ༻ҙ͢Δ


    • มߋ͕͋ͬͨ৔߹௥ै͢Δ


    • มߋʹ͍ͭͯɺࣾ಺ϒϩάͳͲͰ௨஌͢Δ


    • ษڧձ΍࠲ஊձͳͲΛ௨ͯ͠՝୊Λ἞Έ্͛Δ


    • ೔ʑͷϨϏϡʔ

    View Slide

  63. View Slide

  64. View Slide

  65. ϕετϓϥΫςΟεͷఏڙ
    • ࣮૷࣌ʹఆ͕ٛ͋΍;΍ͱͳͬͯ͠·͏఺ʹ͍ͭͯɺݸผʹϕετϓ
    ϥΫςΟεΛఏڙ͢Δ


    • Ͳ͜ͷϞδϡʔϧʹ΋ஔ͚Δ࣮૷ΛͲ͜ʹ͓͘΂͖͔


    • ༷ʑͳґଘղܾͷखஈ͕͋Δͱ͖ʹͲͷΑ͏ͳखஈΛ࠾༻͢΂͖͔

    View Slide

  66. ࢓૊ΈʹΑΔϦΞʔΩςΫνϟͷਪਐ
    • ΠϯηϯςΟϒͷઃܭ


    • Ϟδϡʔϧ෼཭͕ਐΉͱ୯ମϏϧυ͕ૣ͘ͳͬͯ։ൃ͠΍͘͢ͳΔ
    ͱݴͬͨϝϦοτΛଧͪग़͢


    • δΣωϨʔλ΍ϝλϓϩάϥϛϯά


    • ੩తղੳ

    View Slide

  67. ϝλϓϩάϥϛϯάͷར༻
    • ίʔυੜ੒ʢϝλϓϩάϥϛϯάʣΛར༻Ͱ͖ΔϲॴͰ͸ར༻͢Δ


    • ࣮૷ͷ܁Γฦ͕͠ආ͚ΒΕɺҠߦ͕͠΍͘͢ͳΔ


    • ఆܕ͔Β֎Ε࣮ͨ૷͕Ͱ͖ͳ͘ͳͬͨΓ


    • ࣮૷๨ΕΛίϯύΠϧ࣌ʹݕग़Ͱ͖ΔΑ͏ʹͰ͖Δ

    View Slide

  68. ྫ1: Resolverͷੜ੒ࣗಈԽ
    • ઌ΄ͲݟͨDescriptor͔ΒViewControllerΛresolve͢Δ࣮૷Λϝλϓ
    ϩάϥϛϯάʹΑͬͯࣗಈԽ


    • ࣅͨΑ͏ͳίʔυͷ܁Γฦ͠Λຖճ࣮૷͠ͳͯ͘ྑ͘ͳͬͨ

    View Slide

  69. final class CookpadEnvironment: Environment
    {

    public func resolve(_ descriptor: Descriptor)


    -> Descriptor.Output
    {

    switch descriptor
    {

    case let d as ViewDescriptor.RecipeDetailsDescriptor
    :

    return RecipeDetailsViewBuilder.build(with: d, environment: self)


    as! Descriptor.Outpu
    t

    }

    }

    }

    Cookpad
    ࣮૷๨Ε͕͋ΔͱΫϥογϡ͢Δ
    ୯७ʹຖճ࣮૷͢Δͷ͕໘౗
    ͜͜ʹෳࡶͳॲཧ͕ॻ͚ͯ͠·͏



    View Slide

  70. // ࣗಈੜ੒͢ΔΑ͏ʹͨ͠
    final class CookpadEnvironment: Environment
    {

    public func resolve(_ descriptor: Descriptor)


    -> Descriptor.Output
    {

    switch descriptor
    {

    case let d as ViewDescriptor.RecipeDetailsDescriptor
    :

    return RecipeDetailsViewBuilder.build(with: d, environment: self)


    as! Descriptor.Outpu
    t

    }

    }

    }

    Cookpad
    શέʔε͕໢ཏ͞ΕΔΑ͏ʹ
    ຖճ࣮૷͠ͳͯ͘ྑ͍
    ఆܗ֎ͷ࣮૷͕ෆՄೳʹ

    View Slide

  71. ͜ͷ࢓૊Έͷར఺
    • ໢ཏੑͷ୲อ


    • શDescriptorͷResolver͕͋Δ͜ͱΛίϯύΠϧ࣌ʹอূͰ͖Δ


    • ن໿΁ͷద߹ͷอূ


    • ResolverͷதͰҙਤ͠ͳ͍ڍಈΛ࣮૷͢ΔͳͲ͕Ͱ͖ͳ͘ͳΓɺΠϯ
    λʔϑΣΠε͕ἧ͑ΒΕΔ


    • লྗԽ


    • ։ൃऀ͸DescriptorͱViewBuilderΛ༻ҙ͢Δ͚ͩͰྑ͍

    View Slide

  72. ίʔυੜ੒Λ༻͍ͨiOSΞϓϦϚϧνϞδϡʔϧԽͷͨΊͷґଘղܾ
    https://techlife.cookpad.com/entry/2021/06/16/110000

    View Slide

  73. ྫ2: ϞοΫͷࣗಈੜ੒
    • CoreϞδϡʔϧʹ͍ΔProtocolͷϞοΫΛࣗಈੜ੒͢Δ


    • Ϣχοτςετ΍SandboxΞϓϦͰμϛʔͷ஋Λྲྀ͠ࠐΈ͍ͨͱ͖ʹ
    ༗༻


    • krzysztofzablocki/SourceryͰ࣮ݱ͍ͯ͠Δ


    • ࠷ۙ͸uber/mockoloͱ͍͏΍͕ͭ͋Γஔ͖׵͑ΒΕͦ͏

    View Slide

  74. protocol RecipeSearchDataSource
    {

    func searchRecipe(keyword: String) -> Single<[Recipe]>
    }

    CookpadCore

    View Slide

  75. class MockRecipeSearchDataSource: RecipeSearchDataSource
    {

    lazy var searchRecipeReturnValue: Single<[Recipe]> = { fatalError("\
    (#line)") }(
    )

    // MARK: - searchRecipe
    var searchRecipeCallsCount = 0
    var searchRecipeCalled: Bool
    {

    return buildCallsCount > 0
    }

    func searchRecipe(keyword: String) -> Single<[Recipe]>
    {

    searchRecipeCallsCount += 1
    return searchRecipeReturnValu
    e

    }

    }

    let dataStore = MockRecipeSearchDataSource(
    )

    dataStore.searchRecipeReturnValue = .just([recipe0, recipe1]
    )

    dataStore.searchRecipe(keyword: “φΠεΫϦʔϜ”
    )

    View Slide

  76. ͦͷଞͷ޻෉
    • Interface Builderͷ੩తղੳΛߦ͏


    • IBDecodable/IBLinter


    • ϞδϡʔϧҠߦͷͨΊͷεΫϦϓτ੔උ

    View Slide

  77. Ϟδϡʔϧ෼཭ͷਁಁͷͨΊʹͰ͖Δ͜ͱ
    • ͍ͬ΃ΜʹҠߦ͢Δͷ͸೉͍͠


    • ৽͍࣮͠૷ͷΈʹ͍ͭͯҠߦ͠΍͍͢Α͏ʹ͢Δ


    • ݹ͍࣮૷Λޙճ͠ʹͰ͖Δઃܭ


    • ϝϯόʔͷڠྗΛऔΓ෇͚Δ޻෉


    • ਓखΛ࢖Θͳͯ͘΋Ҡߦ͕ਐΉ࢓૊Έ࡞Γ


    • ίʔυੜ੒ɺlinterɺ ੩తղੳ

    View Slide

  78. ·ͱΊ
    • ΫοΫύουͰͷϚϧνϞδϡʔϧԽͷ͜Ε·Ͱͱݱঢ়


    • ଞͷϓϩδΣΫτ΁ͷదԠͷҰॿͱͳΕ͹޾͍Ͱ͢


    • աڈͷࢿྉΛࢀর௖͍ͨΓɺask the speakerͰ࿩͠·͠ΐ͏


    • Twitter : @giginet ·Ͱ͓ؾܰʹ

    View Slide

  79. ࢀߟࢿྉ
    • ʙբ͕ؔʙ ΫοΫύουiOSΞϓϦͷഁյͱ૑଄ɺͦͯ͠ະདྷ


    • https://techconf.cookpad.com/2019/kohki_miki.html


    • ΫοΫύουͷΤϯδχΞ͕ޠΔɺڊେͰྺ࢙͋ΔΞϓϦʹ͓͚Δഁ
    յͱ૑଄ - ϩάϛʔTech


    • https://logmi.jp/tech/articles/321186

    View Slide

  80. • XcodeGenʹΑΔ৽࣌୅ͷiOSϓϩδΣΫτ؅ཧ


    • https://techlife.cookpad.com/entry/2019/04/26/110000


    • େن໛ͳiOSΞϓϦͷը໘։ൃΛޮ཰Խ͢ΔͨΊʹಈ࡞֬ೝ༻ϛχΞ
    ϓϦΛߏங͢Δ


    • https://techlife.cookpad.com/entry/2020/08/05/090000


    • ίʔυੜ੒Λ༻͍ͨiOSΞϓϦϚϧνϞδϡʔϧԽͷͨΊͷґଘղܾ


    • https://techlife.cookpad.com/entry/2021/06/16/110000

    View Slide

  81. • 5/26 Cookpad Lounge #3 ʮΫοΫύου iOS ΞϓϦΛര଎Ͱ։ൃͰ
    ͖ΔΑ͏ʹ͢Δ࿩ʯ


    • https://www.youtube.com/watch?v=hqWiOeRyZ94


    • 6/16 iOS Tech Talk ʙ Multi module ઓུ࠲ஊձ ʙ


    • https://www.youtube.com/watch?v=5p6h5yiQ2PQ


    • 7/2 iOS Tech Talk ʙ Multi module ઓུ࠲ஊձ vol.2


    • https://www.youtube.com/watch?v=glpfnnDDaz8

    View Slide

  82. એ఻
    • After Party iOSDC Japan 2021


    • https://cookpad.connpass.com/event/222056/

    View Slide

  83. ͝ਗ਼ௌ


    ͋Γ͕ͱ͏͍͟͝·ͨ͠
    @giginet

    View Slide