Slide 1

Slide 1 text

!@LJ BWBJMBCMFͱEFQSFDBUFEΛ࢖ͬͯ ޮ཰త͔ͭ҆શʹ ϦϑΝΫλϦϯάΛਐΊΔ QPUBUPUJQT

Slide 2

Slide 2 text

"CPVUNF struct Me { let name = "Takuhiro Muta" let aka = "417.72KI" let company = "(redacted)" let twitter = "417_72ki" let qiita = "417_72ki" let gitHub = "417-72KI" let products = [ "MockUserDefaults", "MultipartFormDataParser", "BuildConfig.swift", "SSGH" ] let contributing = [ "Danger-Swift", "octokit.swift", "etc..." ] }

Slide 3

Slide 3 text

طଘίʔυʹUZQPΛݟ͚ͭͨΒ Ͳ͏͠·͔͢ʁ

Slide 4

Slide 4 text

w मਖ਼͢Δ w ݟͳ͔ͬͨ͜ͱʹ͢Δ

Slide 5

Slide 5 text

w मਖ਼͢Δ w 3FGBDUPS3FOBNF w 'JOEBOE3FQMBDFJO1BDLBHF w ݟͳ͔ͬͨ͜ͱʹ͢Δ

Slide 6

Slide 6 text

w मਖ਼͢Δ w 3FGBDUPS3FOBNF w 'JOEBOE3FQMBDFJO1BDLBHF w ݟͳ͔ͬͨ͜ͱʹ͢Δ

Slide 7

Slide 7 text

3FGBDUPS3FOBNF

Slide 8

Slide 8 text

😩

Slide 9

Slide 9 text

w मਖ਼͢Δ w 3FGBDUPS3FOBNF w 'JOEBOE3FQMBDFJO1BDLBHF w ݟͳ͔ͬͨ͜ͱʹ͢Δ

Slide 10

Slide 10 text

'JOEBOE3FQMBDFJO1BDLBHF

Slide 11

Slide 11 text

'JOEBOE3FQMBDFJO1BDLBHF w Ұݟྑͦ͞͏ʹݟ͑Δ͕ w ͤʔͷͰҰ੪ʹॻ͖׵ΘΔ w ໢ཏੑ͸େৎ෉ʁ w ౰֘GVODUJPOҎ֎ʹಉ͡UZQP͕͋ͬͨ࣌ͦͬͪ΋มΘΔ w ςετ͖͠ΕΔʁ w UZQPΛमਖ਼ͨ݁͠ՌผͷGVODUJPOͱඃͬͨ w ҙਤͤ͵όάͷ஀ੜ 🤔

Slide 12

Slide 12 text

'JOEBOE3FQMBDFJO1BDLBHF // A.swift func doSomesing() { // do something } // C.swift func doSomething(with foo: Foo = .init()) { // do something with `foo` } // B.swift func call() { doSomesing() } // D.swift func callWithFoo() { doSomething() }

Slide 13

Slide 13 text

'JOEBOE3FQMBDFJO1BDLBHF // A.swift func doSomething() { // do something } // C.swift func doSomething(with foo: Foo = .init()) { // do something with `foo` } // B.swift func call() { doSomething() } // D.swift func callWithFoo() { doSomething() } 😱

Slide 14

Slide 14 text

w मਖ਼͢Δ w 3FGBDUPS3FOBNF w 'JOEBOE3FQMBDFJO1BDLBHF w ݟͳ͔ͬͨ͜ͱʹ͢Δ

Slide 15

Slide 15 text

w मਖ਼͢Δ w 3FGBDUPS3FOBNF w 'JOEBOE3FQMBDFJO1BDLBHF w 6TFABWBJMBCMFABOEAEFQSFDBUFEA w ݟͳ͔ͬͨ͜ͱʹ͢Δ

Slide 16

Slide 16 text

BWBJMBCMFʹ͍ͭͯ BWBJMBCMFͱ͸ w ʮಛఆͷ04όʔδϣϯ͔Β࢖͑Δ࢖͑ͳ͘ͳΔʯͱ͍ͬͨ৘ใΛऔΓѻ͏ BUUSJCVUF w Ϗϧυ࣌ʹܯࠂΤϥʔͷܗͰϑΟʔυόοΫͯ͘͠ΕΔ

Slide 17

Slide 17 text

BWBJMBCMFʹ͍ͭͯ ओͳΦϓγϣϯ w ܯࠂ w EFQSFDBUFEඇਪ঑ όʔδϣϯলུՄ w ϏϧυΤϥʔ w PCTPMFUFEഇࢭ όʔδϣϯলུෆՄ w VOBWBJMBCMFར༻ෆՄ όʔδϣϯࢦఆෆՄ

Slide 18

Slide 18 text

BWBJMBCMFʹ͍ͭͯ ओͳΦϓγϣϯ w ͦͷଞ w SFOBNFEϦωʔϜઌͷ"1*GVODUJPO໊ 
 ࢦఆ͢Δͱ9DPEF্ͰαδΣετΑ͠ͳʹஔ׵ͯ͘͠ΕΔ w NFTTBHFܯࠂΤϥʔϝοηʔδ

Slide 19

Slide 19 text

BWBJMBCMFEFQSFDBUFEΛ࢖ͬͯUZQPΛमਖ਼ ϝϦοτ w ཁमਖ਼ՕॴͷಛఆΛίϯύΠϥʹؙ౤͛Ͱ͖Δ w શ෦ม͑Δඞཁ͕ແ͍ͷͰஈ֊తʹϦϑΝΫλϦϯάΛਐΊΒΕΔ w FYվमର৅ʹͳͬͨΒରԠ͢Δ w FYςετίʔυͷ͋Δݺͼग़͠ݩ͔ΒରԠ͍ͯ͘͠ w ӨڹൣғΛ࠷খݶʹ཈͑ΒΕΔ

Slide 20

Slide 20 text

BWBJMBCMFEFQSFDBUFEΛ࢖ͬͯUZQPΛमਖ਼ σϝϦοτ w Өڹൣғ͕ଟ͚Ε͹ଟ͍΄Ͳܯࠂ਺͕૿͑Δ w ಉ͡಺༰ͷܯࠂ͕ζϥοͱฒͿ w $*ͷग़ྗΛπʔϧͰऔͬͯͨΓ͍ͯ͠Δͱπϥ͍͔΋ w SFOBNFEͰͷࣗಈม׵͸ҰׅͰͰ͖ͳ͍ w ·ͱΊͯ΍Ζ͏ͱ͢Δͱෆศ

Slide 21

Slide 21 text

func doSomesing() { // do something }

Slide 22

Slide 22 text

func doSomesing() { doSomething() } func doSomething() { // do something }

Slide 23

Slide 23 text

@available(*, deprecated, renamed: "doSomething()") func doSomesing() { doSomething() } func doSomething() { // do something }

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

Ԡ༻ฤ

Slide 28

Slide 28 text

αϙʔτόʔδϣϯͷؔ܎Ͱ ࢖͑ͳ͍"1*Λ࢖͍͍ͨ

Slide 29

Slide 29 text

αϙʔτόʔδϣϯͷؔ܎Ͱ࢖͑ͳ͍"1* ˞J04ͷ"QQΛ૝ఆ w JGBWBJMBCMFΛ࢖ͬͯ w 8PSLBSPVOEͱซ༻͢Δ w ্ҐόʔδϣϯͷΈ࢖͑ΔΑ͏ʹ͢Δ w FY w "UUSJCVUFE4USJOH J04d w QSFTFOUBUJPO%FUFOUT J04d

Slide 30

Slide 30 text

αϙʔτόʔδϣϯͷؔ܎Ͱ࢖͑ͳ͍"1* ˞J04ͷ"QQΛ૝ఆ w JGBWBJMBCMFΛ࢖ͬͯ w 8PSLBSPVOEͱซ༻͢Δ w ্ҐόʔδϣϯͷΈ࢖͑ΔΑ͏ʹ͢Δ w FY w "UUSJCVUFE4USJOH J04d w QSFTFOUBUJPO%FUFOUT J04d

Slide 31

Slide 31 text

"UUSJCVUFE4USJOH var attributedString: AttributedString { // ← 'AttributedString' is only available in iOS 15 or newer var str = AttributedString("Foo Bar Baz Qux Quux") if let range = str.range(of: "Bar") { str[range].foregroundColor = .red } if let range = str.range(of: "Baz") { str[range].font = .title } if let range = str.range(of: "Qux") { str[range].font = .footnote str[range].foregroundColor = .green } return str } var body: some View { VStack { Text(attributedString) // ← 'init(_:)' is only available in iOS 15.0 or newer } }

Slide 32

Slide 32 text

"UUSJCVUFE4USJOH @available(iOS 15, *) var attributedString: AttributedString { var str = AttributedString("Foo Bar Baz Qux Quux") if let range = str.range(of: "Bar") { str[range].foregroundColor = .red } if let range = str.range(of: "Baz") { str[range].font = .title } if let range = str.range(of: "Qux") { str[range].font = .footnote str[range].foregroundColor = .green } return str } var body: some View { VStack { Text(attributedString) // ← 'init(_:)' is only available in iOS 15.0 or newer } }

Slide 33

Slide 33 text

"UUSJCVUFE4USJOH var body: some View { VStack { if #available(iOS 15, *) { Text(attributedString) } else { Text("Foo") + Text(" ") + Text("Bar") .foregroundColor(.red) + Text(" ") + Text("Baz") .font(.title) + Text(" ") + Text("Qux") .font(.footnote) .foregroundColor(.green) + Text(" ") + Text("Quux") } } } @available(iOS 15, *) var attributedString: AttributedString { var str = AttributedString("Foo Bar Baz Qux Quux") if let range = str.range(of: "Bar") { str[range].foregroundColor = .red } if let range = str.range(of: "Baz") { str[range].font = .title } if let range = str.range(of: "Qux") { str[range].font = .footnote str[range].foregroundColor = .green } return str }

Slide 34

Slide 34 text

"UUSJCVUFE4USJOH var body: some View { VStack { if #available(iOS 15, *) { Text(attributedString) } else { AttributedText { Text("Foo") Text(" ") Text("Bar") .foregroundColor(.red) Text(" ") Text("Baz") .font(.title) Text(" ") Text("Qux") .font(.footnote) .foregroundColor(.green) Text(" ") Text("Quux") } } } } struct AttributedText: View { typealias Body = Text var text: Text init(@Builder builder: () -> Text) { text = builder() } var body: Text { text } } extension AttributedText { @resultBuilder enum Builder { ɾɾɾ } }

Slide 35

Slide 35 text

JGBWBJMBCMFͷपғ͸೥ޙʹ ٕज़తෛ࠴ʹͳΔ͜ͱ͕֬ఆ͍ͯ͠Δ 🤔

Slide 36

Slide 36 text

Ͱ΋೥ޙʹ֮͑ͯΔอূ͸ແ͍ 😅

Slide 37

Slide 37 text

"UUSJCVUFE4USJOH ໿ଋ͞Εٕͨज़తෛ࠴Λ຤୅·Ͱ͓֮͑ͯͨ͘Ίʹ var body: some View { VStack { AttributedText { Text("Foo") Text(" ") Text("Bar") .foregroundColor(.red) Text(" ") Text("Baz") .font(.title) Text(" ") Text("Qux") .font(.footnote) .foregroundColor(.green) Text(" ") Text("Quux") } } } struct AttributedText: View { typealias Body = Text var text: Text init(@Builder builder: () -> Text) { text = builder() } var body: Text { text } } extension AttributedText { @resultBuilder enum Builder { ɾɾɾ } }

Slide 38

Slide 38 text

"UUSJCVUFE4USJOH .JOJNVN%FQMPZNFOU5BSHFU͕ҎԼͷؒ͸ܯࠂ͕ग़ͳ͍ @available(iOS, deprecated: 15.0, obsoleted: 16.0, message: "Use `Text` with `AttributedString`") struct AttributedText: View { typealias Body = Text var text: Text init(@Builder builder: () -> Text) { text = builder() } var body: Text { text } } @available(iOS, deprecated: 15.0, obsoleted: 16.0, message: "Use `Text` with `AttributedString`") extension AttributedText { @resultBuilder enum Builder { ɾɾɾ } } var body: some View { VStack { AttributedText { Text("Foo") Text(" ") Text("Bar") .foregroundColor(.red) Text(" ") Text("Baz") .font(.title) Text(" ") Text("Qux") .font(.footnote) .foregroundColor(.green) Text(" ") Text("Quux") } } }

Slide 39

Slide 39 text

"UUSJCVUFE4USJOH .JOJNVN%FQMPZNFOU5BSHFUΛʹͨ͠Βܯࠂ͕ग़Δ @available(iOS, deprecated: 15.0, obsoleted: 16.0, message: "Use `Text` with `AttributedString`") struct AttributedText: View { typealias Body = Text var text: Text init(@Builder builder: () -> Text) { text = builder() } var body: Text { text } } @available(iOS, deprecated: 15.0, obsoleted: 16.0, message: "Use `Text` with `AttributedString`") extension AttributedText { @resultBuilder enum Builder { ɾɾɾ } } var body: some View { VStack { AttributedText { // ← 'AttributedText' was deprecated in iOS 15.0: Use `Text` with `AttributedString` Text("Foo") Text(" ") Text("Bar") .foregroundColor(.red) Text(" ") Text("Baz") .font(.title) Text(" ") Text("Qux") .font(.footnote) .foregroundColor(.green) Text(" ") Text("Quux") } } }

Slide 40

Slide 40 text

"UUSJCVUFE4USJOH .JOJNVN%FQMPZNFOU5BSHFUΛʹͨ͠ΒϏϧυΤϥʔʹ @available(iOS, deprecated: 15.0, obsoleted: 16.0, message: "Use `Text` with `AttributedString`") struct AttributedText: View { typealias Body = Text var text: Text init(@Builder builder: () -> Text) { text = builder() } var body: Text { text } } @available(iOS, deprecated: 15.0, obsoleted: 16.0, message: "Use `Text` with `AttributedString`") extension AttributedText { @resultBuilder enum Builder { ɾɾɾ } } var body: some View { VStack { AttributedText { // ← 'AttributedText' is unavailable in iOS: Use `Text` with `AttributedString` Text("Foo") Text(" ") Text("Bar") .foregroundColor(.red) Text(" ") Text("Baz") .font(.title) Text(" ") Text("Qux") .font(.footnote) .foregroundColor(.green) Text(" ") Text("Quux") } } }

Slide 41

Slide 41 text

"UUSJCVUFE4USJOH JGBWBJMBCMF \^FMTF\^ͰׅΒΕΔͱܯࠂ͕ग़ͳ͍ var body: some View { VStack { if #available(iOS 15, *) { Text(attributedString) } else { AttributedText { Text("Foo") Text(" ") Text("Bar") .foregroundColor(.red) Text(" ") Text("Baz") .font(.title) Text(" ") Text("Qux") .font(.footnote) .foregroundColor(.green) Text(" ") Text("Quux") } } } } @available(iOS, deprecated: 15.0, obsoleted: 16.0, message: "Use `Text` with `AttributedString`") struct AttributedText: View { typealias Body = Text var text: Text init(@Builder builder: () -> Text) { text = builder() } var body: Text { text } } @available(iOS, deprecated: 15.0, obsoleted: 16.0, message: "Use `Text` with `AttributedString`") extension AttributedText { @resultBuilder enum Builder { ɾɾɾ } }

Slide 42

Slide 42 text

var body: some View { VStack { if #available(iOS 15, *) { Text(attributedString) } else { AttributedText { // ← 'AttributedText' is unavailable in iOS: Use `Text` with `AttributedString` Text("Foo") Text(" ") Text("Bar") .foregroundColor(.red) Text(" ") Text("Baz") .font(.title) Text(" ") Text("Qux") .font(.footnote) .foregroundColor(.green) Text(" ") Text("Quux") } } } } "UUSJCVUFE4USJOH PCTPMFUFEʹ͢ΔͱΤϥʔʹͳΔ @available(iOS, obsoleted: 15.0, message: "Use `Text` with `AttributedString`") struct AttributedText: View { typealias Body = Text var text: Text init(@Builder builder: () -> Text) { text = builder() } var body: Text { text } } @available(iOS, obsoleted: 15.0, message: "Use `Text` with `AttributedString`") extension AttributedText { @resultBuilder enum Builder { ɾɾɾ } }

Slide 43

Slide 43 text

αϙʔτόʔδϣϯͷؔ܎Ͱ࢖͑ͳ͍"1* ˞J04ͷ"QQΛ૝ఆ w JGBWBJMBCMFΛ࢖ͬͯ w 8PSLBSPVOEͱซ༻͢Δ w ্ҐόʔδϣϯͷΈ࢖͑ΔΑ͏ʹ͢Δ w FY w "UUSJCVUFE4USJOH J04d w QSFTFOUBUJPO%FUFOUT J04d

Slide 44

Slide 44 text

QSFTFOUBUJPO%FUFOUT struct ContentView: View { @State private var isPresenting = false var body: some View { VStack { Button { isPresenting = true } label: { VStack { Image(systemName: "globe") .imageScale(.large) Text("Hello, world!") }.foregroundColor(.accentColor) .padding() } .sheet(isPresented: $isPresenting) { SheetView() .presentationDetents([.medium]) // ← 'presentationDetents' is only available in iOS 16.0 or newer } } } }

Slide 45

Slide 45 text

QSFTFOUBUJPO%FUFOUT struct ContentView: View { @State private var isPresenting = false var body: some View { VStack { Button { isPresenting = true } label: { VStack { Image(systemName: "globe") .imageScale(.large) Text("Hello, world!") }.foregroundColor(.accentColor) .padding() } .sheet(isPresented: $isPresenting) { if #available(iOS 16.0, *) { SheetView() .presentationDetents([.medium]) } else { SheetView() } } } } }

Slide 46

Slide 46 text

QSFTFOUBUJPO%FUFOUT GVODUJPOʹ੾Γग़͢ extension View { @ViewBuilder func halfSheetIfAble(height: CGFloat? = nil) -> some View { if #available(iOS 16.0, *) { if let height { presentationDetents([.height(height)]) } else { presentationDetents([.medium]) } } else { self } } } struct ContentView: View { @State private var isPresenting = false var body: some View { VStack { Button { isPresenting = true } label: { VStack { Image(systemName: "globe") .imageScale(.large) Text("Hello, world!") }.foregroundColor(.accentColor) .padding() } .sheet(isPresented: $isPresenting) { SheetView() .halfSheetIfAble() } } } }

Slide 47

Slide 47 text

QSFTFOUBUJPO%FUFOUT struct ContentView: View { @State private var isPresenting = false var body: some View { VStack { Button { isPresenting = true } label: { VStack { Image(systemName: "globe") .imageScale(.large) Text("Hello, world!") }.foregroundColor(.accentColor) .padding() } .sheet(isPresented: $isPresenting) { SheetView() .halfSheetIfAble() } } } } @available(iOS, deprecated: 16, message: "Use `presentationDetents` directly.") extension View { @ViewBuilder func halfSheetIfAble(height: CGFloat? = nil) -> some View { if #available(iOS 16.0, *) { if let height { presentationDetents([.height(height)]) } else { presentationDetents([.medium]) } } else { self } } }

Slide 48

Slide 48 text

QSFTFOUBUJPO%FUFOUT .JOJNVN%FQMPZNFOU5BSHFUΛʹ͢Δͱܯࠂ͕ग़Δ struct ContentView: View { @State private var isPresenting = false var body: some View { VStack { Button { isPresenting = true } label: { VStack { Image(systemName: "globe") .imageScale(.large) Text("Hello, world!") }.foregroundColor(.accentColor) .padding() } .sheet(isPresented: $isPresenting) { SheetView() .halfSheetIfAble() // ← 'halfSheetIfAble(height:)' was deprecated in iOS 16: Use `presentationDetents` directly. } } } } @available(iOS, deprecated: 16, message: "Use `presentationDetents` directly.") extension View { @ViewBuilder func halfSheetIfAble(height: CGFloat? = nil) -> some View { if #available(iOS 16.0, *) { if let height { presentationDetents([.height(height)]) } else { presentationDetents([.medium]) } } else { self } } }

Slide 49

Slide 49 text

QSFTFOUBUJPO%FUFOUT ผղಉ໊ͷCBDLQPSUGVODUJPOΛ༻ҙ͢Δ struct ContentView: View { @State private var isPresenting = false var body: some View { VStack { Button { isPresenting = true } label: { VStack { Image(systemName: "globe") .imageScale(.large) Text("Hello, world!") }.foregroundColor(.accentColor) .padding() } .sheet(isPresented: $isPresenting) { SheetView() .presentationDetents([.medium]) } } } } @available(iOS, obsoleted: 16, message: "Use `presentationDetents` in SwiftUI directly.") extension View { @ViewBuilder func presentationDetents(_ detents: Set) -> some View { if #available(iOS 16.0, *) { presentationDetents(Set(detents.map(\.detent))) } else { self } } } /// Backporting for `SwiftUI.PresentationDetent` enum PresentationDetent: Hashable { ɾɾɾ } @available(iOS 16.0, *) extension PresentationDetent { var detent: SwiftUI.PresentationDetent { switch self { case .medium: return .medium ɾɾɾ } } }

Slide 50

Slide 50 text

QSFTFOUBUJPO%FUFOUT .JOJNVN%FQMPZNFOU5BSHFUΛʹ͢ΔͱϏϧυΤϥʔ struct ContentView: View { @State private var isPresenting = false var body: some View { VStack { Button { isPresenting = true } label: { VStack { Image(systemName: "globe") .imageScale(.large) Text("Hello, world!") }.foregroundColor(.accentColor) .padding() } .sheet(isPresented: $isPresenting) { SheetView() .presentationDetents([.medium]) / / ← Ambiguous use of 'medium' } } } } @available(iOS, deprecated: 16, message: "Use `presentationDetents` in SwiftUI directly.") extension View { @ViewBuilder func presentationDetents(_ detents: Set) -> some View { if #available(iOS 16.0, *) { presentationDetents(Set(detents.map(\.detent))) } else { self } } } /// Backporting for `SwiftUI.PresentationDetent` enum PresentationDetent: Hashable { ɾɾɾ } @available(iOS 16.0, *) extension PresentationDetent { var detent: SwiftUI.PresentationDetent { switch self { case .medium: return .medium ɾɾɾ } } }

Slide 51

Slide 51 text

QSFTFOUBUJPO%FUFOUT CBDLQPSUGVODUJPOΛফͤ͹ܯࠂ΋ফ͑Δ struct ContentView: View { @State private var isPresenting = false var body: some View { VStack { Button { isPresenting = true } label: { VStack { Image(systemName: "globe") .imageScale(.large) Text("Hello, world!") }.foregroundColor(.accentColor) .padding() } .sheet(isPresented: $isPresenting) { SheetView() .presentationDetents([.medium]) } } } } //@available(iOS, obsoleted: 16, message: "Use `presentationDetents` in SwiftUI directly.") //extension View { // @ViewBuilder // func presentationDetents(_ detents: Set) -> some View { // if #available(iOS 16.0, *) { // presentationDetents(Set(detents.map(\.detent))) // } else { // self // } // } //} // ///// Backporting for `SwiftUI.PresentationDetent` //enum PresentationDetent: Hashable { // ɾɾɾ //} // //@available(iOS 16.0, *) //extension PresentationDetent { // var detent: SwiftUI.PresentationDetent { // switch self { // case .medium: return .medium // ɾɾɾ // } // } //}

Slide 52

Slide 52 text

·ͱΊ w BWBJMBCMFͱEFQSFDBUFEΛ࢖͏͜ͱͰӨڹൣғΛߜͬͨϦϑΝΫλϦϯά͕ Մೳ w ͤʔͷͰ΍ΔΑΓ҆શ w ର৅͸ίϯύΠϥ͕ܯࠂΤϥʔʹͯ͘͠ΕΔͷͰQBTTJWFʹಈ͚Δ w SFNJOEFSతͳ໾ׂΛՌͨ͢ w ݟམͱ͕͠ݮΔͷͰޮ཰61

Slide 53

Slide 53 text

'JO IUUQTHJUIVCDPN,*4XJGU"WBJMBCMF4BNQMF