Save 37% off PRO during our Black Friday Sale! »

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

3cb4e761dbdb7aebd1ffd85f752e1e3e?s=47 sonata
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

3cb4e761dbdb7aebd1ffd85f752e1e3e?s=128

sonata

October 10, 2021
Tweet

Transcript

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

  2. Appify Technologie s CT O ͦͳଠ @sonatard

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

    w ($1ɺ(P w ࣾһਓ w ۀ຿ҕୗɺ෭ۀਓ w ࠾༻ w IUUQTBQQJGZJODDPNKPC
  4. ͸͡Ίʹ

  5. 5 ͸͡Ίʹ w ۙ೥ϨίϝϯσʔγϣϯʹΑΓϢʔβ͝ͱʹ࠷దͳίϯςϯπΛఏڙ͢Δ͜ͱ͸Ұൠత ʹͳΓ·ͨ͠ɻ w ·ͨΑΓൃలͤͯ͞Ϣʔβ͝ͱʹ࠷దͳ6*Λදࣔ͢Δ͜ͱ͕ٻΊΒΕΔΑ͏ʹͳ͖ͬͯ ͍ͯ·͢ɻ w ྫ͑͹ʮϢʔβ͕ߪೖ͍ͯ͠Δϒϥϯυͷ৽঎඼ΛϗʔϜը໘ͷҰ൪্ʹදࣔ͢Δʯͱ

    ͍͏͜ͱΛΞϓϦͰ࣮ݱ͍ͨ͜͠ͱ͕͋Γ·͢ɻ w ͜ΕΛ؆୯ʹ࣮ݱ͠Α͏͢Δͱɺैདྷ͸8FC7JFXΛ࢖͏ඞཁ͕͋Γɺ͜ΕͩͱωΠςΟ ϒΞϓϦͰ͸ͳ͍ͨΊ࠷ߴͷϢʔβମݧΛఏڙ͢Δ͜ͱ͕Ͱ͖·ͤΜͰͨ͠ɻ
  6. 6 ͸͡Ίʹ w ͜ΕΛղܾ͢Δٕज़͕4FSWFS%SJWFO6*Ͱ͢ɻ w 4%6*͸ɺ౓ͷ"1*ϦΫΤετ͔Βऔಘͨ͠Ϩεϙϯεͷσʔλߏ଄ʹԠͯ͡ΞϓϦͷ 6*Λಈతʹมߋ͠·͢ɻ w 4%6*Λैདྷͷٕज़ελοΫͰ࣮ݱ͢Δʹ͸ɺϨΠΞ΢τ৘ใΛऔಘ͢ΔͨΊͷ"1*ϦΫ ΤετΛ௥ՃͰૹ৴͢Δ͜ͱ΍6*ʹԠͯ͡ҟͳΔίϯςϯπͷϨεϙϯεΛಈతʹύʔ

    ε͢ΔͳͲɺΫϥΠΞϯταΠυͷ࣮૷ίετ͕ߴ͘ͳΔ՝୊͕͋Γ·ͨ͠ɻ w ݱࡏͰ͸ɺ+FUQBDL$PNQPTFͱ(SBQI2-Λར༻͢Δ͜ͱͰΫϥΠΞϯταΠυΛͱͯ΋ γϯϓϧʹ࣮૷͢Δ͜ͱ͕Ͱ͖·͢ɻ
  7. +FUQBDL$PNQPTFͱ(SBQI2-

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

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

    ҎԼͷ໰୊Λղܾ w ΦʔόʔϑΣον ඞཁͳ͍஋ͷऔಘ  w ΞϯμʔϑΣον ඞཁͳ஋͕଍Γͳ͍ͨΊʹෳ਺ͷ"1*Λݺͼग़͢
  10. 10 +FUQBDL$PNQPTFͱ(SBQI2- w (SBQI2-ͱ"OESPJE w "OSEPJEͷ(SBQI2-ΫϥΠΞϯτBQPMMPBOESPJE w εΩʔϚۦಈ։ൃΛ࣮ݱ w (SBQI2-ͷ2VFSZΛॻ͖DPEFHFO͢Δͱ2VFSZͷϨεϙϯεΛड͚ͱΔܕΛੜ੒

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

    w ˠঢ়ଶΛอ࣋͢ΔͨΊͷ.PEFMͷܕ w ˠ7JFXʹදࣔ͢ΔͨΊͷ7JFX.PEFMͷܕ
  12. 12 +FUQBDL$PNQPTFͱ(SBQI2- w αϯϓϧΞϓϦಈ࡞Πϝʔδ λεΫͷνΣοΫΛ֎͢ λεΫʹνΣοΫΛ͢Δ

  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-͕ઐ༻ͷܕʹ࠷ॳ͔Βม׵ͯ͘͠Ε͍ͯΔ
  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*
  15. 15 +FUQBDL$PNQPTFͱ(SBQI2- w +FUQBDL$PNQPTFͱ(SBQI2-Λར༻͢ΔͱγϯϓϧͳίʔυͰ7JFX͕࣮૷ՄೳʹͳΓ·͢ w (SBQI2-ʹ͸ɺଞʹ΋ΫϥΠΞϯτΩϟογϡͳͲͷϝϦοτ͕͋Γ·͕͢ɺ4FSWFS %SJWFO6*ʹ͸ؔ܎ͳ͍ͷͰׂѪ͠·͢ɻ w J04%$ͷࢿྉΛ͝ࢀߟ͍ͩ͘͞ɻ w

    એݴత6*ͷঢ়ଶ؅ཧͱΞʔΩςΫνϟ4XJGU6*ͱ(SBQI2-ʹΑΔ࣮ફ w IUUQTTQFBLFSEFDLDPNTPOBUBSETXJGUVJHSBQIRM
  16. "JSCOCͷ4FSWFS%SJWFO6*

  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ʹରԠͯ͠ ͍·͢ɻ
  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ͷ৘ใΛ࣋ͭ
  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͕ฦΔ දࣔ಺༰
  20. 20 "JSCOCͷ4FSWFS%SJWFO6* w 5JUMF4FDUJPOͷ࣮૷ w 6*͸ฦͬͯ͘Δ4FDUJPOʹԠ֤ͯ͡ϓϥοτϑΥʔϜͰ࣮૷͢Δඞཁ͕͋Δ w "OESPJEɺJ04ɺ8FC class TitleSectionComponent

    : SectionComponent<TitleSection>() { 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-Ϩεϙϯε
  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 }
  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ඞཁͳίϯςϯπΛ ϦΫΤετͰऔಘՄೳ
  23. 23 "JSCOCͷ4FSWFS%SJWFO6* w )5.-ͷ࠶ൃ໌ʁͦ͜·Ͱ͢ΔͳΒ8FC7JFXͰΑ͍ʁ w 8FC7JFX w )5.-ΛಈతʹϨϯμϦϯά͢Δ w 4%6*

    w "1*ϨεϙϯεΛݩʹಈతͳ6*Λߏங͢Δ w 8FC7JFXͱൺֱͨ͠ϝϦοτ w ωΠςΟϒΞϓϦͷύϑΥʔϚϯεͷԸܙ w ͱ͸͍͑ɺ͜͜·Ͱ࡞ΓࠐΜͰ·Ͱ΍Δඞཁ͕͋Δʜʁ
  24. 24 "JSCOCͷ4FSWFS%SJWFO6* w "JSCOCͷ4FSWFS%SJWFO6*ͷྫ͸΍Γ͗͢Ͱ͋Δͷ͔ʁ w ࣮͸ͦ͜·Ͱෳࡶͳ͜ͱ͸͍ͯ͠ͳ͍ w ΍͍ͬͯΔ͜ͱ͸ w 4FDUJPOΛॱ൪ʹฦ͢͜ͱ

    w 4FDUJPOͷ্ԼͷQBEEJOHͷࢦఆ w .VMUJ$PMVNOPS4JOHMF$PMVNO
  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͕΍͍ͬͯΔ͔ਖ਼֬ͳͱ͜Ζ͸ෆ໌
  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͚ͩ
  27. 27 "JSCOCͷ4FSWFS%SJWFO6* w 4FSWFS%SJWFO6*Λ࣮ࢪ͢Δ্Ͱେ੾ͳ͜ͱ w ͜ͷΑ͏ʹࣗ෼ͨͪͰඞཁͳϢʔεέʔεʹԠͯ͡Մม఺Λઃܭ͢Δ͜ͱ͕େ੾Ͱ͢ w ͢΂ͯΛόοΫΤϯυͰ੍ޚ͠Α͏ͱࢥ͏ͱࠞཚͯ͠͠·͍·͢

  28. 28 "JSCOCͷ4FSWFS%SJWFO6* w Ҏ্Λ౿·͑ͯ΋4FSWFS%SJWFO6*͕೉͍͜͠ͱ w ϨΠΞ΢τͷՄม఺ͷઃܭͱͦΕʹ͋ΘͤͨσβΠϯγεςϜͷߏங w ը໘͕૿͑ΔͨͼʹՄม఺͕ྲྀಈతͳσβΠϯγεςϜͰ͸(SBQI2-εΩʔϚͷ ରԠ͕౎౓ඞཁʹͳΓٯʹ։ൃ଎౓͕མͪ·͢ w

    Մม఺Λಛఆͯ͠σβΠϯγεςϜʹམͱ͠ࠐΉ͜ͱ͕ඞཁͰ͢ w 4FSWFS%SJWFO6*Λ࣮ݱ͢Δ໨తͷ໌֬Խ w ৽͍͠औΓ૊Έ͔ͩΒྑ͍Θ͚Ͱ͸͋Γ·ͤΜ w ແҋʹ࣮ࢪͯ͠΋ҙຯ͕͋Γ·ͤΜ w ϓϩμΫτʹΑͬͯ͸্هΛ༷ʑͳϓϥοτϑΥʔϜͰߟྀ͢Δඞཁੑ w J04ɺ"OESPJEɺ8FC w ϞόΠϧɺλϒϨοτɺσεΫτοϓͷը໘αΠζ
  29. 29 "JSCOCͷ4FSWFS%SJWFO6* w ඞͣ͢΂ͯΛ(SBQI2-Ͱฦ͢ඞཁ͕͋ΔΘ͚Ͱ͸͋Γ·ͤΜ w Ͳ͜·ͰΛαʔό͕࣋ͪɺͲ͜·ͰΛΫϥΠΞϯτͰ͔࣋ͭ͸ઃܭ࣍ୈͰॊೈʹม͑ Δ͜ͱ͕Ͱ͖·͢ w ৄࡉͳϨΠΞ΢τΛ͠ͳͯ͘΋ɺ4%6*͸༗ޮͳέʔε͕͋Γ·͢

  30. "QQJGZͷ4FSWFS%SJWFO6*

  31. 31 "QQJGZͷ4FSWFS%SJWFO6* w "QJQGZͷϗʔϜը໘ͷྫ w ෳ਺ͷηΫγϣϯΛྻʹฒ΂Δ͜ͱ͕Ͱ͖ Δ w ηΫγϣϯλΠϓʹରԠͨ͠4FDUJPOΛදࣔ w

    ηΫγϣϯ಺ͷϨΠΞ΢τ͸ΫϥΠΞϯτ ͕࣋ͭ w "JSCOCͱͷҧ͍ w ηΫγϣϯؒͷQBEEJOH͸ΫϥΠΞϯτ͕ ࣋ͭ w ϢʔβʹԠͯ͡ग़͠෼͚Δ͜ͱ͕໨తͰ͸ ͳ͘ɺΤϯδχΞͰ͸ͳ͍γϣοϓΦʔ φʔͷํ͕༰қʹσβΠϯΛฤूͰ͖Δ͜ ͱ͕໨త w
  32. 32 ϒϥ΢β͔ΒηΫγϣϯͷ௥Ճɺฤूɺ࡟আ͕Մೳ

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

  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͸ίϨΫγϣϯ৘ใͱը૾
  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͕දࣔʹඞཁͳ஋΋ಉ࣌ʹऔಘͰ͖Δ ঎඼όφʔͷϒϩοΫ͸ɺ঎඼ͷλΠτϧͱ঎඼ͷը૾
  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ͱͯ͠ఆٛ
  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
  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 ) } } } } } } ίʔυ͸ͱͯ΋γϯϓϧ
  39. 39 "QQJGZͷ4FSWFS%SJWFO6* w ·ͱΊ w ෳࡶͦ͏ͳϢʔεέʔεͰ΋ɺએݴత6*ͱ(SBQI2-Λར༻͢Δ͜ͱΫϥΠΞϯταΠ υ͸ͱͯ΋γϯϓϧʹ࣮૷͢Δ͜ͱ͕Ͱ͖·͢ w ͱ͸͍͑ɺ͜ͷΑ͏ͳಈతͳ6*Λ೔ৗతʹར༻͢Δ͔ͱ͍͏ٙ໰͸࢒ΔͷͰɺଓ͍ͯ͸ ΑΓγϯϓϧͰҰൠతͳϢʔεέʔεΛઆ໌͠·͢ɻ

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

  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-Λར༻͢Δͱγϯϓϧʹ࣮૷Ͱ͖·͢
  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Ͱ͸͜Ε͚ͩͰ࣮ݱͰ͖Δ ঎඼ը໘ʹભҠ ϝοηʔδΛγʔτͰදࣔ λΠτϧදࣔ λΠτϧදࣔ
  43. 43 4FSWFS%SJWFO6*ͱ͓஌Βͤ w ͜ͷΑ͏ʹ4%6*͸ɺҰൠతͳϢʔεέʔεͰ΋ར༻Ͱ͖·͢ɻ w 4%6*Λ࢖͏͜ͱͰɺࠓ·Ͱ͸໘౗࣮ͩͬͨ૷͕ܕ҆શͰγϯϓϧʹ࣮૷͢Δ͜ͱ͕Ͱ͖ ΔΑ͏ʹͳΓ·͢ɻ

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

  45. 45 (SBQI2- 4FSWFS%SJWFO6*ͷ՝୊ w όοΫΤϯυ͕(SBQI2-Ͱ͋Δඞཁ͕͋Γ·͢ w ࠷େͷ໰୊ w ֶशίετ΋ߴ͍ w

    όοΫΤϯυͰ6OJPOΛฦ࣮͢૷͕ඞཁʹͳΓ·͢ w ͋·Γ΍Δ͜ͱͰ͸ͳ͍ͷͰ৽ͨͳઃܭ͕ඞཁ
  46. 4FSWFS%SJWFO6*ͷՄೳੑ

  47. 47 4FSWFS%SJWFO6*ͷՄೳੑ w 4%6*ͰͰ͖Δ͜ͱ w ΞϓϦͷΞοϓσʔτʹґଘͤͣಈతʹ6*ΛมߋͰ͖Δ w ͨͩ͠૝ఆͰ͖͍ͯͳ͔ͬͨσβΠϯ͕ඞཁʹͳΔͱɺΫϥΠΞϯτͷΞοϓσʔτ ͕ඞཁʹͳΔ w

    ྫ w 4JOHMF$PMVNO͚ͩΛ૝ఆ͍͕ͯͨ͠ɺ.VMUJ$PMVNO͕ඞཁʹͳͬͨ৔߹ͳͲɺΫ ϥΠΞϯτ͸.VMUJ$PMVNOͷ7JFXΛ࣮૷͍ͯ͠ͳ͍ͷͰɺΞοϓσʔτ͕ඞཁ w ྫ w ͓஌ΒͤͰɺ৽ͨʹΫϦοΫͨ͠Β֎෦αΠτʹඈͿ͓஌ΒͤΛ഑৴͠Α͏ͱ ࢥͬͯ΋ɺΫϥΠΞϯτ͕֎෦ϦϯΫ͓஌Βͤ7JFXΛ࣮૷͍ͯ͠ͳ͍ͷͰɺΞο ϓσʔτ͕ඞཁʹͳΔ
  48. 48 4FSWFS%SJWFO6*ͷՄೳੑ w 8FC7JFXͱͷൺֱ w 4%6*Ͱ͸ࣄલʹ૝ఆͨ͠ൣғͰ͔͠ಈతʹ6*ͷมߋ͕Ͱ͖·ͤΜ w ֤ϓϥοτϑΥʔϜͰ࣮૷΋ඞཁʹͳΓ·͢ w ͦΕͧΕʹ࠷దͳ6*Λద༻Ͱ͖ΔͷͰϝϦοτͰ΋͋Γ·͢

    w ͜ΕΒ͕໰୊ʹͳΔ৔߹ʹ͸8FC7JFXͷํ͕༗ޮͰ͢
  49. ·ͱΊ

  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*ͷ࣮૷ϋʔυϧΛԼ͛Δ͜ͱ͕Ͱ ͖·͢
  51. 51 w ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠