Slide 1

Slide 1 text

େن໛ΞϓϦʹ͓͚Δ Xcode Previews࣮༻Խ ·ͰͷಓͷΓ @ikesyo / Sho Ikeda iOSDC Japan 2025 2025-09-20 Sat (day1) #iosdc #c 1

Slide 2

Slide 2 text

@ikesyo / Sho Ikeda • ͍͚͠ΐʔʗ஑ా ᠳ • LINEϠϑʔגࣜձࣾ / MDX Team of LINE app • https://github.com/ikesyo • Swift Contributor / Various OSS Contributor • iOSDCొஃ • LT5ճʢ2016-2019ɺ2023ʣ • ϨΪϡϥʔτʔΫ͸ࠓճ͕ॳΊͯ 2

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

େن໛ΞϓϦ = LINE 4

Slide 5

Slide 5 text

ΞδΣϯμ 1. LINE iOSΞϓϦͷഎܠͱ՝୊ 2. Static FrameworkͱXcode 16 3. ࣮༻Խ·Ͱͷ՝୊ͱղܾࡦ 4. ݱࡏͷঢ়گͱࠓޙͷల๬ 5

Slide 6

Slide 6 text

LINE iOSΞϓϦͷഎܠͱ՝୊ 6

Slide 7

Slide 7 text

LINE iOSΞϓϦͷಛ௃ • ϚϧνϞδϡʔϧߏ੒: ਺ඦͷϞδϡʔϧ • Static Framework: ߴ଎ىಈͷͨΊͷબ୒ • ڊେͳΞϓϦέʔγϣϯλʔήοτ: Previewʹ͸ෆ޲͖ 7

Slide 8

Slide 8 text

LINE iOSͷ։ൃن໛ʢ2025೥9݄ݱࡏʣ ߲໨ ਺ ίʔυߦ਺ 250ສߦҎ্ XcodeϓϩδΣΫτ਺ 600Ҏ্ Xcodeλʔήοτ਺ ʢ˞ςετΛআ͘ʣ 800Ҏ্ ϏϧυࡁΈϑϨʔϜϫʔΫ਺ 150Ҏ্ 8

Slide 9

Slide 9 text

LINE iOSͱStatic Framework • ඇৗʹଟ͘ͷϞδϡʔϧ͕͋ΔͷͰɺDynamic LinkͩͱΞϓϦͷىಈ ͕࣌ؒ஗͘ͳͬͯ͠·͏ • → Static FrameworkԽͯ͠࢖༻ • ͔͠͠Xcode 15.x·Ͱ͸Dynamic FrameworkͰͳ͍ͱͦͷϞδϡʔ ϧΛPreviewͰ͖ͳ͔ͬͨ • Xcode Previewsͷ࣮ߦΤϯδϯͷ੍ݶ • → LINE iOSΞϓϦͰ͸ࣄ্࣮Preview͕࢖͑ͳ͔ͬͨ 9

Slide 10

Slide 10 text

Static FrameworkͱXcode 16 10

Slide 11

Slide 11 text

Static FrameworkͱXcode 16 • Xcode 15·Ͱ: Dynamic FrameworkͰ͔͠Preview͕ಈ͔ ͳ͍ • Xcode 16.0Ͱ͍ͭʹStatic Frameworkͷαϙʔτ͕௥Ճ͞Ε ͨ͜ͱΛ೺Ѳ͠ɺݕূΛ։࢝ • https://developer.apple.com/ documentation/xcode-release- notes/xcode-16-release-notes 11

Slide 12

Slide 12 text

Static FrameworkͱXcode 16 • ·ͣ͸طଘͷϞδϡʔϧͰ͸ͳ͘ɺඞཁ ࠷খݶͷϞδϡʔϧΛ࡞ͬͯࢼͯ͠ΈΔ • ͔֬ʹStatic FrameworkͰ΋Preview ͕ಈ͘͜ͱΛ֬ೝ ! • ͨͩ͠ɺ৽͍࣮͠ߦΤϯδϯΛ࢖͏ ඞཁ͕͋Δ • Editor → Canvas → Use Legacy Previews Execution ΛΦϑʹ͢ ΔʢແޮԽʣ 12

Slide 13

Slide 13 text

Static FrameworkͱXcode 16 • ͔͠͠طଘͷϞδϡʔϧͰ΋ݕূͯ͠ΈΔͱɺavailableΛ࢖͏ͱ Preview͕Ϋϥογϡ͢Δ͜ͱΛൃݟ • Өڹൣғ͕େ͖͘ɺ͜ͷ໰୊͕௚Βͳ͍ͱLINEΞϓϦͰ͸࣮༻Ͱ͖ ͳ͍Ϩϕϧ • AppleʹFeedbackΛग़ͨ͠ͷͰɺमਖ਼Λ଴ͭ೔ʑ • FB16037528: Xcode Previews in static frameworks (using the new Execution) failed to run if availability checking is used 13

Slide 14

Slide 14 text

FB16037528 import SwiftUI struct MyLabel: View { var body: some View { if #available(iOS 18, *) { Text("aaa") } } } #Preview { MyLabel() } 14

Slide 15

Slide 15 text

Static FrameworkͱXcode 16 • Xcode 16.3Ͱ͍ͭʹͦͷόά ͕मਖ਼͞ΕΔ ! • https:// developer.apple.com/ documentation/xcode- release-notes/xcode-16_3- release-notes Fixed: Previews in static libraries that used @availability annotations would fail with a symbol not found error. This should now be resolved but if you continue to experience this please file a new feedback with fresh diagnostics since we’ve added new logging for triage. (140898299) (FB16037528) 15

Slide 16

Slide 16 text

16

Slide 17

Slide 17 text

ΊͰͨ͠ΊͰͨ͠ 17

Slide 18

Slide 18 text

׬ 18

Slide 19

Slide 19 text

ͦΜͳ༁͸ͳ͍ 19

Slide 20

Slide 20 text

࣮༻Խ·Ͱͷ՝୊ͱղܾࡦ 20

Slide 21

Slide 21 text

࣮༻Խ·Ͱͷ՝୊ͱղܾࡦ • ϏϧυεΩʔϜ؅ཧ • ϓϨϏϡʔର৅͔Βґଘؔ܎ΛݮΒ͢ • ը૾΍ϩʔΧϦθʔγϣϯͳͲͷϦιʔεͷಡΈࠐΈ 21

Slide 22

Slide 22 text

ϏϧυεΩʔϜ؅ཧ 22

Slide 23

Slide 23 text

ϏϧυεΩʔϜ؅ཧ • ಛఆͷUIϞδϡʔϧʢϓϨϏϡʔର ৅ʣΛϓϨϏϡʔ͢ΔɾϏϧυ͢Δ ʹ͸ɺͦͷϞδϡʔϧʢʹλʔήο τʣ༻ͷϏϧυεΩʔϜΛ༻ҙ͢Δ ඞཁ͕͋Δ 23

Slide 24

Slide 24 text

ϏϧυεΩʔϜ؅ཧ • ͔͠͠Ϟδϡʔϧ͕਺ඦݸ͋ΔͷͰɺ͢΂ͯͷλʔήοτͷϏϧυ εΩʔϜΛ࡞੒͢Δͷ͸ඇݱ࣮తʢϏϧυεΩʔϜબ୒ͷUI͕࢖͍ ෺ʹͳΒͳ͍ʣ • UIϞδϡʔϧʹߜͬͨͱͯ͠΋·ͩ਺͕ଟ͍ • جຊతʹνʔϜຖʹ୲౰͢ΔϞδϡʔϧ͕෼͔Ε͍ͯΔͷͰɺ ࣗνʔϜʹؔ܎ͳ͍Ϗϧυ͕͋ΔͱϊΠζʹͳͬͯ͠·͏ → ඞཁͳϏϧυεΩʔϜ͚ͩΛ࡞੒ɾදࣔͰ͖ΔΑ͏ͳ࢓૊Έ͕ඞཁ 24

Slide 25

Slide 25 text

ղܾࡦ: XcodeGenͷScheme Managementͷshared/ isShown • https://github.com/yonaskolb/XcodeGen/blob/master/Docs/ProjectSpec.md#scheme- management • shared: Bool - indicates whether the scheme is shared • isShown: Bool - indicates whether the scheme is shown in the scheme list • sharedͳϏϧυεΩʔϜͱͯ͠࡞੒ɾڞ༗ͭͭ͠ɺσϑΥϧτͰ͸ඇදࣔͷঢ়ଶʹ͓ͯ͘͠ • → ֘౰ͷϞδϡʔϧΛ։ൃɾϓϨϏϡʔ͍ͨ͠։ൃऀ͕ࣗ෼ͷखݩͰ͚ͩදࣔঢ়ଶʹมߋ͢ Δ • ϏϧυεΩʔϜͷදࣔɾඇදࣔͷঢ়ଶ͸Ϣʔβʔ͝ͱʹ؅ཧ͞ΕΔ • *.xcodeproj/xcuserdata/*.xcuserdatad/xcschemes/xcschememanagement.plist 25

Slide 26

Slide 26 text

26

Slide 27

Slide 27 text

ղܾࡦ: XcodeGenͷScheme Managementͷshared/ isShown ͜ͷઃఆΛߦ͏ͨΊͷXcodeGenͷςϯϓϨʔτΛ༻ҙ͠ɺϓϨϏϡʔ ͍ͨ͠ϞδϡʔϧͰࢦఆ͢Δɻ # templates.yml targetTemplates: PreviewableTarget: scheme: management: shared: true isShown: false # project.yml targets: SomeUIModule: templates: - Framework - PreviewableTarget 27

Slide 28

Slide 28 text

ϓϨϏϡʔର৅͔Βґଘؔ܎ΛݮΒ͢ 28

Slide 29

Slide 29 text

ϓϨϏϡʔର৅͔Βґଘؔ܎ΛݮΒ͢ LINEͷϚϧνϞδϡʔϧߏ੒ 29

Slide 30

Slide 30 text

ϓϨϏϡʔର৅͔Βґଘؔ܎ ΛݮΒ͢ • UIϞδϡʔϧ͕LogicϞδϡʔϧʹґଘ͢ Δߏ੒ • LogicϞδϡʔϧ͕େྔͷґଘΛ࣋ͪࠐ Ή͜ͱ͕͋Δ • UIϞδϡʔϧͰͷPreview࣮ߦ࣌ʹ ϏϧυΤϥʔ΍ϨϯμϦϯά໰୊͕ى͖ Δ͜ͱ͕͋Δ • Previewը໘͕ϩʔσΟϯάͷ··Կ ΋ى͖ͳ͍ 30

Slide 31

Slide 31 text

ղܾࡦ: PreviewableUIϞδϡʔϧ • PreviewableUI: ϩδοΫϨεͳUIί ϯϙʔωϯτͷΈ • ґଘੑ஫ೖ: ϩδοΫ΍ΞΫγϣϯ͸ Ϋϩʔδϟ΍όΠϯσΟϯάͰ஫ೖ • ܰྔԽ: UIίϯϙʔωϯτߏங༻ͷ࠷ খݶͷґଘʹཹΊΔ → ґଘ͕ଟ͍طଘϞδϡʔϧͰϓϨϏ ϡʔΛ࢖͍͍ͨ࣌΍ɺ৽نͷϞδϡʔϧ ʹରͯ͠ਪ঑ 31

Slide 32

Slide 32 text

ը૾΍ϩʔΧϦθʔγϣϯͳͲͷ ϦιʔεͷಡΈࠐΈ 32

Slide 33

Slide 33 text

ը૾΍ϩʔΧϦθʔγϣϯͳ ͲͷϦιʔεͷಡΈࠐΈ ΞϓϦશମɾଟ਺ͷϞδϡʔϧͰڞ༗͢ Δը૾΍ϩʔΧϦθʔγϣϯϦιʔε͸ ΞϓϦຊମʹຒΊࠐ·Ε͍ͯΔɻ • جຊతʹ͸ Bundle.main ͰΞΫηε ͢Δ • ͨͩ͠ίʔυੜ੒͍ͯ͠ΔΞΫηα ʔίʔυ͸ڞ௨Ϟδϡʔϧͱͯ͠ఆٛ ͍ͯ͠Δ 33

Slide 34

Slide 34 text

ը૾΍ϩʔΧϦθʔγϣϯͳ ͲͷϦιʔεͷಡΈࠐΈ → ͜ΕͩͱɺͦΕΒͷϦιʔεΛ࢖༻͢ΔUI ϞδϡʔϧͷPreviewͰΫϥογϡͨ͠Γɺϩ ʔΧϥΠζ͕ਖ਼͘͠දࣔ͞Εͳ͘ͳΔɻ • Xcode Previewsͷ࣮ߦ؀ڥ͸ XCPreviewAgent.app ͱ͍͏ΞϓϦ͕ϗε τʹͳ͍ͬͯͯɺ Bundle.main ͸͜ͷΞϓ ϦΛࢦ͢ • → ͜ͷΞϓϦʹ͸౰વඞཁͳϦιʔε͕ຒ Ίࠐ·Ε͍ͯͳ͍ͷͰɺϦιʔε͕ݟ͔ͭΒ ͳ͍ 34

Slide 35

Slide 35 text

ղܾࡦ: Development AssetsΛ࢖ͬͯΞΫηαʔίʔυͷڞ ௨ϞδϡʔϧʹϦιʔεΛຒΊࠐΉ • https://developer.apple.com/documentation/xcode/ previewing-your-apps-interface-in-xcode#Reduce-your-app- size-with-development-assets • Preview༻ͷը૾Ϧιʔε΍ϞοΫσʔλͳͲͷɺ੡඼൛ͷΞϓ Ϧʹ͸ؚΊͳ͍Ξηοτ΍ιʔείʔυͷύεΛࢦఆ͢Δ΋ͷ • ArchiveϏϧυʢxcodebuild ͷ archive ΞΫγϣϯʣ࣌ʹ͸Ϗ ϧυର৅͔Βআ֎͞ΕΔ 35

Slide 36

Slide 36 text

ղܾࡦ: Development AssetsΛ࢖ͬ ͯΞΫηαʔίʔυͷڞ௨Ϟδϡʔϧʹ ϦιʔεΛຒΊࠐΉ • Xcode GUI: ϓϩδΣΫτ಺ͷ֤λʔ ήοτͷઃఆͷ General → Development Assets • DEVELOPMENT_ASSET_PATHS ͱ͍͏ Build SettingͰ΋ઃఆՄೳ 36

Slide 37

Slide 37 text

ղܾࡦ: Development AssetsΛ࢖ͬ ͯΞΫηαʔίʔυͷڞ௨Ϟδϡʔϧʹ ϦιʔεΛຒΊࠐΉ • ͜ΕΛ࢖͍ɺීஈ͸ΞϓϦຊମʹຒ Ίࠐ·Ε͍ͯΔϦιʔεΛɺΞΫηα ʔίʔυͷڞ௨Ϟδϡʔϧࣗମʹຒ ΊࠐΜͰ͠·͏ • ·ͨੜ੒ίʔυͰ Bundle.main ʹ ΞΫηε͢Δ෦෼Λɺڞ௨Ϟδϡ ʔϧࣗମͷBundleʹஔ͖׵͑Δ 37

Slide 38

Slide 38 text

ղܾࡦ: Development AssetsΛ࢖ͬͯΞΫηαʔίʔυͷڞ ௨ϞδϡʔϧʹϦιʔεΛຒΊࠐΉ #if DEBUG let isInPreviews = ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" if isInPreviews { // Xcode Previewsͷ࣮ߦ؀ڥͰ͸ `Bundle(for: BundleFinder.self)` ͕ // Derived Dataͷதͷ `Debug-iphonesimulator` (`BUILT_PRODUCTS_DIR` ૬౰) Λฦ͢ɻ if let bundle = Bundle(for: BundleFinder.self) .url(forResource: "SharedUICommonMediaAssets", withExtension: "framework") .flatMap(Bundle.init(url:)) { return bundle } } return Bundle.main #else return Bundle.main #endif 38

Slide 39

Slide 39 text

ղܾࡦ: Development AssetsΛ࢖ͬͯΞΫηαʔίʔυͷڞ ௨ϞδϡʔϧʹϦιʔεΛຒΊࠐΉ Ұ෦ͷϞδϡʔϧͰΞΫηαʔίʔυͷڞ௨Ϟδϡʔϧܦ༝Ͱ͸ͳ͘ɺ UIImage(named: ...) Ͱ௚઀ Bundle.main Λࢀর͍ͯͨ͠΋ͷ΋௚͢ɻ // NG: σϑΥϧτͷ `in bundle: Bundle? = nil` ͩͱ // `Bundle.main` ͕࢖ΘΕͯ͠·͏ɻ UIImage(named: "button_type_lightgreen_normal") // → nil // OK: PreviewͰ͸ࠩ͠ସ͑ͨBundle͕࢖ΘΕΔɻ // `UIImage(named: "button_type_lightgreen_normal", in: bundle)` UIImage.buttonTypeLightgreenNormal 39

Slide 40

Slide 40 text

ݱࡏͷঢ়گͱࠓޙͷల๬ 40

Slide 41

Slide 41 text

ݱࡏͷঢ়گ • ✅ Static FrameworkͰͷಈ࡞ݕূ • ✅ ʢຊ೔঺հͨ͠Α͏ͳʣ༷ʑͳԼ४උ • ✅ ΨΠυϥΠϯͷ࡞੒ • ✅ ։ൃνʔϜ΁ͷΞφ΢ϯε • " ֤։ൃνʔϜɾػೳ։ൃͰͷ࣮ફ • ݱ࣌఺ͰPreviewableTarget͸5݅ʢΞφ΢ϯε͔Β3݅૿Ճʣ 41

Slide 42

Slide 42 text

ࠓޙͷల๬ • Xcode Previewsͷӹʑͷൃలʢػೳ௥Ճͱ҆ఆੑվળʣ΁ͷظ଴ • ։ൃνʔϜ͔Βͷٙ໰ɾ࣭໰΁ͷճ౴ˠΨΠυϥΠϯ΁ͷ൓ө • ࣾ಺ษڧձͰͷൃදɺϫʔΫγϣοϓͷ࣮ࢪͳͲ • ׆༻ঢ়گͷτϥοΩϯά • PreviewableTargetͷ਺ɺιʔείʔυதͷPreviewͷ਺ɺͳͲ • ͦͷͨΊͷܭଌπʔϧͷ࡞੒΍ఆظ࣮ߦ 42

Slide 43

Slide 43 text

ࠓޙͷల๬ • Mergeable Libraryͷݕ౼ • Static FrameworkͰ͋Δ͕ނʹɺґଘϞδϡʔϧͷଟ͞ʹΑͬͯPreview ͷಈ࡞͕ෆ҆ఆʹͳͬͨΓɺϞδϡʔϧ෼ׂΛڧ͍ΒΕͨΓ͍ͯ͠Δ ʢՄೳੑ͕͋Δʣ • → Mergeable LibraryΛಋೖ͢ΔͱɺDebugϏϧυͰ͸Dynamic Linkʹ ͳΔͷͰɺϦϯΫىҼͷ໰୊ΛܰݮͰ͖Δ͔΋͠Εͳ͍ • ৄ͘͠͸ Mergeable LibraryͰ ߴ଎ͳΞϓϦىಈΛ࣮ݱ͠Α͏ʂ Λࢀর ʢ@giginetʣ 43

Slide 44

Slide 44 text

·ͱΊ 44

Slide 45

Slide 45 text

·ͱΊ • LINE iOSΞϓϦͱ͍͏େن໛ͳΞϓϦͷ։ൃʹXcode PreviewsΛಋೖ࢝͠Ίͨ • ͲͷΑ͏ͳ՝୊͕͋Δͷ͔Λચ͍ग़͠ɺͦΕΒΛௐࠪɾղܾ͠ɺXcodeʹ΋ϑΟʔυ όοΫͯ͠όάΛमਖ਼ͯ͠΋Βͬͨ • Static FrameworkͰXcode PreviewsΛ׆༻͢Δʹ͸·ͩ·ͩϋʔυϧ͕͋Δ • Previewsࣗମͷಈ࡞҆ఆੑͷ໰୊ʢ৽͍࣮͠ߦΤϯδϯͷJITίϯύΠϧɾϦϯΫʣ • Ϧιʔεόϯυϧͷѻ͍ • Mergeable Library͕վળࡦʹͳΓಘΔͷͰௐࠪɾݕ౼ΛਐΊ͍ͯ͘ • Զͨͪͷઓ͍͸͜Ε͔Βͩʂ 45

Slide 46

Slide 46 text

ࢀߟࢿྉ • Previews in Xcode | Apple Developer Documentation • Development Assets • Build settings reference | Apple Developer Documentation • Previewing your app’s interface in Xcode | Apple Developer Documentation • XcodeͷDevelopment Assets୳๚ @ikesyo • WWDC Videos • Build programmatic UI with Xcode Previews - WWDC23 - Videos - Apple Developer • Structure your app for SwiftUI previews - WWDC20 - Videos - Apple Developer • Mastering Xcode Previews - WWDC19 - Videos - Apple Developer • Mergeable Library • Configuring your project to use mergeable libraries | Apple Developer Documentation • Mergeable LibraryͰ ߴ଎ͳΞϓϦىಈΛ࣮ݱ͠Α͏ʂ @giginet 46

Slide 47

Slide 47 text

! Happy Xcode Previews 47

Slide 48

Slide 48 text

Thank you @ikesyo / Sho Ikeda 48