Slide 1

Slide 1 text

enum Ͱ Key Paths ͷΑ͏ͳ 
 ػೳΛ࣮ݱ͢Δ Case Paths After Party iOSDC Japan 2022 kalupas226

Slide 2

Slide 2 text

ࣗݾ঺հ • ΞΠΧϫ • @kalupas226 • Cookpad Inc. iOS Developer 2

Slide 3

Slide 3 text

ΞδΣϯμ • Key Paths ͷجຊ • Case Paths ʹ͍ͭͯ • Case Paths ͱ͸ʁ • Case Paths ͷجຊͱར༻ྫ • Case Paths ͷ࣮૷໘നϙΠϯτ • Case Paths ͷར༻ʹෆ͕҆͋Δํ΁ • ·ͱΊ 3

Slide 4

Slide 4 text

Key Paths ͷجຊ

Slide 5

Slide 5 text

Key Paths ͱ͸ʁ • Swift Ͱ struct Λੜ੒͢ΔͱԿ΋ͤͣʹར༻Ͱ͖Δπʔϧ • struct ʹ͓͚Δ struct ࣗ਎ͷܕͱ fi eld ͷܕ৘ใΛදݱͰ͖ΔͨΊ 
 ༷ʑͳ༻్Ͱར༻Ͱ͖Δ • ׳ΕΔ·Ͱ͸ࣗ༝ࣗࡏʹ࢖͏ͷ͕೉͍͔͠΋͠Εͳ͍ • SwiftUI, Combine, KeyPath Member Lookup ͳͲͰ΋ 
 ར༻͞Ε͍ͯΔ 5

Slide 6

Slide 6 text

Key Paths ͷجຊ struct User { var id: Int var name: String var address: Address? } var user = User( id: 42, name: "kalupas", address: .init(street: "ͩ͜͜Α") ) // user[keyPath: \.id] Ͱ΋ OK user[keyPath: \User.id] // 42 user[keyPath: \.id] = 57

Slide 7

Slide 7 text

Key Paths ͷجຊ struct User { var id: Int var name: String var address: Address? } var user = User( id: 42, name: "kalupas", address: .init(street: "ͩ͜͜Α") ) // user[keyPath: \.id] Ͱ΋ OK user[keyPath: \User.id] // 42 user[keyPath: \.id] = 57

Slide 8

Slide 8 text

Key Paths ͷجຊ struct User { var id: Int var name: String var address: Address? } var user = User( id: 42, name: "kalupas", address: .init(street: "ͩ͜͜Α") ) // user[keyPath: \.id] Ͱ΋ OK user[keyPath: \User.id] // 42 user[keyPath: \.id] = 57

Slide 9

Slide 9 text

Key Paths ͷجຊ struct User { var id: Int var name: String var address: Address? } var user = User( id: 42, name: "kalupas", address: .init(street: "ͩ͜͜Α") ) // user[keyPath: \.id] Ͱ΋ OK user[keyPath: \User.id] // 42 user[keyPath: \.id] = 57

Slide 10

Slide 10 text

͜Ε͚ͩͩͱ Key Paths ͷࢫຯ͕ͳ͍ user[keyPath: \.id] // 42 user[keyPath: \.id] = 57 ! dot syntax Ͱ୅༻Ͱ͖Δ user.id // 42 user.id = 57

Slide 11

Slide 11 text

Key Paths ͷخ͠͞ΛҰ෦ൈਮͯ͠঺հ • ӳจͷΑ͏ͳߏจʹͰ͖Δ • Ҿ਺ͱͯ͠ getter ͱ setter Λඞཁͱ͢ΔγʔϯͰҾ਺Λ 
 ίϯύΫτʹͰ͖Δ • map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ 11

Slide 12

Slide 12 text

Key Paths ͷخ͠͞ΛҰ෦ൈਮͯ͠঺հ • ӳจͷΑ͏ͳߏจʹͰ͖Δ • Ҿ਺ͱͯ͠ getter ͱ setter Λඞཁͱ͢ΔγʔϯͰҾ਺Λ 
 ίϯύΫτʹͰ͖Δ • map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ 12

Slide 13

Slide 13 text

ӳจͷΑ͏ͳߏจʹ / Ҿ਺ΛίϯύΫτʹ Ͱ͖Δ class Label { var text = "" var fontSize = 12 } class ProductModel { var name = "kalupas" } let model = ProductModel() let label = Label()

Slide 14

Slide 14 text

Label ʹ ProductModel ͷ property Λ bind ͢Δ class Label { var text = "" var fontSize = 12 } class ProductModel { var name = "kalupas" } let model = ProductModel() let label = Label()

Slide 15

Slide 15 text

Label ʹ ProductModel ͷ property Λ bind ͢Δ class Label { var text = "" var fontSize = 12 } class ProductModel { var name = "kalupas" } let model = ProductModel() let label = Label() bind(model, \.name, to: label, \.text)

Slide 16

Slide 16 text

Label ʹ ProductModel ͷ property Λ bind ͢Δ class Label { var text = "" var fontSize = 12 } class ProductModel { var name = "kalupas" } let model = ProductModel() let label = Label() bind(model, \.name, to: label, \.text) • ӳจͷΑ͏ʹಡΊΔ • Bind model name to label text • getter, setter Λ؆ܿʹهड़Ͱ͖Δ • \.name = KeyPath (getter) • \.text = ReferenceWritableKeyPath (getterɾsetter)

Slide 17

Slide 17 text

Key Paths ͷخ͠͞ΛҰ෦ൈਮͯ͠঺հ • ӳจͷΑ͏ͳߏจʹͰ͖Δ • Ҿ਺ͱͯ͠ getter ͱ setter Λඞཁͱ͢ΔγʔϯͰҾ਺Λ 
 ίϯύΫτʹͰ͖Δ • map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ 17

Slide 18

Slide 18 text

map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ struct User { var name: String } let users = [ .init(...), .init(...), // ... ]

Slide 19

Slide 19 text

map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ struct User { var name: String } let users = [ .init(...), .init(...), // ... ] let userNames = users.map { $0.name }

Slide 20

Slide 20 text

map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ struct User { var name: String } let users = [ .init(...), .init(...), // ... ] let userNames = users.map(\.name)

Slide 21

Slide 21 text

map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ struct User { var name: String } let users = [ .init(...), .init(...), // ... ] let userNames = users.map(\.name) 0249-key-path-literal-function- expressions ʹΑ࣮ͬͯݱ͞Εͨ

Slide 22

Slide 22 text

΋ͪΖΜଞʹ΋ Key Paths ͕༗༻ͳ৔໘͸͋Δ • ෳࡶͳϓϩύςΟ΁ͷΞΫηε • KeyPath Member Lookup Λར༻ͨ͠ܕ҆શͳϓϩύςΟ΁ͷΞΫηε • SwiftUI • ʮ@StateʯΛʮ$user.nameʯͷΑ͏ʹѻ͑Δ • ʮ@Environment(\.dismiss)ʯ • ForEach ͳͲͰΑ͘ݟ͔͚Δʮ\.selfʯ(Identity key path) ͳͲ΋ • ଞʹ΋୔ࢁ͋Δͱࢥ͍·͕͢ɺ঺հ͖͠Εͳ͍ͷͰׂѪ͠·͢ 22

Slide 23

Slide 23 text

Case Paths ʹ͍ͭͯ

Slide 24

Slide 24 text

Case Paths ͱ͸ʁ • Point-Free ੡ͷϥΠϒϥϦ • https://github.com/pointfreeco/swift-case-paths • struct → Key Paths, enum → Case Paths • enum Ͱ Key Paths ͷΑ͏ͳػೳ͕࢖͑Δ • Key Paths ͸ʮ\ʯͰΞΫηε͢Δ͕ɺCase Paths ͸ʮ/ʯ • ݩʑ͸ Mirror Λ࢖ͬͨ Re fl ection ʹΑΓ࣮૷͞Ε͍͕ͯͨɺݱࡏ͸ Swift ͷ 
 Pointer Λར༻͍ͯ͠ΔͷͰ Mirror ΑΓ͸҆શͳ࣮૷ํ๏ʹมΘ͍ͬͯΔ 24

Slide 25

Slide 25 text

Case Paths ͷجຊ enum Authentication { case authenticated(accessToken: String) case unauthenticated }

Slide 26

Slide 26 text

Case Paths ͷجຊ enum Authentication { case authenticated(accessToken: String) case unauthenticated } /Authentication.authenticated // CasePath ʮ/Enum.Caseʯͱ͍͏ߏจͰ
 CasePath ΛखʹೖΕΒΕΔ

Slide 27

Slide 27 text

enum ʹ associated value ΛೖΕ͍ͨ࣌ (௨ৗ) enum Authentication { case authenticated(accessToken: String) case unauthenticated } let authentication = Authentication.authenticated( accessToken: "cafebeef" )

Slide 28

Slide 28 text

enum ʹ associated value ΛೖΕ͍ͨ࣌ (௨ৗ) enum Authentication { case authenticated(accessToken: String) case unauthenticated } let authentication = Authentication.authenticated( accessToken: "cafebeef" )

Slide 29

Slide 29 text

enum ͷ associated value ΛऔΓग़͍ͨ࣌͠ (௨ৗ) enum Authentication { case authenticated(accessToken: String) case unauthenticated } let authentication = Authentication.authenticated( accessToken: "cafebeef" ) if case let .authenticated(accessToken) = authentication { // use accessToken }

Slide 30

Slide 30 text

enum ͷ associated value ΛऔΓग़͍ͨ࣌͠ (௨ৗ) enum Authentication { case authenticated(accessToken: String) case unauthenticated } let authentication = Authentication.authenticated( accessToken: "cafebeef" ) if case let .authenticated(accessToken) = authentication { // use accessToken }

Slide 31

Slide 31 text

Case Paths Λ࢖ͬͯ associated value ΛೖΕΔ enum Authentication { case authenticated(accessToken: String) case unauthenticated } let authentication = Authentication.authenticated( accessToken: "cafebeef" )

Slide 32

Slide 32 text

Case Paths Λ࢖ͬͯ associated value ΛೖΕΔ enum Authentication { case authenticated(accessToken: String) case unauthenticated } // let authentication = Authentication.authenticated( // accessToken: "cafebeef" // ) let authentication = (/Authentication.authenticated).embed("cafebeef")

Slide 33

Slide 33 text

Case Paths Ͱ associated value ΛऔΓग़͢ enum Authentication { case authenticated(accessToken: String) case unauthenticated } let authentication = (/Authentication.authenticated).embed(“cafebeef") if case let .authenticated(accessToken) = authentication { // use accessToken }

Slide 34

Slide 34 text

Case Paths Ͱ associated value ΛऔΓग़͢ enum Authentication { case authenticated(accessToken: String) case unauthenticated } let authentication = (/Authentication.authenticated).embed(“cafebeef") // if case let .authenticated(accessToken) = authentication { // // use accessToken // } (/Authentication.authenticated).extract(from: authentication)

Slide 35

Slide 35 text

Case Paths ͷଞͷػೳ - Composition - \HighScore.user.name // WritableKeyPath /Result..Authentication.authenticated // CasePath, String>

Slide 36

Slide 36 text

Case Paths ͷଞͷػೳ - Identity case path - \User.self // WritableKeyPath /Authentication.self // CasePath

Slide 37

Slide 37 text

Case Paths ͷར༻ํ๏ • Key Paths ͱಉ͘͡ɺgetterɾsetter ͚ͩͷ໾ׂͰ Case Paths Λ 
 ར༻ͯ͠΋ࢫຯΛಘΒΕΔέʔε͸গͳ͍ • Caes Paths ͷར༻ྫΛݟ͍ͯ͘͜ͱͰศར͞Λগ͚ͩ͠঺հ͢Δ • map ͳͲͷߴ֊ؔ਺Ͱͷར༻ • swiftui-navigation 37

Slide 38

Slide 38 text

map ͳͲͷߴ֊ؔ਺Ͱͷར༻ // Key Paths users.map(\User.name) // Case Paths authentications.compactMap(/Authentication.authenticated)

Slide 39

Slide 39 text

map ͳͲͷߴ֊ؔ਺Ͱͷར༻ // Key Paths users.map(\User.name) // Case Paths authentications.compactMap(/Authentication.authenticated) Case Paths ͷॲཧ͸ɺcase ʹ associated value ͕
 ଘࡏ͠ͳ͍৔߹ nil ʹͳΔͨΊɺcompactMap ͳͲΛར༻͢Δ

Slide 40

Slide 40 text

swiftui-navigation • Point-Free ੡ͷ SwiftUI Navigation ʹ focus ͨ͠ϥΠϒϥϦ • https://github.com/pointfreeco/swiftui-navigation • iOSDC Japan 2022 Ͱൃදͨ͠ͷͰৄ͘͠͸ͦͪΒΛࢀর͍ͩ͘͞🙏 • ʮSwiftUI Navigation ͷ͢΂ͯʯ • swiftui-navigation ͷ಺෦Ͱ΋ Case Paths ͕ར༻͞Ε͍ͯΔ 40

Slide 41

Slide 41 text

swiftui-navigation struct ContentView: View { enum Route { case create case draft(Post) } @State var route: Route? var body: some View { BaseView(...) .sheet( unwrapping: $route, case: /Route.draft ) { $draft in EditPostView(post: $draft) } } }

Slide 42

Slide 42 text

swiftui-navigation struct ContentView: View { enum Route { case create case draft(Post) } @State var route: Route? var body: some View { BaseView(...) .sheet( unwrapping: $route, case: /Route.draft ) { $draft in EditPostView(post: $draft) } } }

Slide 43

Slide 43 text

swiftui-navigation struct ContentView: View { enum Route { case create case draft(Post) } @State var route: Route? var body: some View { BaseView(...) .sheet( unwrapping: $route, case: /Route.draft ) { $draft in EditPostView(post: $draft) } } }

Slide 44

Slide 44 text

swiftui-navigation extension View { func sheet( unwrapping enum: Binding, case casePath: CasePath, @ViewBuilder content: @escaping (Binding) -> Content ) -> some View where Content: View { self.sheet( isPresented: `enum`.case(casePath).isPresented() ) { Binding( unwrapping: `enum`.case(casePath) ).map(content) } } }

Slide 45

Slide 45 text

Case Paths ͷ͓͔͛Ͱ enum ͷಛఆͷ case ʹ 
 Ԡͨ͡ॲཧΛهड़Ͱ͖ΔΑ͏ʹͳΔ extension View { func sheet( unwrapping enum: Binding, case casePath: CasePath, @ViewBuilder content: @escaping (Binding) -> Content ) -> some View where Content: View { self.sheet( isPresented: `enum`.case(casePath).isPresented() ) { Binding( unwrapping: `enum`.case(casePath) ).map(content) } } } Enum ͷಛఆͷ case ͕ active ͳ࣌ʹͷΈ
 දࣔ͞ΕΔ sheet

Slide 46

Slide 46 text

Case Paths ͷ࣮૷໘നϙΠϯτ • pre fi x, in fi x operator • @_disfavoredOverload • Mirror • Pointer 46

Slide 47

Slide 47 text

Case Paths ͷ࣮૷໘നϙΠϯτ • pre fi x, in fi x operator • @_disfavoredOverload • Mirror • Pointer • ࠓ೔ͷൃද·Ͱʹઆ໌Ͱ͖ΔϨϕϧ·Ͱམͱ͠ࠐΊͳ͔ͬͨͷͰɺ 
 ·ͨͷػձʹهࣄʹ͢Δ͔ൃද͠Α͏ͱࢥ͍·͢🙇 47

Slide 48

Slide 48 text

Case Paths ͷ࣮૷໘നϙΠϯτ • pre fi x, in fi x operator • @_disfavoredOverload • Mirror • Pointer 48

Slide 49

Slide 49 text

pre fi x, in fi x operator • pre fi x: ઀಄ࣙ • in fi x: ઀தࣙ • post fi x (઀ඌࣙ) ΋͋Δ͕ɺͦΕ͸ར༻͞Ε͍ͯͳ͍ͷͰׂѪ • pre fi x operator ͷྫ • Key Paths ʹ͓͚Δʮ\User.idʯɺBoolean ͷ൓సʮ!isPresentedʯͳͲ • in fi x operator ͷྫ • ʮ1 + 2ʯʮ2 - 1ʯͳͲ • operator ͸ࣗ࡞͢Δ͜ͱ͕Ͱ͖Δ 49

Slide 50

Slide 50 text

Case Paths ʹ͓͚Δ pre fi x operatorʮ/ʯͷࣗ࡞ prefix operator / public prefix func / ( embed: @escaping (Value) -> Root ) -> CasePath { .init(embed: embed, extract: extractHelp(embed)) }

Slide 51

Slide 51 text

Case Paths ʹ͓͚Δ pre fi x operatorʮ/ʯͷࣗ࡞ prefix operator / public prefix func / ( embed: @escaping (Value) -> Root ) -> CasePath { .init(embed: embed, extract: extractHelp(embed)) }

Slide 52

Slide 52 text

Case Paths ʹ͓͚Δ pre fi x operatorʮ/ʯͷࣗ࡞ prefix operator / public prefix func / ( embed: @escaping (Value) -> Root ) -> CasePath { .init(embed: embed, extract: extractHelp(embed)) }

Slide 53

Slide 53 text

Case Paths ʹ͓͚Δ pre fi x operatorʮ/ʯͷࣗ࡞ prefix operator / public prefix func / ( embed: @escaping (Value) -> Root ) -> CasePath { .init(embed: embed, extract: extractHelp(embed)) } /Authentication.authenticated

Slide 54

Slide 54 text

embed: (Value) -> Root ʹ 
 Authentication.authenticated Λ౉ͤΔཧ༝ embed: @escaping (Value) -> Root enum Authentication { case authenticated(accessToken: String) case unauthenticated } let auth = Authentication.authenticated // (String) -> Authentication // (String) -> Authentication // (Value) -> Root

Slide 55

Slide 55 text

embed: (Value) -> Root ʹ 
 Authentication.authenticated Λ౉ͤΔཧ༝ embed: @escaping (Value) -> Root enum Authentication { case authenticated(accessToken: String) case unauthenticated } let auth = Authentication.authenticated // (String) -> Authentication // (String) -> Authentication // (Value) -> Root

Slide 56

Slide 56 text

embed: (Value) -> Root ʹ 
 Authentication.authenticated Λ౉ͤΔཧ༝ embed: @escaping (Value) -> Root enum Authentication { case authenticated(accessToken: String) case unauthenticated } let auth = Authentication.authenticated // (String) -> Authentication // (String) -> Authentication // (Value) -> Root

Slide 57

Slide 57 text

Case Paths ʹ͓͚Δ in fi x operatorʮ..ʯͷࣗ࡞ precedencegroup CasePathCompositionPrecedence { associativity: left } infix operator ..: CasePathCompositionPrecedence extension CasePath { public static func .. ( lhs: CasePath, rhs: @escaping (AppendedValue) -> Value ) -> CasePath { lhs.appending(path: /rhs) } }

Slide 58

Slide 58 text

Case Paths ʹ͓͚Δ in fi x operatorʮ..ʯͷࣗ࡞ precedencegroup CasePathCompositionPrecedence { associativity: left } infix operator ..: CasePathCompositionPrecedence extension CasePath { public static func .. ( lhs: CasePath, rhs: @escaping (AppendedValue) -> Value ) -> CasePath { lhs.appending(path: /rhs) } } precedencegroup Ͱ operator ʹ
 ϧʔϧΛ࣋ͨͤΔ͜ͱ͕Ͱ͖Δ associativity: leftͰ
 operator ʹࠨ݁߹ͷ݁߹ੑΛ࣋ͨͤΒΕΔ (1 + 2) + 3 Ͱ͸ͳ͘ 1 + 2 + 3
 Ͱߦ͚ΔΑ͏ʹ͢ΔΠϝʔδ

Slide 59

Slide 59 text

Case Paths ʹ͓͚Δ in fi x operatorʮ..ʯͷࣗ࡞ precedencegroup CasePathCompositionPrecedence { associativity: left } infix operator ..: CasePathCompositionPrecedence extension CasePath { public static func .. ( lhs: CasePath, rhs: @escaping (AppendedValue) -> Value ) -> CasePath { lhs.appending(path: /rhs) } }

Slide 60

Slide 60 text

Case Paths ʹ͓͚Δ in fi x operatorʮ..ʯͷࣗ࡞ precedencegroup CasePathCompositionPrecedence { associativity: left } infix operator ..: CasePathCompositionPrecedence extension CasePath { public static func .. ( lhs: CasePath, rhs: @escaping (AppendedValue) -> Value ) -> CasePath { lhs.appending(path: /rhs) } } /Result .. Authentication.authenticated lhs rhs

Slide 61

Slide 61 text

Case Paths ͷ࣮૷໘നϙΠϯτ • pre fi x, in fi x operator • @_disfavoredOverload • Mirror • Pointer 61

Slide 62

Slide 62 text

Swift Ͱ͸ function overload ͕ڐ͞Ε͍ͯΔ func sameName() {} func sameName(value: Int) {} func sameName() {} func sameName(value: Int = 5) {} func sameName(_ number: Int) {} func sameName(_ number: N) {} func sameName(_ number: Int) {} func sameName(_ numbers: Int...) {}

Slide 63

Slide 63 text

overload ͞Ε͍ͯΔ৔߹ɺΑΓ۩ମతͳ΋ͷ͕ 
 ༏ઌ͞Εͯݺ͹ΕΔ (ެࣜʹ͸ݴٴ͞Ε͍ͯͳ͍) func sameName() {} func sameName(value: Int = 5) {} func sameName(_ number: Int) {} func sameName(_ number: N) {} func sameName(_ number: Int) {} func sameName(_ numbers: Int...) {} https://forums.swift.org/t/compiler-choosing-between-functions-with-no-parameters-vs-functions-with-default-parameters/23501/2 https://www. fi vestars.blog/articles/disfavoredOverload/

Slide 64

Slide 64 text

overload ͞Ε͍ͯΔ৔߹ɺΑΓ۩ମతͳ΋ͷ͕ 
 ༏ઌ͞Εͯݺ͹ΕΔ (ެࣜʹ͸ݴٴ͞Ε͍ͯͳ͍) func sameName() {} // sameName() Ͱͪ͜Β͕ݺ͹ΕΔ func sameName(value: Int = 5) {} func sameName(_ number: Int) {} // sameName(5) Ͱͪ͜Β͕ݺ͹ΕΔ func sameName(_ number: N) {} func sameName(_ number: Int) {} // sameName(5) Ͱͪ͜Β͕ݺ͹ΕΔ func sameName(_ numbers: Int...) {} https://forums.swift.org/t/compiler-choosing-between-functions-with-no-parameters-vs-functions-with-default-parameters/23501/2 https://www. fi vestars.blog/articles/disfavoredOverload/

Slide 65

Slide 65 text

sameName(value:) Λ༏ઌͯ͠ݺ͹ͤΔํ๏ func sameName() {} // sameName() Ͱͪ͜Β͕ݺ͹ΕΔ func sameName(value: Int = 5) {}

Slide 66

Slide 66 text

sameName(value:) Λ༏ઌͯ͠ݺ͹ͤΔํ๏ @_disfavoredOverload func sameName() {} func sameName(value: Int = 5) {} // sameName() Ͱͪ͜Β͕ݺ͹ΕΔΑ͏ʹͳΔ

Slide 67

Slide 67 text

sameName(value:) Λ༏ઌͯ͠ݺ͹ͤΔํ๏ @_disfavoredOverload func sameName() {} func sameName(value: Int = 5) {} // sameName() Ͱͪ͜Β͕ݺ͹ΕΔΑ͏ʹͳΔ `@_disfavoredOverload` ʹʮ_ (ΞϯμʔείΞ)ʯ͕
 ෇͍͍ͯΔͷ͸ɺSwift Evolution Λ௨ա͍ͯ͠ͳ͍ͨΊɻ Library headers ʹ͸ `@_disfavoredOverload` Ͱ͋Δ͜ͱ͕
 දࣔ͞Εͳ͍ͨΊɺdocument comment Λ͚ͭΔ͜ͱ͕ॏཁ

Slide 68

Slide 68 text

Case Paths Ͱ΋ @_disfavoredOverload ͕ 
 ར༻͞Ε͍ͯΔ public prefix func / ( embed: @escaping (Value) -> Root ) -> CasePath { .init(embed: embed, extract: extractHelp(embed)) } @_disfavoredOverload public prefix func / ( embed: @escaping (Value) -> Root ) -> (Root) -> Value? { (/embed).extract(from:) }

Slide 69

Slide 69 text

Case Paths Ͱ΋ @_disfavoredOverload ͕ 
 ར༻͞Ε͍ͯΔ public prefix func / ( embed: @escaping (Value) -> Root ) -> CasePath { .init(embed: embed, extract: extractHelp(embed)) } @_disfavoredOverload public prefix func / ( embed: @escaping (Value) -> Root ) -> (Root) -> Value? { (/embed).extract(from:) } ීஈ `/Authentication.authenticated` ͷΑ͏ʹ
 ར༻ͨ͠৔߹ɺ্ͷ function ͕༏ઌ͞Εͯݺ͹ΕΔ
 
 `compactMap` ͳͲͰར༻ͨ࣌͠ = `(Root) -> Value?` Λඞཁͱ͢Δ࣌ ͷΈͰԼͷ function ͕ݺ͹ΕΔΑ͏ʹͳ͍ͬͯΔ

Slide 70

Slide 70 text

Case Paths ͷ࣮૷໘നϙΠϯτ • pre fi x, in fi x operator • @_disfavoredOverload • Mirror • Pointer 70

Slide 71

Slide 71 text

Mirror • ݩʑ Case Paths ͷ಺෦࣮૷ʹ͸ Mirror ͕ར༻͞Ε͍ͯͨ • ݱࡏ͸ Pointer Λར༻ͨ͠ Mirror ΑΓ͸҆શͳ࣮૷ํ๏ʹͳ͍ͬͯΔ • https://github.com/pointfreeco/swift-case-paths/pull/37 • Mirror ͸ Swift Ͱ Re fl ection Λߦ͑Δ API • Re fl ection: ϓϩάϥϜͷதͰͦͷϓϩάϥϜʹؚ·ΕΔܕ΍ 
 ม਺/ϝιουͷ৘ใΛࢀর/ૢ࡞Ͱ͖ΔΑ͏ʹ͢Δ࢓૊Έ • Mirror ͷڍಈ͸อূ͞Ε͍ͯͳ͍ͨΊɺར༻ʹ͸஫ҙ͕ඞཁ (جຊతʹ͸σόοά༻్͘Β͍ Ͱར༻ͨ͠ํ͕ྑ͍) 71

Slide 72

Slide 72 text

struct Ͱ Mirror Λར༻͢ΔҰྫ struct User { var id: Int var name: String var address: Address? } let user = User(id: 1, name: "kalupas", address: .init(street: "street")) func allProperties(_ value: Any) -> [String] { Mirror(reflecting: value).children.compactMap { $0.label } } print(allProperties(user))

Slide 73

Slide 73 text

struct Ͱ Mirror Λར༻͢ΔҰྫ struct User { var id: Int var name: String var address: Address? } let user = User(id: 1, name: "kalupas", address: .init(street: "street")) func allProperties(_ value: Any) -> [String] { Mirror(reflecting: value).children.compactMap { $0.label } } print(allProperties(user))

Slide 74

Slide 74 text

struct Ͱ Mirror Λར༻͢ΔҰྫ struct User { var id: Int var name: String var address: Address? } let user = User(id: 1, name: "kalupas", address: .init(street: "street")) func allProperties(_ value: Any) -> [String] { Mirror(reflecting: value).children.compactMap { $0.label } } print(allProperties(user)) dump(Mirror(reflecting: value).children.first!) ▿ (2 elements) ▿ label: Optional("id") - some: "id" - value: 1

Slide 75

Slide 75 text

struct Ͱ Mirror Λར༻͢ΔҰྫ struct User { var id: Int var name: String var address: Address? } let user = User(id: 1, name: "kalupas", address: .init(street: "street")) func allProperties(_ value: Any) -> [String] { Mirror(reflecting: value).children.compactMap { $0.label } } print(allProperties(user)) // ["id", "name", "address"]

Slide 76

Slide 76 text

enum Ͱ Mirror Λར༻͢ΔҰྫ enum Authentication { case authenticated(String) case unauthenticated } let auth = Authentication.authenticated("deadbeef") let mirror = Mirror(reflecting: auth)

Slide 77

Slide 77 text

enum Ͱ Mirror Λར༻͢ΔҰྫ enum Authentication { case authenticated(String) case unauthenticated } let auth = Authentication.authenticated("deadbeef") let mirror = Mirror(reflecting: auth)

Slide 78

Slide 78 text

enum Ͱ Mirror Λར༻͢ΔҰྫ enum Authentication { case authenticated(String) case unauthenticated } let auth = Authentication.authenticated("deadbeef") let mirror = Mirror(reflecting: auth) dump(mirror.children.first!) ▿ (2 elements) ▿ label: Optional("authenticated") - some: "authenticated" - value: "deadbeef"

Slide 79

Slide 79 text

Case Paths Ͱͷ Mirror ͷར༻ྫΛݟͯΈΔ enum Authentication { case authenticated(String) case unauthenticated } let auth = Authentication.authenticated("deadbeef") extract(case: Authentication.authenticated, from: auth) // Optional(“deadbeef") ͕खʹೖΔ

Slide 80

Slide 80 text

Case Paths Ͱͷ Mirror ͷར༻ྫΛݟͯΈΔ enum Authentication { case authenticated(String) case unauthenticated } let auth = Authentication.authenticated("deadbeef") extract(case: Authentication.authenticated, from: auth) // Optional(“deadbeef") ͕खʹೖΔ ※ ͜ͷ extract function ͸આ໌ͷͨΊͷ΋ͷͰ͋ΓɺCase Paths ʹ͸ଘࡏ͍ͯ͠ͳ͍

Slide 81

Slide 81 text

Mirror ʹΑΔ extract ͷ࣮૷ํ๏ 
 (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ func extract( case: @escaping (Value) -> Root, from root: Root ) -> Value? { let mirror = Mirror(reflecting: root) guard let child = mirror.children.first else { return nil } guard let value = child.value as? Value else { return nil } let newRoot = `case`(value) let newMirror = Mirror(reflecting: newRoot) guard let newChild = newMirror.children.first else { return nil } guard newChild.label == child.label else { return nil } return value }

Slide 82

Slide 82 text

Mirror ʹΑΔ extract ͷ࣮૷ํ๏ 
 (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ func extract( case: @escaping (Value) -> Root, from root: Root ) -> Value? { let mirror = Mirror(reflecting: root) guard let child = mirror.children.first else { return nil } guard let value = child.value as? Value else { return nil } let newRoot = `case`(value) let newMirror = Mirror(reflecting: newRoot) guard let newChild = newMirror.children.first else { return nil } guard newChild.label == child.label else { return nil } return value } let auth = Authentication.authenticated("deadbeef") extract(case: Authentication.authenticated, from: auth) -> Optional(“deadbeef”)

Slide 83

Slide 83 text

Mirror ʹΑΔ extract ͷ࣮૷ํ๏ 
 (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ func extract( case: @escaping (Value) -> Root, from root: Root ) -> Value? { let mirror = Mirror(reflecting: root) guard let child = mirror.children.first else { return nil } guard let value = child.value as? Value else { return nil } let newRoot = `case`(value) let newMirror = Mirror(reflecting: newRoot) guard let newChild = newMirror.children.first else { return nil } guard newChild.label == child.label else { return nil } return value } dump(mirror.children.first!) ▿ (2 elements) ▿ label: Optional("authenticated") - some: "authenticated" - value: "deadbeef" child value

Slide 84

Slide 84 text

Mirror ʹΑΔ extract ͷ࣮૷ํ๏ 
 (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ func extract( case: @escaping (Value) -> Root, from root: Root ) -> Value? { let mirror = Mirror(reflecting: root) guard let child = mirror.children.first else { return nil } guard let value = child.value as? Value else { return nil } let newRoot = `case`(value) let newMirror = Mirror(reflecting: newRoot) guard let newChild = newMirror.children.first else { return nil } guard newChild.label == child.label else { return nil } return value } extract(case: Authentication.authenticated, from: auth) newRoot = Authentication.authenticated("deadbeef")

Slide 85

Slide 85 text

Mirror ʹΑΔ extract ͷ࣮૷ํ๏ 
 (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ func extract( case: @escaping (Value) -> Root, from root: Root ) -> Value? { let mirror = Mirror(reflecting: root) guard let child = mirror.children.first else { return nil } guard let value = child.value as? Value else { return nil } let newRoot = `case`(value) let newMirror = Mirror(reflecting: newRoot) guard let newChild = newMirror.children.first else { return nil } guard newChild.label == child.label else { return nil } return value } label ͕ਖ਼͍͜͠ͱΛνΣοΫ நग़Ͱ͖ͨ value Λฦ͢

Slide 86

Slide 86 text

೚ҙͷ enum ͷ associated value Λ 
 நग़Ͱ͖ΔΑ͏ʹͳͬͨ extract(case: Authentication.authenticated, from: auth) // AccessToken extract(case: Authentication.authenticated, from: .unauthenticated) // nil extract(case: Result.success, from: .success(42)) // 42 struct MyError: Error {} extract(case: Result.failure, from: .failure(MyError())) // MyError extract(case: Example.foo, from: .foo(2)) // 2 extract(case: Example.bar, from: .foo(2)) // nil

Slide 87

Slide 87 text

Case Paths ͷར༻ʹෆ͕҆͋Δํ΁ • Swift ͕ఏڙͯ͘͠Εͳ͍΋ͷΛఏڙͰ͖ΔΑ͏ʹ͢ΔͨΊʹɺCase Paths ͸ Pointer Λར༻ͯ͠ػೳΛ࣮ݱ͍ͯ͠Δ • ໰୊ͳ͘ಈ࡞͢ΔΑ͏ʹɺϕϯνϚʔΫςετ΍ڍಈΛอূ͢ΔͨΊͷϢχοτςετ͕༻ҙ͞Ε͍ͯΔ • ϕϯνϚʔΫςετ: https://github.com/pointfreeco/swift-case-paths/blob/ 7346701ea29da0a85d4403cf3d7a589a58ae3dee/Sources/swift-case-paths-benchmark/main.swift • Ϣχοτςετ: https://github.com/pointfreeco/swift-case-paths/blob/ 7346701ea29da0a85d4403cf3d7a589a58ae3dee/Tests/CasePathsTests/CasePathsTests.swift • ଟ਺ͷར༻࣮੷΋͋Δ • ݴޠʹ૊Έࠐ·ΕΔ͜ͱ͕͔ͳΓظ଴͞Ε͍ͯͯಈ͖΋׆ൃͳ༷ࢠ • https://forums.swift.org/t/discussion-inclusion-of-casepaths-into-the-language/53024 • https://github.com/apple/swift/pull/58940

Slide 88

Slide 88 text

Case Paths ͷར༻ʹෆ͕҆͋Δํ΁

Slide 89

Slide 89 text

·ͱΊ • struct ʹ͓͚Δ Key Paths ͷΑ͏ͳػೳΛ enum Ͱ࣮ݱ͢Δ Case Paths ͱ͍͏ϥΠϒϥϦ͕ Point-Free ʹΑͬͯ 
 ఏڙ͞Ε͍ͯΔ • Case Paths ʹ͸༷ʑͳར༻γʔϯ͕͋Δ • map ͳͲͷߴ֊ؔ਺Ͱͷར༻ɺfunction ͷҾ਺ͱͯ͠ಛఆͷ case Λ׆༻͍ͨ͠৔߹ͳͲ • Case Paths ͷ࣮૷Ͱ͸ҎԼͷΑ͏ͳٕज़͕ར༻͞Ε͍ͯΔ • pre fi x, in fi x operator • @_disfavoredOverload • Mirror (ࠓ͸ར༻͞Ε͍ͯͳ͍) • Pointer • Case Paths ͸͔ͬ͠Γςετ͞Ε͍ͯͯར༻࣮੷΋͋Γɺݴޠػೳʹ૊ΈࠐΉͷͨΊͷಈ͖΋ൃੜ͍ͯ͠Δ

Slide 90

Slide 90 text

ࢀߟ • Case Paths पล • https://github.com/pointfreeco/swift-case-paths • https://github.com/wickwirew/Runtime • https://github.com/pointfreeco/swiftui-navigation • https://forums.swift.org/t/discussion-inclusion-of-casepaths-into-the-language/53024 • Mirror • https://developer.apple.com/documentation/swift/mirror • https://qiita.com/bannzai/items/96b912a9db43cefdb728 • Pointer • https://developer.apple.com/documentation/swift/unsafepointer • https://qiita.com/omochimetaru/items/c95e0d36ae7f1b1a9052

Slide 91

Slide 91 text

ࢀߟ • pre fi x, in fi x operator • https://docs.swift.org/swift-book/LanguageGuide/BasicOperators.html • https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html • disfavoredOverload • https://www. fi vestars.blog/articles/disfavoredOverload/ • Key Paths पล • proposals • https://github.com/apple/swift-evolution/blob/main/proposals/0161-key-paths.md • https://github.com/apple/swift-evolution/blob/main/proposals/0227-identity-keypath.md • https://github.com/apple/swift-evolution/blob/master/proposals/0249-key-path-literal-function-expressions.md • https://github.com/apple/swift-evolution/blob/main/proposals/0252-keypath-dynamic-member-lookup.md • https://www.youtube.com/watch?v=-eRhuwAZyCU • https://www.pointfree.co/episodes/ep8-getters-and-key-paths • https://www.pointfree.co/episodes/ep7-setters-and-key-paths

Slide 92

Slide 92 text

ิ଍ࢿྉ

Slide 93

Slide 93 text

Key Paths Λ࢖Θͣʹಉ͡දݱΛ͢Δʹ͸ʁ bind(model, \.name, to: label, \.text)

Slide 94

Slide 94 text

Key Paths Λ࢖Θͣʹಉ͡දݱΛ͢Δʹ͸ʁ bind(model, \.name, to: label, \.text) ! bind( model: model, get: { $0.name }, to: label, get: { $0.text }, set: { $0.text = $1 } )

Slide 95

Slide 95 text

Key Paths Λ࢖Θͣʹಉ͡දݱΛ͢Δʹ͸ʁ bind(model, \.name, to: label, \.text) ! bind( model: model, get: { $0.name }, to: label, get: { $0.text }, set: { $0.text = $1 } ) • ӳจͷΑ͏ʹಡΊͳ͍ • getter, setter Λදݱ͢ΔͨΊʹ Ҿ਺͕ଟ͘ͳΔ

Slide 96

Slide 96 text

bind ͷྫͷ৔߹ɺҎԼͷΑ͏ʹ͸Ͱ͖Δ bind( model: model, get: { $0.name }, to: label, get: { $0.text }, set: { $0.text = $1 } ) ! bind(model.name, to: label)

Slide 97

Slide 97 text

Combine Ͱ΋ࣅͨΑ͏ͳߏจ͕͋Δ let subject = PassthroughSubject() subject.assign(to: \.text, on: label)

Slide 98

Slide 98 text

Combine Ͱ΋ࣅͨΑ͏ͳߏจ͕͋Δ let subject = PassthroughSubject() subject.assign(to: \.text, on: label) • ӳจͷΑ͏ʹಡΊΔ • Ҿ਺͕ίϯύΫτʹͳΔ