2021/10/1に開催されたAfter Party iOSDC Japan 2021の登壇資料です
νʔϜͰSwiftUIΛॻͨ͘Ίʹ~ಡΈ͘͢อक͍͢͠SwiftUIͷઃܭʹ͍ͭͯߟ͑ͨ͜ͱ~After Party iOSDC Japan 2021 / Atsuya Sato
View Slide
ࠤ౻ ರ (͋ͭ)@n_atmark• iOS / Android ΤϯδχΞ• ݱࡏΫοΫύουAndroidΞϓϦͷʮ͔͍ͷʯλϒΛӶҙ։ൃத2019/042021/01• ΫοΫύου৽ଔೖࣾ• ങࣄۀຊ෦ ങϓϩμΫτ։ൃ෦• ΫοΫύουϚʔτͷྲྀ௨͚ͷαʔϏε։ൃɺϋʔυΣΞ։ൃͳͲʹैࣄ• ΫοΫύουࣄۀຊ෦ ങαʔϏε։ൃ෦• ΫοΫύουiOSΞϓϦͷʮ͔͍ͷʯλϒͷ։ൃʹैࣄ
ΞδΣϯμ● എܠ● ։ൃ͍ͯ͠ΔϓϩμΫτͷհ● νʔϜϓϩμΫτͷنײ● SwiftUIΛ༻͍ͯ1ؒ։ൃͯ͠Έͯ● ಡΈͯ͘͢อक͍͢͠ઃܭʹ͍ͭͯߟ͑ͨࣄ● ౷Ұͨ͠ॻ͖ํͰϨΠΞτΛઃܭ͢ΔͨΊʹ● ෳࡶɾଟ༷ͳ݅ʹରॲ͢Δ● ྑ͍ઃܭͷҙࣝΛνʔϜਁಁ͢ΔͨΊʹ● ·ͱΊ
։ൃ͍ͯ͠ΔϓϩμΫτͷհ● എܠ● ։ൃ͍ͯ͠ΔϓϩμΫτͷհ● νʔϜϓϩμΫτͷنײ● SwiftUIΛ༻͍ͯ1ؒ։ൃͯ͠Έͯ
ΫοΫύουΞϓϦʮങ͍ػೳʯͷհ
ΫοΫύουΞϓϦʮങ͍ػೳʯͷհ৯ࡐ͔ΒϨγϐ͕୳ͤΔ৯ࡐΛങͬͨਓ͕࣮ࡍʹ࡞ͬͨྉཧΛࢀߟʹͰ͖Δ
ΫοΫύουΞϓϦʮങ͍ػೳʯͷհレシピから必要な⾷材を購⼊できる
νʔϜϓϩμΫτͷنײ● എܠ● ։ൃ͍ͯ͠ΔϓϩμΫτͷհ● νʔϜϓϩμΫτͷنײ● SwiftUIΛ༻͍ͯ1ؒ։ൃͯ͠Έͯ
νʔϜͱ։ൃ͍ͯ͠ΔϓϩμΫτʹ͍ͭͯ● ങαʔϏε։ൃ෦● ʮϨγϐʯ×ʮങ͍ʯʹΑͬͯ৽ͨͳՁΛੜΈग़͢ઓ● ΫοΫύουiOSΞϓϦͷʮങ͍ػೳʯ● ΤϯδχΞ6ਓ (iOS 3ɺαʔόʔαΠυ3)● + ඇఆظͰΠϯλʔϯੜ͕νʔϜʹδϣΠϯ
ΫοΫύουΞϓϦʮങ͍ػೳʯͷ։ൃ● ϚϧνϞδϡʔϧԽ͞Ε͍ͯΔΫοΫύουΞϓϦͷFeature ModuleͷҰͭͱͯ͠ػೳ։ൃ (KaimonoϞδϡʔϧ)
https://speakerdeck.com/giginet/da-gui-mo-naapurifalsemarutimoziyurugou-cheng-falseshi-jian⼤規模なアプリのマルチモジュール構成の実践
ΫοΫύουΞϓϦʮങ͍ػೳʯͷ։ൃ● ΄΅શͯSwiftUIͰ։ൃத● VIPERΞʔΩςΫνϟͷ͏ͪɺView෦ͷΈͰSwiftUIΛར༻͢Δઃܭ
https://techlife.cookpad.com/entry/2021/01/18/kaimono-swift-uiSwiftUI を活⽤した「レシピ」×「買い物」の新機能開発
ΫοΫύουΞϓϦʮങ͍ػೳʯͷ։ൃ● Feature ModuleͷҰͭͱͯ͠ػೳ։ൃ● VIPERΞʔΩςΫνϟͷ͏ͪɺView෦ͷΈͰSwiftUIΛར༻͢Δઃܭ● 20203݄͔Β։ൃ (202010݄ʹҰൠϦϦʔε)● ࠷αϙʔτOSiOS 13.3
ΫοΫύουΞϓϦʮങ͍ػೳʯͷ։ൃ● Kaimono ϞδϡʔϧԼͷίʔυߦ● 45,953ߦ (ΫοΫύουiOSશମͷ15%)● UIHostingViewControllerͰϥοϓ͞ΕͨRootView● 35ը໘● ผϑΝΠϧʹΓग़͞ΕͨSwiftUIίϯϙʔωϯτ● 149ݸ
SwiftUIΛ༻͍ͯ1ؒ։ൃͯ͠Έͯ● എܠ● ։ൃ͍ͯ͠ΔϓϩμΫτͷհ● νʔϜϓϩμΫτͷنײ● SwiftUIΛ༻͍ͯ1ؒ։ൃͯ͠Έͯ
1લͷঢ়گ● iOSDC Japan 2020Ͱൃදͨ͠λΠϛϯά … ͪΐ͏Ͳങ͍ػೳϦϦʔε࣌ظͩͬͨ
https://speakerdeck.com/yujif/iosdc-japan-2020-day-2-cookpadクックパッドが、⾰新的な⽅法でまったく新しい買い物体験を皆様にお届けします
1લͷঢ়گ● SwiftUIͷϝϦοτΛײ͍ͯͨ͡෦● ίʔυྔ͕ݮΔ● ෳࡶɾଟ༷ͳ݅ʹର͔ͯ͠Γ͘͢ॻ͚Δ● ίϯϙʔωϯτ୯ҐͰ͍ճ͠ɾվม͕͍͢͠
ΞϓϦΛϦϦʔε͔ͯ͠Β1։ൃ͍ͯ͘͠தͰͷมԽ● ίʔυϕʔεͷڊେԽ● Feature ModuleͷதͰίʔυߦVIPERγʔϯ࠷େʹ● iOS 14ɾiOS 15ͷϦϦʔε● iOS 13ɺ14ɺ15ͷ3όʔδϣϯαϙʔτʹ● νʔϜͷ֦େ● ϝΠϯͰ։ൃ͢ΔΤϯδχΞ૿͑ͨ● ΠϯλʔϯੜܞΘΔΑ͏ʹ
1։ൃͯ͠Έͯײ͍ͯ͡Δ՝● iOS 13ରԠʹΑΔফগͳ͔Βͣൃੜ͢Δ● iOS 13͚ͩڍಈ͕ҧ͏෦͕গͳ͔Βͣ͋Δ● ύϑΥʔϚϯεΛ٘ਜ਼ʹ͍ͯ͠ΔՕॴ݁ߏ͋Δ● LazyV/HStackɺLazyV/HGrid͑ͯͳ͍😢
1։ൃͯ͠Έͯײ͍ͯ͡Δ՝● ॊೈੑ͕ߴ͍ͨΊɺUIͷΈํ͕ਓʹΑͬͯมΘΔ● ྫ: ཁૉΛॏͶ͍ͨͱ͖ͷZStackɺbackgroundɺoverlayͷ͍͚● ྫ: εϖʔεͷ։͚ํ● ίϯϙʔωϯτׂͷཻ
Q: ࠓޙSwiftUIΛऔΓೖΕͨ։ൃΛ͢Δʁ৭ʑফײͯͦ͡͏͚ͩͲ…?
Q: ࠓޙSwiftUIΛऔΓೖΕͨ։ൃΛ͢ΔʁA: Yes
ࠓޙSwiftUIΛऔΓೖΕͨ։ൃΛ͢Δʁෆ֬ఆͳϢʔβʔମݧΛ͍ͪૣཱ֬͘͢ΔͨΊʹʮ࡞Γ͍͢ɾյ͍͢͠ʯͷॏཁSwiftUIΛ͏͜ͱͰ໌Β͔ʹ͜ͷ෦ૣ͘ͳ͍ͬͯΔ
ࠓޙSwiftUIΛऔΓೖΕͨ։ൃΛ͢ΔʁࠓޙνʔϜͰSwiftUIΛ࠾༻͍ͯͨ͘͠Ίʹ…SwiftUIΛͬͯײ͍ͯ͡Δ՝ΛҰͭͣͭऔΓআ͍͍͖͍ͯͨ💪
ಡΈͯ͘͢อक͍͢͠SwiftUIͷઃܭʹ͍ͭͯߟ͑Δ
今⽇のトピックでは取り扱いません
αʔϏεಛੑΛߟ͑Δ● ʮ͔͍ͷʯλϒͷߏجຊॎεΫϩʔϧͷը໘● Viewߏஙͷࡍͷجຊܗͱͯ͠ScrollViewͷதʹཁૉΛ٧Ί͍ͯ͘ը໘͕΄ͱΜͲ
αʔϏεಛੑΛߟ͑Δ● ෳࡶଟ༷ͳঢ়ଶ● ྫ: จޙͷड͚औΓʹؔ͢Δঢ়ଶ
αʔϏεಛੑʹجͮ͘՝● ը໘͕ॎʹ͘ͳΓ͍͢● ίϯϙʔωϯτͷཻॻ͖ํ͕౷Ұ͞Εͯͳ͍ͱಡΈʹ͍͘● ঢ়ଶ͕૿͑Δͱɺذͦͷ૿͑Δ
ಡΈ͘͢อक͍͢͠ઃܭ● ઃܭʹݸਓ͕ࠩ͋·Γग़ͳ͍Α͏ʹ͍ͨ͠● ͕݅ෳࡶʹͳͬͯಡΊΔΑ͏ʹ͓͖͍ͯͨ͠● ͕݅ෳࡶͳը໘ͷอकΛ༰қʹ͍ͨ͠● ྑ͍ઃܭͷҙࣝΛνʔϜʹਁಁ͍ͤͨ͞
౷Ұͨ͠ॻ͖ํͰϨΠΞτΛઃܭ͢ΔͨΊʹ● ಡΈͯ͘͢อक͍͢͠ઃܭʹ͍ͭͯߟ͑ͨࣄ○ ౷Ұͨ͠ॻ͖ํͰϨΠΞτΛઃܭ͢ΔͨΊʹ○ ෳࡶɾଟ༷ͳ݅ʹରॲ͢Δ○ ྑ͍ઃܭͷҙࣝΛνʔϜਁಁ͢ΔͨΊʹ
౷Ұͨ͠ॻ͖ํͰϨΠΞτΛઃܭ͢ΔͨΊʹ● RootViewͷbodyʹॻ͖͘༰Λఆ͍ٛͨ͠● ίϯϙʔωϯτԽͷཻʹؔͯ͠ڞ௨ೝࣝΛͱΓ͍ͨ
ϨΠΞτΈʹؔͯ͠struct KaimonoCartView: View {var body: some View {ScrollView {orderDeliverySectioncartPriceSectionpickupNameSettingSectiondeliveryInformationSectionpickupStepsSection}}}
ϨΠΞτΈʹؔͯ͠● ը໘Λҙຯͷ͋Δ·ͱ·ΓͰSectionʹׂ● ෳίϯϙʔωϯτΛͱΓ·ͱΊΔ● ίϯϙʔωϯτؒͷϚʔδϯௐΛ ߦ͏ͨΊͷϨΠϠʔ
ϨΠΞτΈʹؔͯ͠● ը໘Λҙຯͷ͋Δ·ͱ·ΓͰSectionʹׂ● ׂͨ͠SectionRootViewʹඥͮ͘ (ෳը໘Ͱར༻͠ͳ͍)● SectionΛෳը໘Ͱ͍ͨ͘ͳͬͨΒίϯϙʔωϯτԽͷཻΛݟ͢
SectionͷׂҐஔͷܾΊํ● σβΠφʔͱ૬ஊ͠ͳ͕ΒηΫγϣϯׂҐஔΛܾΊΔΑ͏ʹ͢Δ● Figma্ͰσβΠϯελΠϧʹԊͬͯάϧʔϐϯά͞Ε͍ͯΔͷͰɺσβΠφʔͷҙਤΛөͭͭ͠Sectionׂ
Atoms Molecules Moleculesͷू߹ମ OrganismsProductTile ProductsGrid PopularProductsSection
ίϯϙʔωϯτʹؔͯ͠● ίϯϙʔωϯτԽ͢Δͷʹؔͯ͠ผϑΝΠϧʹΓग़͠● ཻ● ΞτϛοΫσβΠϯͰݴ͏Molecules● ୯ମMoleculesΛෳฒͨঢ়ଶͷͷ※ΛίϯϙʔωϯτԽ● AtomsϨϕϧͷίϯϙʔωϯτԽͯ͠·ͤΜ
ίϯϙʔωϯτʹؔͯ͠● ίϯϙʔωϯτͷཻʹؔͯ͠νʔϜͰೝ͕ࣝऔΕ͍ͯΔͱίϛϡχέʔγϣϯ͕ͱΓ͍͢● Sectionͱͯ͠RootViewʹඥͮ͘ܗͰ༻ҙ͖͢ͷ● ίϯϙʔωϯτͱͯ͠Γग़͖͢ͷ● ίϯϙʔωϯτͱͯ͠Γग़͢߹● Ͳ͜·Ͱࡉׂ͔͘͢Δͷ͔
ෳࡶɾଟ༷ͳ݅ʹରॲ͢Δ● ಡΈͯ͘͢อक͍͢͠ઃܭʹ͍ͭͯߟ͑ͨࣄ○ ౷Ұͨ͠ॻ͖ํͰϨΠΞτΛઃܭ͢ΔͨΊʹ○ ෳࡶɾଟ༷ͳ݅ʹରॲ͢Δ○ ྑ͍ઃܭͷҙࣝΛνʔϜਁಁ͢ΔͨΊʹ
ෳࡶɾଟ༷ͳ݅ʹରॲ͢Δ
ෳࡶɾଟ༷ͳ݅ʹରॲ͢Δstruct KaimonoDeliveryDetailView: View {var body: some View {ScrollView {switch dataSource.delivery.deliveryStatus {case .accepting:headerSectionpickupNameSectionorderedProductsSectionif shouldShowPickupGuide { pickupGuideSection } // SectionͷτϧπϝacceptedButtonSectionordersSectioncase .preparing:// ུ}}}}
ෳࡶɾଟ༷ͳ݅ʹରॲ͢Δ● RootViewͷbodyʹSectionΛྻڍ͢ΔΑ͏ʹͨ͜͠ͱͰݟ௨͕͠ྑ͍● ঢ়ଶʹԠͨ͡Sectionͷग़͚͠ɺಛఆ݅ʹԠͨ͡Sectionͷτϧπϝ͔Γ͍͢● body͕ը໘ϨΠΞτͷઃܭਤͱͯ͠ػೳ͢Δ
ਖ਼֬ʹಈ࡞ͤ͞ΔͨΊʹ● ίʔυ্Ͱͷݟ௨͘͢͠͠ͳ͕ͬͨ…● ෳͷ݅Λߟྀͨ͠ը໘ʹ͓͚ΔϢʔβʔঢ়ଶͷ࠶ݱ͍͠● ը໘͕յΕ͍ͯͳ͍͜ͱΛಈ࡞֬ೝ͢Δ● ࣮ʹೖΔ·Ͱʹ֘ը໘ΛදࣔͰ͖ΔΑ͏ʹͳΔ·Ͱ͕͔͔࣌ؒΔ
ਖ਼֬ʹಈ࡞ͤ͞ΔͨΊͷऔΓΈ
https://speakerdeck.com/aomathwift/ji-neng-gotonidong-zuo-suruminiapuridepurebiyusaikuruwobao-su-nisitahua機能ごとに動作するミニアプリでプレビューサイクルを爆速にした話
ਖ਼֬ʹಈ࡞ͤ͞ΔͨΊͷऔΓΈextension SandboxScene {static let kaimonoTopForJustSetupCompleted = SandboxScene(sceneName: "τοϓʢडऔॴઃఆࡁɾະจʣ") { initializer inlet environment = StubbableEnvironment()initializer.initialize(environment)environment.registerClientResponses(fixtures, overrideFixtures: [.orders(.empty),.userOrderedDeliveries(.empty),])ɹ let viewController = KaimonoTopViewBuilder.build(environment: environment)return viewController}static let kaimonoTopForDeliveriesUnavailable = SandboxScene(sceneName: "τοϓʢۙͷૹͳ͠ʣ") { initializer in// ུ}}private let fixtures: [Fixture] = [.cart(.normal),.promotedDeliveryProducts(.ordinary),.me(.userLocation),.feature(.popularProducts),// ུ]
ਖ਼֬ʹಈ࡞ͤ͞ΔͨΊͷऔΓΈprivate let fixtures: [Fixture] = [.cart(.normal),.promotedDeliveryProducts(.ordinary),.me(.userLocation),.feature(.popularProducts),// ུ]environment.registerClientResponses(fixtures, overrideFixtures: [.orders(.empty),.userOrderedDeliveries(.empty),])● ը໘දࣔʹඞཁͳϨεϙϯεελϒΛFixtureԽ● ϕʔεͷFixtureͱɺ݅͝ͱͷoverrideFixturesΛ༻ҙ● ϛχΞϓϦͷ݅͝ͱͷը໘දࣔΛ؆୯ʹఆٛͰ͖ΔΑ͏ʹ
ྑ͍ઃܭͷҙࣝΛνʔϜਁಁ͢ΔͨΊʹ● ಡΈͯ͘͢อक͍͢͠ઃܭʹ͍ͭͯߟ͑ͨࣄ○ ౷Ұͨ͠ॻ͖ํͰϨΠΞτΛઃܭ͢ΔͨΊʹ○ ෳࡶɾଟ༷ͳ݅ʹରॲ͢Δ○ ྑ͍ઃܭͷҙࣝΛνʔϜਁಁ͢ΔͨΊʹ
ྑ͍ઃܭͷҙࣝΛνʔϜਁಁ͢ΔͨΊʹ● υΩϡϝϯτԽ● υΩϡϝϯτඋձ● ݟ·ͱΊࣾϒϩά● SwiftUIϨΠΞτʹؔ͢Δௐࠪ● ઃܭʹ͍ͭͯޠΔձ
ྑ͍ઃܭͷҙࣝΛνʔϜਁಁ͢ΔͨΊʹ
·ͱΊ● ౷Ұͨ͠ॻ͖ํͰϨΠΞτΛઃܭ͢ΔͨΊʹ● ϨΠΞτઃܭΛ౷ҰԽ● ίϯϙʔωϯτԽͷཻΛ໌֬ʹ● ෳࡶɾଟ༷ͳ݅ʹରॲ͢Δ● ը໘ϨΠΞτͷઃܭਤͱͯ͠RootViewͷbodyΛར༻● ಈ࡞֬ೝͷͨΊʹϛχΞϓϦΛ׆༻● ྑ͍ઃܭͷҙࣝΛνʔϜਁಁ͢ΔͨΊʹ● ఆظతʹઃܭʹ͍ͭͯٞ͢ΔΛઃ͚Δ● υΩϡϝϯτԽ͢Δ͜ͱʹΑͬͯڞ௨ೝࣝΛऔΔ