Slide 1

Slide 1 text

Swi$Ͱۀ຿վળϫʔΫγϣοϓ 2022/10/14 @ MoneyForward giginet 1/45

Slide 2

Slide 2 text

ࣗݾ঺հ • @giginet • LINEגࣜձࣾ σΟϕϩούʔΤΫεϖ ϦΤϯε։ൃνʔϜ • Core Contributor of Carthage/fastlane/ XcodeGen etc... • h=ps:/ /github.com/giginet • h=ps:/ /twi=er.com/giginet • ؾܰʹmenBon͍ͯͩ͘͠͞ʂ 2/45

Slide 3

Slide 3 text

ࠓ೔ͷ໨త • ۀ຿վળΛSwi%ͰͰ͖ΔΑ͏ʹ͠Α͏ʂ • Xcode 14͔Β࢖͑ΔΑ͏ʹͳͬͨSwi% Package Pluginʹ͍ͭͯ ֶ΅͏ • ۀ຿վળͷͨΊͷπʔϧΛ࡞ͬͯΈͯɺ໌೔͔Βͷۀ຿Λޮ཰ Խ͠Α͏ʂ 3/45

Slide 4

Slide 4 text

Swi$Ͱۀ຿վળ • Swi% PackageΛ࢖ͬͨΤίγεςϜ͕੔͖ͬͯͨ • ैདྷ͸ผͷݴޠͰߦ͖ͬͯͨࡉ͔ͳλεΫΛSwi%ʹ౷ҰͰ͖Δ • Shell Script • εΫϦϓτݴޠ • Rake, fastlaneͳͲͷλεΫϥϯφʔ 4/45

Slide 5

Slide 5 text

Swi$Ͱॻ͘ར఺ • iOS։ൃऀ͕ଞͷݴޠΛशಘ͢Δඞཁ͕ͳ͍ • Ruby΍Shell ScriptͳͲͷशಘେม • εΫϦϓτݴޠ͸ਏ͍ • ςετ͕ॻ͖΍͍͢ • XCTestͰςετ͕هड़Մೳ 5/45

Slide 6

Slide 6 text

Swi$ PackageΛ࢖ͬͨϢʔςΟϦςΟͷ࡞Γํ • executableTarget • Package Plugin(NEW!!!) 6/45

Slide 7

Slide 7 text

Swi$ͰίϚϯυϥΠϯπʔϧ • Swi% PackageͷexecutableΛ࢖ͬͯ؆୯ʹίϚϯυϥΠϯ ϢʔςΟϦςΟ͕࣮૷Ͱ͖Δ • Swi% ArgumentParserͳͲͷϢʔςΟϦςΟ΋ॆ࣮ 7/45

Slide 8

Slide 8 text

Swi$ Packageͷ࡞੒ $ mkdir MyUsefulTools $ cd MyUsefulTools $ swift package init Creating library package: MyUsefulTools Creating Package.swift Creating README.md Creating .gitignore Creating Sources/ Creating Sources/MyUsefulTools/MyUsefulTools.swift Creating Tests/ Creating Tests/MyUsefulToolsTests/ Creating Tests/MyUsefulToolsTests/MyUsefulToolsTests.swift 8/45

Slide 9

Slide 9 text

xcodeproj? • جૅ஌ࣝɿSwi% Package͸௨ৗͷXcodeϓϩδΣΫτͱҧͬͯ xcodeproj͕ͳ͍ʂ • ࣮͸XcodeͰσΟϨΫτϦΛ௚઀։͚·͢ 9/45

Slide 10

Slide 10 text

Package.swi+ // swift-tools-version: 5.7 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "MyUsefulTools", products: [ .executable(name: "greeting", targets: ["greeting"]), ], dependencies: [], targets: [ .executableTarget(name: "greeting"), ] ) 10/45

Slide 11

Slide 11 text

σΟϨΫτϦߏ੒ . ├── Package.swift ├── README.md ├── Sources │ └── greeting │ └── main.swift └── Tests └── greetingTests └── CommandTests.swift 11/45

Slide 12

Slide 12 text

σΟϨΫτϦߏ੒ . ├── Package.swift ├── README.md ├── Sources │ └── greeting │ └── main.swift └── Tests └── greetingTests └── CommandTests.swift 11/45

Slide 13

Slide 13 text

import Foundation let arguments = ProcessInfo.processInfo.arguments guard let name = arguments.last, arguments.count >= 2 else { fatalError("name is required") } print("Hello \(name)!") 12/45

Slide 14

Slide 14 text

$ swift run greeting giginet Building for debugging... Build complete! (0.08s) Hello giginet! 13/45

Slide 15

Slide 15 text

Package Plugin(NEW!!!) • Swi% Package ManagerͰ։ൃπʔϧΛ഑෍͢ΔͨΊͷ࢓૊Έ • ैདྷͷ։ൃπʔϧ͸ίϚϯυϥΠϯ্͔Β࣮ߦ͕ඞཁͩͬͨ ͕ɺPluginʹ͢Δ͜ͱͰɺXcode্͔Β؆୯ʹ։ൃϑϩʔʹऔΓ ࠐΉ͜ͱ͕Ͱ͖Δ 14/45

Slide 16

Slide 16 text

Package Pluginͷ෼ྨ • Command Plugin (SE-0332) • Build Tool Plugin (SE-0303) • Prebuild Command • Build Command 15/45

Slide 17

Slide 17 text

Command Pluginͱ͸ • Ϣʔβʔ͕ೳಈతʹ࣮ߦ͢ΔϓϥάΠϯ • Xcode্ͷGUI͔Β࣮ߦՄೳͳίϚϯυΛ഑෍Ͱ͖Δ • ࢖༻ྫɿ • ίʔυϑΥʔϚολ • υΩϡϝϯτࣗಈੜ੒ 16/45

Slide 18

Slide 18 text

17/45

Slide 19

Slide 19 text

Build Tool Pluginͱ͸ • Ϗϧυ࣌ʹࣗಈతʹ࣮ߦ͢ΔΑ͏ʹ͢ΔϓϥάΠϯ • ࣮ߦλΠϛϯάͰPrebuild CommandͱBuild Commandͷ2छ͕ ͋Δ • ैདྷͷRun Script PhaseΛ؆୯ʹ࠶഑෍Ͱ͖Δ + Swi;Ͱॻ͚Δ 18/45

Slide 20

Slide 20 text

• ྫɿ • Prebuild Command • ίʔυδΣωϨʔλ • Ϧιʔεͷੜ੒ • Build Command • linter 19/45

Slide 21

Slide 21 text

20/45

Slide 22

Slide 22 text

࡞ྫɿgiginet/Milepost • h#ps:/ /github.com/giginet/Milepost • GitͷϦϏδϣϯΛ؆୯ʹΞϓϦʹຒΊ ࠐΉBuild Tool Plugin • Ϗϧυ࣌ʹgitίϚϯυΛ࣮ߦͯ͠ revisionͳͲΛऔಘ • BundleʹϦϏδϣϯΛؚΜͩplistΛ ੜ੒ɺσίʔυͯ͠දࣔ 21/45

Slide 23

Slide 23 text

Milepost import PackageDescription let package = Package( name: "Milepost", targets: [ .target( name: "Milepost" ), .plugin( name: "PrepareMilepost", capability: .buildTool(), dependencies: ["bundle-generator"] ), .executableTarget( name: "bundle-generator", dependencies: [ .target(name: "Milepost"), ] ), ] ) 22/45

Slide 24

Slide 24 text

Milepost import PackageDescription let package = Package( name: "Milepost", targets: [ .target( name: "Milepost" ), .plugin( name: "PrepareMilepost", capability: .buildTool(), dependencies: ["bundle-generator"] ), .executableTarget( name: "bundle-generator", dependencies: [ .target(name: "Milepost"), ] ), ] ) 22/45

Slide 25

Slide 25 text

Milepost import PackageDescription let package = Package( name: "Milepost", targets: [ .target( name: "Milepost" ), .plugin( name: "PrepareMilepost", capability: .buildTool(), dependencies: ["bundle-generator"] ), .executableTarget( name: "bundle-generator", dependencies: [ .target(name: "Milepost"), ] ), ] ) 22/45

Slide 26

Slide 26 text

ࢀߟ • Meet Swi) Package Plugins • h4ps:/ /developer.apple.com/videos/play/wwdc2022/110359/ • Create Swi) Package Plugins • h4ps:/ /developer.apple.com/videos/play/wwdc2022/110401 23/45

Slide 27

Slide 27 text

Command Pluginͷ࣮૷ 24/45

Slide 28

Slide 28 text

Package.swi+ // swift-tools-version: 5.7 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "MyUsefulTools", products: [], dependencies: [], targets: [ .plugin( name: "CommandPlugin", capability: .command( intent: .custom(verb: "Run Something", description: "This is a sample command!"), permissions: [.writeToPackageDirectory(reason: "testing")] ) ) ] ) 25/45

Slide 29

Slide 29 text

σΟϨΫτϦߏ੒ . ├── Package.swift ├── Plugins │ └── MyCommand │ └── Command.swift ├── README.md ├── Sources │ └── greeting │ └── main.swift └── Tests └── greetingTests └── CommandTests.swift 26/45

Slide 30

Slide 30 text

࡞ͬͨϓϥάΠϯΛϓϩδΣΫτʹೖΕΔ 1. ௨ৗ௨ΓXcode ProjectΛ࡞੒ iOS > Swi2UI AppͳͲ 2. General > Package Dependencies > Add Local... 3. PackageͷϑΥϧμΛ௥Ճ ͜ΕͰ௚઀ΞϓϦଆͷXcodeϓϩδΣΫτ͔ΒϓϥάΠϯ΋ฤू͠ ͯσόοάͰ͖Δ 27/45

Slide 31

Slide 31 text

Command PluginͷॲཧΛ࣮૷ import PackagePlugin import Foundation @main struct MyCommand: CommandPlugin { func performCommand(context: PluginContext, arguments: [String]) async throws { // (ry } } #if canImport(XcodeProjectPlugin) import XcodeProjectPlugin extension MyCommand: XcodeCommandPlugin { func performCommand(context: XcodePluginContext, arguments: [String]) throws { let path = context.xcodeProject.directory.appending("hello.txt") let data = "hello".data(using: .utf8) FileManager.default.createFile(atPath: path.string, contents: data) } } #endif 28/45

Slide 32

Slide 32 text

CommandPluginͱXcodeCommandPlugin • CommandPlugin͸Swi% Package༻ͷϓϥάΠϯ • XcodeͰ࢖͏ʹ͸XcodeCommandPluginͷ࣮૷͕ඞཁ 29/45

Slide 33

Slide 33 text

࣮ߦʂ 30/45

Slide 34

Slide 34 text

Α͘࢖͏Founda'on • FileManager • ϑΝΠϧૢ࡞ • Process • γΣϧͷ࣮ߦ 31/45

Slide 35

Slide 35 text

ศརͳOSS • swi%-argument-parser • h1ps:/ /github.com/apple/swi%-argument-parser 32/45

Slide 36

Slide 36 text

import ArgumentParser @main struct Repeat: ParsableCommand { @Flag(help: "Include a counter with each repetition.") var includeCounter = false @Option(name: .shortAndLong, help: "The number of times to repeat 'phrase'.") var count: Int? @Argument(help: "The phrase to repeat.") var phrase: String mutating func run() throws { let repeatCount = count ?? 2 for i in 1...repeatCount { if includeCounter { print("\(i): \(phrase)") } else { print(phrase) } } } } 33/45

Slide 37

Slide 37 text

Command Plugin -ps • arguments͔ΒGUIͰ౉ͨ͠Ҿ਺ΛऔΕΔ • Xcode͔Βબ୒ͨ͠λʔήοτ͸["--target", ]ͱͯ͠Ҿ਺ʹ౉͞ΕΔ 34/45

Slide 38

Slide 38 text

ϫʔΫγϣοϓ 35/45

Slide 39

Slide 39 text

ϫʔΫγϣοϓ • ۀ຿Ͱ࢖͍ͬͯΔπʔϧΛSwi%ʹҠ২ͯ͠ΈΑ͏ • Package PluginͰ༡ΜͰΈΑ͏ʂ • ʮωλாʯʹ͋ΔΞΠσΟΞ͔ΒԿ͔࡞ͬͯΈΑ͏ • ׬੒͠ͳͯ͘΋΍ͬͯΈ·͠ΐ͏ 36/45

Slide 40

Slide 40 text

ωλா 37/45

Slide 41

Slide 41 text

ςϯϓϨʔτੜ੒ػ • ΫϦʔϯΞʔΩςΫνϟͷςϯϓϨʔτੜ੒ͳͲΛXcode্Ͱ ߦ͑ΔCommand Plugin 38/45

Slide 42

Slide 42 text

όʔδϣϯΛࣗಈͰ͋͛ΔCommand Plugin • Info.plistΛݟͯɺόʔδϣϯΛ্͛Δ͚ͩͷϓϥάΠϯ 39/45

Slide 43

Slide 43 text

ϞοΫࣗಈੜ੒ • ProtocolͷϞοΫੜ੒ͷͨΊͷ࢓૊ΈΛ੔උ • mockolo, Sourcery, swi3-syntaxͳͲΛ࢖ͬͯΈΑ͏ 40/45

Slide 44

Slide 44 text

Linter • ༷ʑͳۀ຿՝୊ͷLinterΛ࡞੒ • swi*-syntaxͳͲΛར༻ • ྫɿdebug import checker 41/45

Slide 45

Slide 45 text

ίʔυࢧԉ • Α͋͘Δఆܕతͳૢ࡞Λ؆୯ʹͰ͖ΔϢʔςΟϦςΟ • ini$alizerͷੜ੒ࢧԉͱ͔ • structΛ public Խ͢Δͱ͔ 42/45

Slide 46

Slide 46 text

Ϧιʔεͷࣗಈੜ੒ • Image Assetͷࣗಈੜ੒ • ը૾ϑΝΠϧ͔ΒࣗಈతʹΞηοτΛੜ੒ • localize bundleͷࣗಈੜ੒ • ιʔείʔυதͷจࣈྻΛऩूͯ͠Localize BundleԽ͢Δ 43/45

Slide 47

Slide 47 text

ීஈͷۀ຿εΫϦϓτΛSwi$ʹҠ২ • Shell Script, RubyͳͲͰॻ͔Ε͍ͯΔϢʔςΟϦςΟΛSwi2Խ 44/45

Slide 48

Slide 48 text

طଘOSSͷϓϥάΠϯରԠ • طଘͷπʔϧͷCommand PluginରԠ • OSSʹߩݙͯ͠ΈΑ͏ • Swi1Gen • LicensePlist • mockolo • dSYMΞοϓϩʔυ 45/45