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

Jetpack ComposeとGraphQLによるServer Driven UI/jetpackcompose-grahpql-serverdrivernui

sonatard
October 10, 2021

Jetpack ComposeとGraphQLによるServer Driven UI/jetpackcompose-grahpql-serverdrivernui

関連資料

宣言的UIの状態管理とアーキテクチャSwiftUIとGraphQLによる実践
https://speakerdeck.com/sonatard/swiftui-graphql

宣言的UI
https://speakerdeck.com/sonatard/xuan-yan-de-ui

sonatard

October 10, 2021
Tweet

More Decks by sonatard

Other Decks in Programming

Transcript

  1. 1
    +FUQBDL$PNQPTFͱ(SBQI2-ʹΑΔ
    4FSWFS%SJWFO6*
    %SPJE,BJHJ
    !TPOBUBSEͦͳଠ

    View Slide

  2. Appify Technologie
    s

    CT
    O

    ͦͳଠ
    @sonatard

    View Slide

  3. 3
    "QQJGZ
    w 4IPQJGZ΍#"4&ͷΞϓϦΛ/P$PEFͰϦϦʔε͢ΔαʔϏε
    w ؅ཧը໘͔ΒωΠςΟϒΞϓϦͷσβΠϯΛಈతʹมߋՄೳ
    w 8FC7JFXͰ͸ͳ͘ɺ4FSWFS%SJWFO6*Λ࠾༻
    w όοΫΤϯυ
    w ($1ɺ(P
    w ࣾһਓ
    w ۀ຿ҕୗɺ෭ۀਓ
    w ࠾༻
    w IUUQTBQQJGZJODDPNKPC

    View Slide

  4. ͸͡Ίʹ

    View Slide

  5. 5
    ͸͡Ίʹ
    w ۙ೥ϨίϝϯσʔγϣϯʹΑΓϢʔβ͝ͱʹ࠷దͳίϯςϯπΛఏڙ͢Δ͜ͱ͸Ұൠత
    ʹͳΓ·ͨ͠ɻ
    w ·ͨΑΓൃలͤͯ͞Ϣʔβ͝ͱʹ࠷దͳ6*Λදࣔ͢Δ͜ͱ͕ٻΊΒΕΔΑ͏ʹͳ͖ͬͯ
    ͍ͯ·͢ɻ
    w ྫ͑͹ʮϢʔβ͕ߪೖ͍ͯ͠Δϒϥϯυͷ৽঎඼ΛϗʔϜը໘ͷҰ൪্ʹදࣔ͢Δʯͱ
    ͍͏͜ͱΛΞϓϦͰ࣮ݱ͍ͨ͜͠ͱ͕͋Γ·͢ɻ
    w ͜ΕΛ؆୯ʹ࣮ݱ͠Α͏͢Δͱɺैདྷ͸8FC7JFXΛ࢖͏ඞཁ͕͋Γɺ͜ΕͩͱωΠςΟ
    ϒΞϓϦͰ͸ͳ͍ͨΊ࠷ߴͷϢʔβମݧΛఏڙ͢Δ͜ͱ͕Ͱ͖·ͤΜͰͨ͠ɻ

    View Slide

  6. 6
    ͸͡Ίʹ
    w ͜ΕΛղܾ͢Δٕज़͕4FSWFS%SJWFO6*Ͱ͢ɻ
    w 4%6*͸ɺ౓ͷ"1*ϦΫΤετ͔Βऔಘͨ͠Ϩεϙϯεͷσʔλߏ଄ʹԠͯ͡ΞϓϦͷ
    6*Λಈతʹมߋ͠·͢ɻ
    w 4%6*Λैདྷͷٕज़ελοΫͰ࣮ݱ͢Δʹ͸ɺϨΠΞ΢τ৘ใΛऔಘ͢ΔͨΊͷ"1*ϦΫ
    ΤετΛ௥ՃͰૹ৴͢Δ͜ͱ΍6*ʹԠͯ͡ҟͳΔίϯςϯπͷϨεϙϯεΛಈతʹύʔ
    ε͢ΔͳͲɺΫϥΠΞϯταΠυͷ࣮૷ίετ͕ߴ͘ͳΔ՝୊͕͋Γ·ͨ͠ɻ
    w ݱࡏͰ͸ɺ+FUQBDL$PNQPTFͱ(SBQI2-Λར༻͢Δ͜ͱͰΫϥΠΞϯταΠυΛͱͯ΋
    γϯϓϧʹ࣮૷͢Δ͜ͱ͕Ͱ͖·͢ɻ

    View Slide

  7. +FUQBDL$PNQPTFͱ(SBQI2-

    View Slide

  8. 8
    +FUQBDL$PNQPTFͱ(SBQI2-
    w +FUQBDL$PNQPTF
    w એݴత6*ϥΠϒϥϦ
    w 7JFX͸ঢ়ଶΛ࣋ͨͳ͘ͳΓ1SFTFOUBUJPO%PNBJO4FQBSBUJPO͕ଅਐ͞Εɺ7JFXͷ։
    ൃ͕͠΍͘͢ͳΓ·ͨ͠
    6*7JFX 4UBUF

    View Slide

  9. 9
    +FUQBDL$PNQPTFͱ(SBQI2-
    w (SBQI2-
    w એݴతσʔλϑΣονϯά
    w ΫϥΠΞϯταΠυ͔Β2VFSZΛૹ৴͢Δ͜ͱͰɺඞཁͳ஋Λ౓ͷϦΫΤετͰऔಘ͢
    Δ͜ͱ͕Ͱ͖·͢
    w ҎԼͷ໰୊Λղܾ
    w ΦʔόʔϑΣον ඞཁͳ͍஋ͷऔಘ

    w ΞϯμʔϑΣον ඞཁͳ஋͕଍Γͳ͍ͨΊʹෳ਺ͷ"1*Λݺͼग़͢

    View Slide

  10. 10
    +FUQBDL$PNQPTFͱ(SBQI2-
    w (SBQI2-ͱ"OESPJE
    w "OSEPJEͷ(SBQI2-ΫϥΠΞϯτBQPMMPBOESPJE
    w εΩʔϚۦಈ։ൃΛ࣮ݱ
    w (SBQI2-ͷ2VFSZΛॻ͖DPEFHFO͢Δͱ2VFSZͷϨεϙϯεΛड͚ͱΔܕΛੜ੒

    View Slide

  11. 11
    +FUQBDL$PNQPTFͱ(SBQI2-
    w (SBQI2-ͷϝϦοτ
    w ίʔυ͕γϯϓϧʹͳΓ։ൃ଎౓͕޲্͢Δ
    w 3&45Ͱݫີʹ΍Ζ͏ͱ͢ΔͱɺҎԼͷม׵͕ඞཁͰͨ͠
    w "1*Ϩεϙϯε༻ͷܕ
    w ˠঢ়ଶΛอ࣋͢ΔͨΊͷ.PEFMͷܕ
    w ˠ7JFXʹදࣔ͢ΔͨΊͷ7JFX.PEFMͷܕ

    View Slide

  12. 12
    +FUQBDL$PNQPTFͱ(SBQI2-
    w αϯϓϧΞϓϦಈ࡞Πϝʔδ
    λεΫͷνΣοΫΛ֎͢
    λεΫʹνΣοΫΛ͢Δ

    View Slide

  13. 13
    +FUQBDL$PNQPTFͱ(SBQI2-
    w (SBQI2-ʹ͓͚Δܕͷҙࣝ
    "1*ͷϨεϙϯεͷܕ
    7JFXʹදࣔ͢ΔͨΊͷܕ
    ม׵͕ඞཁͳ͍
    query ToDoView
    {

    todo
    {

    ...CompletedTasksViewFragmen
    t

    }

    }
    @Composabl
    e

    fun TodToView()
    {

    val (data) = apollo.query(ToDoViewQuery()
    )

    CompletedTasksView(data.todo.fragments.completedTasksViewFragment
    )

    }

    'SBHNFOUͱ͍͏ػೳͰ2VFSZͱ͸ผʹઐ༻ͷܕΛੜ੒͢Δ
    ੜ੒͞Εͨ'SBHNFOUΛίϯϙʔωϯτͷϑΟʔϧυͱ͢Δ
    ˠ'SBHNFOU$PMPDBUJPO
    fragment CompletedTasksViewFragment on ToDo
    {

    i
    d

    completed
    {

    ...TaskViewFragmen
    t

    }

    }

    .PEFM͝ͱʹ'SBHNFOUΛ࡞ΔͷͰ͸ͳ͘
    7JFX͝ͱʹ࡞Δ͜ͱ͕େ੾
    2VFSZ΍'SBHNFOU͔Βੜ੒͞Εͨܕ͸7JFX༝དྷͷܕͰ͋ͬͯɺ
    .PEFM༝དྷͷܕͰ͸ͳ͍ɻ7JFXʹ౉ͯͦ͠ͷ··ར༻͢Δ
    3&45ͷΑ͏ʹ.PEFMͷܕʹม׵ͯ͠͠·ͬͯ͸
    (SBQI2-ͷϝϦοτ͕ଛͳΘΕΔ
    7JFXʹ'SBHNFOUΛ౉͚ͩ͢Ͱม׵͕ඞཁͳ͍
    (SBIQ2-͕ઐ༻ͷܕʹ࠷ॳ͔Βม׵ͯ͘͠Ε͍ͯΔ

    View Slide

  14. 14
    +FUQBDL$PNQPTFͱ(SBQI2-
    @Composabl
    e

    fun TodToView()
    {

    val (data) = apollo.query(ToDoViewQuery()
    )

    TasksView(data.todo.fragments.tasksViewFragment
    )

    UnCompletedTasksView(data.todo.fragments.unCompletedTasksViewFragment
    )

    CompletedTasksView(data.todo.fragments.completedTasksViewFragment
    )

    }

    query ToDoView
    {

    todo
    {

    ...TasksViewFragmen
    t

    ...UnCompletedTasksViewFragmen
    t

    ...CompletedTasksViewFragmen
    t

    }

    }

    7JFXͷίʔυɺσʔλϑΣονɺ6*͕એݴతʹରԠ
    એݴతσʔλϑΣονϯά
    એݴత6*

    View Slide

  15. 15
    +FUQBDL$PNQPTFͱ(SBQI2-
    w +FUQBDL$PNQPTFͱ(SBQI2-Λར༻͢ΔͱγϯϓϧͳίʔυͰ7JFX͕࣮૷ՄೳʹͳΓ·͢
    w (SBQI2-ʹ͸ɺଞʹ΋ΫϥΠΞϯτΩϟογϡͳͲͷϝϦοτ͕͋Γ·͕͢ɺ4FSWFS
    %SJWFO6*ʹ͸ؔ܎ͳ͍ͷͰׂѪ͠·͢ɻ
    w J04%$ͷࢿྉΛ͝ࢀߟ͍ͩ͘͞ɻ
    w એݴత6*ͷঢ়ଶ؅ཧͱΞʔΩςΫνϟ4XJGU6*ͱ(SBQI2-ʹΑΔ࣮ફ
    w IUUQTTQFBLFSEFDLDPNTPOBUBSETXJGUVJHSBQIRM

    View Slide

  16. "JSCOCͷ4FSWFS%SJWFO6*

    View Slide

  17. 17
    "JSCOCͷ4FSWFS%SJWFO6*
    w "JSCOC
    w ೥݄ͷ"JSCOCهࣄͰ࿩୊ʹͳΓ·͕ͨ͠ɺ࣮͸"JSCOC͸(SBQI2-4VNNJU
    Ͱൃද͍ͯ͠·͢ɻ
    w ౰࣌ͷ໊শ͸4FSWFS%SJWFO6*Ͱ͸ͳ͘#BDLFOE%SJWFO6*Ͱͨ͠ɻ
    w ౰࣌͸8FCϖʔδͷ6*ΛϢʔβ͝ͱʹಈతʹมߋ͍ͯ͠·ͨ͠ɻ
    w ͜ΕʹΑΓ"#ςετ͕༰қʹͰ͖Δ؀ڥʹͳ͍ͬͯ·ͨ͠ɻ
    w ೥ൃද͞ΕͨهࣄͰ͸(IPTU1MBUGPSNͱͯ͠ɺ"OESPJEɺJ04ɺ8FCʹରԠͯ͠
    ͍·͢ɻ

    View Slide

  18. 18
    "JSCOCͷ4FSWFS%SJWFO6*
    w 4FDSFFOͱ4FDUJPO
    interface GPResponse
    {

    sections: [SectionContainer
    ]

    screens: [ScreenContainer]
    }
    type SectionContainer
    {

    id: String
    !

    sectionComponentType: SectionComponentTyp
    e

    section: Sectio
    n

    }
    (SBQI2-εΩʔϚ
    Ҿ༻IUUQTNFEJVNDPNBJSCOCFOHJOFFSJOHDG
    type ScreenContainer
    {

    id: Strin
    g

    # දࣔํ๏ ϙοϓΞοϓɺγʔτ
    screenProperties: ScreenPropertie
    s

    # ϨΠΞ΢τ
    layout: LayoutsPerFormFacto
    r

    }
    4DSFFO$POUBJOFS͸શମͷϨΠΞ΢τΛ࣋ͭ
    4FDUJPO$POUBJOFS͸֤4FDUJPOͷ৘ใΛ࣋ͭ

    View Slide

  19. 19
    "JSCOCͷ4FSWFS%SJWFO6*
    w 4FDUJPO
    union Section = HeroSection | TitleSectio
    n

    type HeroSection
    {

    images: [String]
    !

    }

    type TitleSection
    {

    title: String!
    ,

    titleStyle: TextStyle
    !

    subtitle: Strin
    g

    subtitleStyle: TextStyl
    e

    onSubtitleClickAction: IActio
    n

    }
    type SectionContainer
    {

    id: String
    !

    sectionComponentType: SectionComponentTyp
    e

    section: Sectio
    n

    }

    enum SectionComponentType
    {

    HERO
    ,

    TITLE
    ,

    #


    }
    4FDUJPOͷλΠϓ
    4FDUJPO͸6OJPOͰ͋ΔͨΊҟͳΔܕΛ
    ड͚औΔ͜ͱ͕Մೳ
    දࣔ಺༰
    "DUJPO
    αʔό͕4FDUJPOͷฦ͢ॱ൪Λม͑Ε͹
    6*΋ಈతʹมΘΔ
    interface GPResponse
    {

    sections: [SectionContainer
    ]

    screens: [ScreenContainer]
    }
    )FSP4FDUJPOPS5JUMF4FDUJPO͕ฦΔ
    දࣔ಺༰

    View Slide

  20. 20
    "JSCOCͷ4FSWFS%SJWFO6*
    w 5JUMF4FDUJPOͷ࣮૷
    w 6*͸ฦͬͯ͘Δ4FDUJPOʹԠ֤ͯ͡ϓϥοτϑΥʔϜͰ࣮૷͢Δඞཁ͕͋Δ
    w "OESPJEɺJ04ɺ8FC
    class TitleSectionComponent : SectionComponent()
    {

    override fun buildSectionUI(section: TitleSection)
    {

    Text
    (

    text = section.title
    ,

    style = section.titleStyl
    e

    )

    if (!section.subtitle.isNullOrEmpty()
    {

    Text
    (

    text = section.subtitle
    ,

    style = section.subtitleStyl
    e

    onClick =
    {

    GPActionHandler.handleIAction(section.onSubtitleClickAction
    )

    }

    )

    }

    }

    }
    (SBQI2-Ϩεϙϯε

    View Slide

  21. 21
    "JSCOCͷ4FSWFS%SJWFO6*
    w 4DSFFO֤4FDUJPOͷϨΠΞ΢τ഑ஔΛܾఆ͢Δ
    w 4JOHMF$PMVNO-BZPVUͷྫ
    type ScreenContainer
    {

    id: Strin
    g

    # දࣔํ๏ ϙοϓΞοϓɺγʔτ
    screenProperties: ScreenPropertie
    s

    # ϨΠΞ΢τ
    layout: LayoutsPerFormFacto
    r

    }

    type LayoutsPerFormFactor
    {

    # ϞόΠϧ༻
    compact: ILayou
    t

    # σεΫτοϓɺλϒϨοτ
    wide: ILayou
    t

    }

    interface ILayout {
    }

    type SingleColumnLayout implements ILayout
    {

    # φϏήʔγϣϯ͸1ͭͷSectio
    n

    # SingleSectionPlacementͷSectionDetailͰ
    # paddingΛܾఆ͢Δ
    nav: SingleSectionPlacemen
    t

    # ϝΠϯ͸ෳ਺Sectio
    n

    # MultipleSectionsPlacemen
    t

    main: MultipleSectionsPlacemen
    t

    # Footer͸1ͭͷSectio
    n

    floatingFooter: SingleSectionPlacemen
    t

    }

    type SingleSectionPlacement
    {

    sectionDetail: SectionDetail
    !

    }

    type MultipleSectionsPlacement
    {

    sectionDetails: [SectionDetail]
    !

    }

    type SectionDetail
    {

    sectionId: Strin
    g

    topPadding: In
    t

    bottomPadding: In
    t

    }

    View Slide

  22. 22
    "sections":
    [

    {

    "id": "toolbar_section"
    ,

    "sectionComponentType": "TOOLBAR"
    ,

    "section":
    {

    "type": "ToolbarSection"
    ,

    "nav_button":
    {

    "onClickAction":
    {

    "type": "NavigateBack"
    ,

    "screenId": "previous_screen_id
    "

    }

    }

    }

    }
    ,

    {

    "id": "hero_section"
    ,

    "sectionComponentType": "HERO"
    ,

    "section":
    {

    "type": "HeroSection"
    ,

    "images":
    [

    "api.airbnb.com/..."
    ,

    ]
    ,

    }

    }
    ,

    {

    "id": "title_section"
    ,

    "sectionComponentType": "TITLE"
    ,

    "section":
    {

    "type": "TitleSection"
    ,

    "title": "Seamist Beach Cottage, Private Beach & Ocean Views"
    ,

    "titleStyle": {
    }

    }

    }
    ,

    "screens":
    [

    {

    "id": "ROOT"
    ,

    "screenProperties": {}
    ,

    "layout":
    {

    "wide": {}
    ,

    "compact":
    {

    "type": "SingleColumnLayout"
    ,

    "main":
    {

    "type": "MultipleSectionsPlacement"
    ,

    "sectionDetails":
    [

    {

    "sectionId": "hero_section
    "

    }
    ,

    {

    "sectionId": "title_section
    "

    }

    ]

    }
    ,

    "nav":
    {

    "type": "SingleSectionPlacement"
    ,

    "sectionDetail":
    {

    "sectionId": "toolbar_section
    "

    }

    }
    ,

    "footer":
    {

    "type": "SingleSectionPlacement"
    ,

    "sectionDetail":
    {

    "sectionId": "book_bar_footer
    "

    }

    }

    }

    }

    }
    ,

    ],
    (SBQI2-Ϩεϙϯε
    ΫϥΠΞϯτ͕ཁٻͨ͠Ϋ
    ΤϦʔΛฦ͢
    (SBQI2-ͳͷͰ
    -BZPVUͱ4FDUJPOඞཁͳίϯςϯπΛ
    ϦΫΤετͰऔಘՄೳ

    View Slide

  23. 23
    "JSCOCͷ4FSWFS%SJWFO6*
    w )5.-ͷ࠶ൃ໌ʁͦ͜·Ͱ͢ΔͳΒ8FC7JFXͰΑ͍ʁ
    w 8FC7JFX
    w )5.-ΛಈతʹϨϯμϦϯά͢Δ
    w 4%6*
    w "1*ϨεϙϯεΛݩʹಈతͳ6*Λߏங͢Δ
    w 8FC7JFXͱൺֱͨ͠ϝϦοτ
    w ωΠςΟϒΞϓϦͷύϑΥʔϚϯεͷԸܙ
    w ͱ͸͍͑ɺ͜͜·Ͱ࡞ΓࠐΜͰ·Ͱ΍Δඞཁ͕͋Δʜʁ

    View Slide

  24. 24
    "JSCOCͷ4FSWFS%SJWFO6*
    w "JSCOCͷ4FSWFS%SJWFO6*ͷྫ͸΍Γ͗͢Ͱ͋Δͷ͔ʁ
    w ࣮͸ͦ͜·Ͱෳࡶͳ͜ͱ͸͍ͯ͠ͳ͍
    w ΍͍ͬͯΔ͜ͱ͸
    w 4FDUJPOΛॱ൪ʹฦ͢͜ͱ
    w 4FDUJPOͷ্ԼͷQBEEJOHͷࢦఆ
    w .VMUJ$PMVNOPS4JOHMF$PMVNO

    View Slide

  25. 25
    "JSCOCͷ4FSWFS%SJWFO6*
    w "JSCOC΋͢΂ͯͷ6*Λ(SBQI2-Ͱߏங͍ͯ͠ΔΘ͚Ͱ͸ͳ͍
    w 4FDUJPO಺෦ͷϨΠΞ΢τ·Ͱ͸(SBQI2-Ͱฦ͍ͯ͠ͳ͍
    w ͦͷ৔߹͸ϨΠΞ΢τ͸ΫϥΠΞϯτଆ͕࣋ͭ͜ͱʹͳΔ
    union Section = HeroSection | TitleSectio
    n

    type HeroSection
    {

    images: [String]
    !

    }

    type TitleSection
    {

    title: String!
    ,

    titleStyle: TextStyle
    !

    subtitle: Strin
    g

    subtitleStyle: TextStyl
    e

    onSubtitleClickAction: IActio
    n

    }
    දࣔ಺༰ɺ૷০ɺ"DUJPOΛฦ͍ͯ͠Δ͕ɺ
    ϨΠΞ΢τ͸ฦ͍ͯ͠ͳ͍
    UJUMFͱTVCUJUMFͷڑ཭͸ͲΕ͘Β͍ʁ
    هࣄͷεΩʔϚ͸αϯϓϧͩͱࢥ͏ͷͰɺ
    Ͳ͜·Ͱ"JSCOC͕΍͍ͬͯΔ͔ਖ਼֬ͳͱ͜Ζ͸ෆ໌

    View Slide

  26. 26
    "JSCOCͷ4FSWFS%SJWFO6*
    w "JSCOC΋͢΂ͯͷ6*Λ(SBQI2-Ͱߏங͍ͯ͠ΔΘ͚Ͱ͸ͳ͍
    w ϨΠΞ΢τ͸4JOHMF$PMVNOPS.VMUJ$PMVNO͚ͩ
    w .VMUJ3PXͳͲʹ͸ରԠ͍ͯ͠ͳ͍
    w 4FDUJPOؒͷ্ԼͷQBEEJOHҎ֎͸όοΫΤϯυͰ੍ޚ͍ͯ͠ͳ͍
    type SingleSectionPlacement
    {

    sectionDetail: SectionDetail
    !

    }

    type MultipleSectionsPlacement
    {

    sectionDetails: [SectionDetail]
    !

    }

    type SectionDetail
    {

    sectionId: Strin
    g

    topPadding: In
    t

    bottomPadding: In
    t

    }
    4FDUJPOؒͷUPQ1BEEJOHɺCUUPN1BEEJOH͚ͩ

    View Slide

  27. 27
    "JSCOCͷ4FSWFS%SJWFO6*
    w 4FSWFS%SJWFO6*Λ࣮ࢪ͢Δ্Ͱେ੾ͳ͜ͱ
    w ͜ͷΑ͏ʹࣗ෼ͨͪͰඞཁͳϢʔεέʔεʹԠͯ͡Մม఺Λઃܭ͢Δ͜ͱ͕େ੾Ͱ͢
    w ͢΂ͯΛόοΫΤϯυͰ੍ޚ͠Α͏ͱࢥ͏ͱࠞཚͯ͠͠·͍·͢

    View Slide

  28. 28
    "JSCOCͷ4FSWFS%SJWFO6*
    w Ҏ্Λ౿·͑ͯ΋4FSWFS%SJWFO6*͕೉͍͜͠ͱ
    w ϨΠΞ΢τͷՄม఺ͷઃܭͱͦΕʹ͋ΘͤͨσβΠϯγεςϜͷߏங
    w ը໘͕૿͑ΔͨͼʹՄม఺͕ྲྀಈతͳσβΠϯγεςϜͰ͸(SBQI2-εΩʔϚͷ
    ରԠ͕౎౓ඞཁʹͳΓٯʹ։ൃ଎౓͕མͪ·͢
    w Մม఺Λಛఆͯ͠σβΠϯγεςϜʹམͱ͠ࠐΉ͜ͱ͕ඞཁͰ͢
    w 4FSWFS%SJWFO6*Λ࣮ݱ͢Δ໨తͷ໌֬Խ
    w ৽͍͠औΓ૊Έ͔ͩΒྑ͍Θ͚Ͱ͸͋Γ·ͤΜ
    w ແҋʹ࣮ࢪͯ͠΋ҙຯ͕͋Γ·ͤΜ
    w ϓϩμΫτʹΑͬͯ͸্هΛ༷ʑͳϓϥοτϑΥʔϜͰߟྀ͢Δඞཁੑ
    w J04ɺ"OESPJEɺ8FC
    w ϞόΠϧɺλϒϨοτɺσεΫτοϓͷը໘αΠζ

    View Slide

  29. 29
    "JSCOCͷ4FSWFS%SJWFO6*
    w ඞͣ͢΂ͯΛ(SBQI2-Ͱฦ͢ඞཁ͕͋ΔΘ͚Ͱ͸͋Γ·ͤΜ
    w Ͳ͜·ͰΛαʔό͕࣋ͪɺͲ͜·ͰΛΫϥΠΞϯτͰ͔࣋ͭ͸ઃܭ࣍ୈͰॊೈʹม͑
    Δ͜ͱ͕Ͱ͖·͢
    w ৄࡉͳϨΠΞ΢τΛ͠ͳͯ͘΋ɺ4%6*͸༗ޮͳέʔε͕͋Γ·͢

    View Slide

  30. "QQJGZͷ4FSWFS%SJWFO6*

    View Slide

  31. 31
    "QQJGZͷ4FSWFS%SJWFO6*
    w "QJQGZͷϗʔϜը໘ͷྫ
    w ෳ਺ͷηΫγϣϯΛྻʹฒ΂Δ͜ͱ͕Ͱ͖
    Δ
    w ηΫγϣϯλΠϓʹରԠͨ͠4FDUJPOΛදࣔ
    w ηΫγϣϯ಺ͷϨΠΞ΢τ͸ΫϥΠΞϯτ
    ͕࣋ͭ
    w "JSCOCͱͷҧ͍
    w ηΫγϣϯؒͷQBEEJOH͸ΫϥΠΞϯτ͕
    ࣋ͭ
    w ϢʔβʹԠͯ͡ग़͠෼͚Δ͜ͱ͕໨తͰ͸
    ͳ͘ɺΤϯδχΞͰ͸ͳ͍γϣοϓΦʔ
    φʔͷํ͕༰қʹσβΠϯΛฤूͰ͖Δ͜
    ͱ͕໨త
    w

    View Slide

  32. 32
    ϒϥ΢β͔ΒηΫγϣϯͷ௥Ճɺฤूɺ࡟আ͕Մೳ

    View Slide

  33. 33
    ηΫγϣϯͷฤू
    දࣔը૾ͷΞοϓϩʔυ
    ը໘ભҠઌͷ63-Λબ୒

    View Slide

  34. 34
    "QQJGZͷ4FSWFS%SJWFO6*
    w (SBQI2-εΩʔϚ
    (SBIQ2-εΩʔϚ
    union ShopifyDesignBlock
    =

    ShopifyDesignBlockProductBanner | ShopifyDesignBlockCollectionBanner
    |

    ShopifyDesignBlockCollection
    s

    type ShopifyDesignBlockProductBanner
    {

    kind: String
    !

    image: ImageResource
    !

    product: ShopifyProduc
    t

    }

    type ShopifyDesignBlockCollectionBanner
    {

    kind: String
    !

    image: ImageResource
    !

    collection: ShopifyCollectio
    n

    }

    type ShopifyDesignBlockCollections
    {

    kind: String
    !

    title: String
    !

    collections: [ShopifyCollection]
    !

    showsTitle: Boolean
    !

    }
    6OJPOͰ͋Δ4IPQJGZ%FTJHO#MPDL͕
    ϦετͰฦͬͯ͘Δ
    type ShopifyReleasedDesign {
    id: ID!
    blocks: [ShopifyDesignBlock]!
    }

    1SPEVDU#BOOFS͸঎඼৘ใͱը૾
    $PMMFDUJPOT͸ίϨΫγϣϯҰཡ
    $PMMFDUJPO#BOOFS͸ίϨΫγϣϯ৘ใͱը૾

    View Slide

  35. 35
    "QQJGZͷ4FSWFS%SJWFO6*
    w (SBQI2-ΫΤϦʔ
    blocks
    {

    i
    d

    block
    {

    __typenam
    e

    ... on ShopifyDesignBlockProductBanner
    {

    product
    {

    i
    d

    titl
    e

    }

    image
    {

    i
    d

    ur
    l

    }

    }

    ... on ShopifyDesignBlockCollectionBanner
    {

    collection
    {

    i
    d

    titl
    e

    }

    image
    {

    i
    d

    ur
    l

    }

    }

    # ུͦͷଞBloc
    k

    }

    }
    ෳ਺#MPDL͕ϦετͰॱ൪ʹฦͬͯ͘Δ
    ͜ΕͰϨΠΞ΢τͷॱ൪͕Θ͔Δ
    #MPDL͕දࣔʹඞཁͳ஋΋ಉ࣌ʹऔಘͰ͖Δ
    ঎඼όφʔͷϒϩοΫ͸ɺ঎඼ͷλΠτϧͱ঎඼ͷը૾

    View Slide

  36. 36
    "QQJGZͷ4FSWFS%SJWFO6*
    w 'SBHNFOUͷ׆༻
    query Home
    {

    blocks
    {

    nodes
    {

    i
    d

    block
    {

    __typenam
    e

    ... on ShopifyDesignBlockProductBanner
    {

    ...ShopifyDesignBlockProductBannerFragmen
    t

    }

    ... on ShopifyDesignBlockCollectionBanner
    {

    ...ShopifyDesignBlockCollectionBannerFragmen
    t

    }

    ... on ShopifyDesignBlockCollections
    {

    ...ShopifyDesignBlockCollectionsFragmen
    t

    }

    }

    }

    }

    }
    ఆٛͨ͠'SBHNFOUΛద༻ͨ͠
    2VFSZ
    fragment ShopifyDesignBlockProductBannerFragment


    ɹɹɹɹɹɹɹon ShopifyDesignBlockProductBanner
    {

    product
    {

    i
    d

    titl
    e

    }

    image
    {

    i
    d

    ur
    l

    }

    }

    ଞͷFragment͸লུ
    7JFXͰར༻͢ΔܕΛ'SBHNFOUͱͯ͠ఆٛ

    View Slide

  37. 37
    "QQJGZͷ4FSWFS%SJWFO6*
    @Composabl
    e

    fun DesignBlockProductBanner
    (

    p: ShopifyDesignBlockProductBannerFragmen
    t

    )
    {

    Image
    (

    painter = rememberCoilPainter(request = p.image.url.toString())
    ,

    modifier = Modifie
    r

    .clickable
    {

    navController.navigat
    e

    ɹɹɹɹɹɹɹɹɹɹɹɹ(NavItem.Product.name + "/" + p.product.id.raw
    )

    }

    )

    }

    fragment ShopifyDesignBlockProductBannerFragment


    ɹɹɹɹɹɹɹon ShopifyDesignBlockProductBanner
    {

    product
    {

    i
    d

    titl
    e

    }

    image
    {

    i
    d

    ur
    l

    }

    }

    @Composabl
    e

    fun Home()
    {

    val (data) = apollo.query(input = ShopifyHomeQuery()
    )

    LazyColumn
    {

    data?.viewer?.shopify?.design?.block?.released?.blocks?.let
    {

    items(it.nodes) { node -
    >

    when
    {

    node.block.asShopifyDesignBlockProductBanner != null ->
    {

    DesignBlockProductBanner(node.asShopifyDesignBlockProductBanner.fragment
    )

    }
    'SBHNFOU͔Βੜ੒͞ΕͨܕΛ
    7JFXͷҾ਺ʹ͢Δ
    'SBHNFOU͔Βੜ੒ͨ͠7JFXͰར༻͢ΔܕΛ౉͚ͩ͢
    w 'SBHNFOUͷ׆༻
    'SBHNFOU

    View Slide

  38. 38
    "QQJGZͷ4FSWFS%SJWFO6*
    w ίʔυશମ
    @Composabl
    e

    fun Home()
    {

    val (data) = apollo.query(input = HomeQuery()
    )

    LazyColumn
    {

    data?.viewer?.shopify?.design?.block?.released?.blocks?.let
    {

    items(it.nodes) { node -
    >

    when
    {

    node.block.asShopifyDesignBlockProductBanner != null ->
    {

    DesignBlockProductBanner(node.asShopifyDesignBlockProductBanner.fragment
    )

    }

    node.block.asShopifyDesignBlockCollectionBanner != null ->
    {

    DesignBlockCollectionBanner(node.asShopifyDesignBlockCollectionBanner.fragment
    )

    }

    node.block.asShopifyDesignBlockCollections != null ->
    {

    DesignBlockCollections(node.asShopifyDesignBlockCollections.fragment
    )

    }

    node.block.asShopifyDesignBlockCollectionProducts != null ->
    {

    DesignBlockCollectionProducts(node.asShopifyDesignBlockCollectionProducts.fragment
    )

    }

    }

    }

    }

    }

    }
    ίʔυ͸ͱͯ΋γϯϓϧ

    View Slide

  39. 39
    "QQJGZͷ4FSWFS%SJWFO6*
    w ·ͱΊ
    w ෳࡶͦ͏ͳϢʔεέʔεͰ΋ɺએݴత6*ͱ(SBQI2-Λར༻͢Δ͜ͱΫϥΠΞϯταΠ
    υ͸ͱͯ΋γϯϓϧʹ࣮૷͢Δ͜ͱ͕Ͱ͖·͢
    w ͱ͸͍͑ɺ͜ͷΑ͏ͳಈతͳ6*Λ೔ৗతʹར༻͢Δ͔ͱ͍͏ٙ໰͸࢒ΔͷͰɺଓ͍ͯ͸
    ΑΓγϯϓϧͰҰൠతͳϢʔεέʔεΛઆ໌͠·͢ɻ

    View Slide

  40. 4FSWFS%SJWFO6*ͷϢʔεέʔε
    ͓஌ΒͤछผʹΑΔ"DUJPOͷมߋ

    View Slide

  41. 41
    4FSWFS%SJWFO6*ͱ͓஌Βͤ
    w ͓஌Βͤ
    w ςΩετͷ͓஌Βͤ
    w λοϓͨ͠ΒৄࡉϝοηʔδΛදࣔ
    w ঎඼͓஌Βͤ
    w λοϓͨ͠Β঎඼ৄࡉʹը໘ભҠ
    w 3&45Ͱ΋ҎԼͷΑ͏ͳఆٛΛ࣮ͯ͠૷͍ͯ͠Δਓ΋ଟ͍͔ͱࢥ͍·͢ɻ
    w 5ZQFUFYU
    w %BUBUJUMFlϝϯςφϯεͷ͓஌Βͤz CPEZlऴ೔ϝϯςφϯεͰ͢z
    w 5ZQF1SPEVDU
    w %BUBUJUMFl999ൃചͷ͓஌Βͤz QSPEVDU*%lz
    w ͜Ε͕(SBQI2-Λར༻͢Δͱγϯϓϧʹ࣮૷Ͱ͖·͢

    View Slide

  42. 42
    4FSWFS%SJWFO6*ͱ͓஌Βͤ
    w ͓஌Βͤͷ4FSWFS%SJWFO6*
    w ςΩετ͓஌Βͤ
    w UJUMFͱNFTTBHF
    w ঎඼͓஌Βͤ
    w UJUMFͱQSPEVDU*%
    query Announcement
    {

    announcements
    {

    nodes
    {

    ... on AnnouncementText
    {

    i
    d

    titl
    e

    messag
    e

    }

    ... on AnnouncementProduct
    {

    i
    d

    titl
    e

    productI
    D

    }

    }

    }

    }
    @Composabl
    e

    fun Announcement()
    {

    val (data) = apollo.query(input = AnnouncementQuery(10)
    )

    LazyColumn
    {

    items(data?.viewer?.shopify?.announcements?.nodes) { announcemen
    announcement.asAnnouncementText?.let
    {

    AnnouncementItem
    (

    title = it.title
    ,

    onClick =
    {

    showBottomSheet
    (

    SetBottomSheet.State
    .

    AnnouncementText(it.message
    )

    )

    }

    )

    }

    announcement.asAnnouncementProduct?.let
    {

    AnnouncementItem
    (

    title = it.title
    ,

    onClick =
    {

    navController.navigate(NavItem.Product.name


    + "/" + p.product?.id
    )

    }

    )

    }

    }

    }

    } (SBQI2-ͱ+FUQBDL$PNQPTFͰ͸͜Ε͚ͩͰ࣮ݱͰ͖Δ
    ঎඼ը໘ʹભҠ
    ϝοηʔδΛγʔτͰදࣔ
    λΠτϧදࣔ
    λΠτϧදࣔ

    View Slide

  43. 43
    4FSWFS%SJWFO6*ͱ͓஌Βͤ
    w ͜ͷΑ͏ʹ4%6*͸ɺҰൠతͳϢʔεέʔεͰ΋ར༻Ͱ͖·͢ɻ
    w 4%6*Λ࢖͏͜ͱͰɺࠓ·Ͱ͸໘౗࣮ͩͬͨ૷͕ܕ҆શͰγϯϓϧʹ࣮૷͢Δ͜ͱ͕Ͱ͖
    ΔΑ͏ʹͳΓ·͢ɻ

    View Slide

  44. (SBIQ2-4FSWFS%SJWFO6*ͷ՝୊

    View Slide

  45. 45
    (SBQI2-4FSWFS%SJWFO6*ͷ՝୊
    w όοΫΤϯυ͕(SBQI2-Ͱ͋Δඞཁ͕͋Γ·͢
    w ࠷େͷ໰୊
    w ֶशίετ΋ߴ͍
    w όοΫΤϯυͰ6OJPOΛฦ࣮͢૷͕ඞཁʹͳΓ·͢
    w ͋·Γ΍Δ͜ͱͰ͸ͳ͍ͷͰ৽ͨͳઃܭ͕ඞཁ

    View Slide

  46. 4FSWFS%SJWFO6*ͷՄೳੑ

    View Slide

  47. 47
    4FSWFS%SJWFO6*ͷՄೳੑ
    w 4%6*ͰͰ͖Δ͜ͱ
    w ΞϓϦͷΞοϓσʔτʹґଘͤͣಈతʹ6*ΛมߋͰ͖Δ
    w ͨͩ͠૝ఆͰ͖͍ͯͳ͔ͬͨσβΠϯ͕ඞཁʹͳΔͱɺΫϥΠΞϯτͷΞοϓσʔτ
    ͕ඞཁʹͳΔ
    w ྫ
    w 4JOHMF$PMVNO͚ͩΛ૝ఆ͍͕ͯͨ͠ɺ.VMUJ$PMVNO͕ඞཁʹͳͬͨ৔߹ͳͲɺΫ
    ϥΠΞϯτ͸.VMUJ$PMVNOͷ7JFXΛ࣮૷͍ͯ͠ͳ͍ͷͰɺΞοϓσʔτ͕ඞཁ
    w ྫ
    w ͓஌ΒͤͰɺ৽ͨʹΫϦοΫͨ͠Β֎෦αΠτʹඈͿ͓஌ΒͤΛ഑৴͠Α͏ͱ
    ࢥͬͯ΋ɺΫϥΠΞϯτ͕֎෦ϦϯΫ͓஌Βͤ7JFXΛ࣮૷͍ͯ͠ͳ͍ͷͰɺΞο
    ϓσʔτ͕ඞཁʹͳΔ

    View Slide

  48. 48
    4FSWFS%SJWFO6*ͷՄೳੑ
    w 8FC7JFXͱͷൺֱ
    w 4%6*Ͱ͸ࣄલʹ૝ఆͨ͠ൣғͰ͔͠ಈతʹ6*ͷมߋ͕Ͱ͖·ͤΜ
    w ֤ϓϥοτϑΥʔϜͰ࣮૷΋ඞཁʹͳΓ·͢
    w ͦΕͧΕʹ࠷దͳ6*Λద༻Ͱ͖ΔͷͰϝϦοτͰ΋͋Γ·͢
    w ͜ΕΒ͕໰୊ʹͳΔ৔߹ʹ͸8FC7JFXͷํ͕༗ޮͰ͢

    View Slide

  49. ·ͱΊ

    View Slide

  50. 50
    4FSWFS%SJWFO6*·ͱΊ
    w 4FSWFS%SJWFO6*ʹΑΓɺωΠςΟϒΞϓϦͰ΋ಈతʹ6*ΛมߋͰ͖ΔΑ͏ʹͳΓ·͢
    w ϢʔβʹԠͨ͡ಈతͳ6*
    w "#ςετɺϨίϝϯσʔγϣϯ
    w ΤϯδχΞҎ֎ʹΑΔ6*ͷมߋ
    w "QQJGZ
    w λΠϓʹԠͨ͡ॲཧͷ੾Γସ͑
    w ͓஌Βͤ
    w ͓஌ΒͤͷΑ͏ʹҰൠతͳϢʔεέʔεͰ։ൃޮ཰Λ্͛ΔͨΊʹ΋ར༻Ͱ͖·͢
    w όοΫΤϯυ͕Մมͳσʔλߏ଄Λฦ͢͜ͱ͕ࣗવͳ৔߹ʹ͸࠾༻ͷݕ౼Ձ஋͕͋Γ
    ·͢
    w +FUQBDL$PNQPTFͱ(SBQI2-ʹΑΓɺ4FSWFS%SJWFO6*ͷ࣮૷ϋʔυϧΛԼ͛Δ͜ͱ͕Ͱ
    ͖·͢

    View Slide

  51. 51
    w ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠

    View Slide