$30 off During Our Annual Pro Sale. View Details »

しくみから理解するSwiftUI

kumamotone
September 18, 2019

 しくみから理解するSwiftUI

2019/08/07 Bonfire iOS #6
https://yj-meetup.connpass.com/event/136285/

apple/swift-evolution:

SE-0255: Implicit return from single expressions
https://github.com/apple/swift-evolution/blob/master/proposals/0255-omit-return.md

SE-XXXX: Function Builders
https://github.com/apple/swift-evolution/blob/9992cf3c11c2d5e0ea20bee98657d93902d5b174/proposals/XXXX-function-builders.md

SE-0244: Opaque Result Type
https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md

SE-0258: Property Wrappers
https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md

See Also:

SwiftUIの魔法を実現する仕組み (Custom Attributes, Function Builder)
https://qiita.com/kentrino/items/dc6e77a0ddd21187cc55

SwiftUIのコードを読み解く
https://blog.personal-factory.com/2019/06/07/understand-swiftui-code/

Swift 5.1 に導入される Opaque Result Type とは何か
https://qiita.com/koher/items/338d2f2d0c4731e3508f

Opaque Result Typeの解説
https://qiita.com/omochimetaru/items/f13fe3e54fab01648ba4

SwiftUIで使用されているSwift5.1の新機能 - クックパッド開発者ブログ
https://techlife.cookpad.com/entry/2019/06/25/120000Qa

SwiftUIのProperty Wrappersとデータへのアクセス方法 - Qiita
https://qiita.com/shiz/items/6eaf87fa79499623306a

kumamotone

September 18, 2019
Tweet

More Decks by kumamotone

Other Decks in Technology

Transcript

  1. ͘͠Έ͔Βཧղ͢ΔSwiftUI
    2019/09/18 iOSDC Reject Conference (Day. 2)
    twitter.com/kumamo_tone

    View Slide

  2. View Slide

  3. • iOS/AndroidΤϯδχΞ
    • Yahoo!ΧϨϯμʔ iOS։ൃ
    • ษڧձӡӦ
    • Bonfire iOSɺWWDC Extended ͳͲ
    • potatotips #65 ΋ΑΖ͘͠
    • ϚΠϒʔϜ
    • ఱؾͷࢠ
    ۽ຊ ࿨ਖ਼ (@kumamo_tone)

    View Slide

  4. • ୈҰ෦: some View ΍ VStack/HStack Λ࣮ݱ͢Δػೳ
    • Implicit return from single expressions
    • Function Builder
    • Opaque Result Type
    • ୈೋ෦: @State ΍ @Binding Λ࣮ݱ͢Δػೳ
    • Property Wrappers
    • ૝ఆର৅ऀ
    • ؾܰʹཧղ͍ͨ͠
    • SwiftUI ؾʹͳͬͯ͸͍Δ͚Ͳ͋·ΓखΛ෇͚Ε͍ͯͳ͍
    TL;DR

    View Slide

  5. View Slide

  6. struct ContentView: View {
    var body: some View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }

    View Slide

  7. struct ContentView: View {
    var body: some View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    } public protocol View {
    associatedtype Body : View
    var body: Self.Body { get }
    }
    7JFXϓϩτίϧͷ͜ͱ
    7JFXϓϩτίϧͷఆٛ

    View Slide

  8. struct ContentView: View {
    var body: some View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    } public protocol View {
    associatedtype Body : View
    var body: Self.Body { get }
    }
    7JFXϓϩτίϧͷ͜ͱ
    7JFXϓϩτίϧͷఆٛ
    ٙ໰

    View Slide

  9. struct ContentView: View {
    var body: some View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    TPNFͬͯԿʁ͆

    View Slide

  10. struct ContentView: View {
    var body: some View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    TPNFͬͯԿʁ͆
    ͜ͷதͰ͸
    Կ͕ߦΘΕ͍ͯΔʁ

    View Slide

  11. struct ContentView: View {
    var body: some View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    SFUVSOͲ͜ʹߦͬͨʁ
    ͜ͷதͰ͸
    Կ͕ߦΘΕ͍ͯΔʁ
    TPNFͬͯԿʁ͆

    View Slide

  12. struct ContentView: View {
    var body: some View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    SFUVSOͲ͜ʹߦͬͨʁ
    ʁ
    TPNFͬͯԿʁ͆
    ͜ͷதͰ͸
    Կ͕ߦΘΕ͍ͯΔʁ

    View Slide

  13. "
    Magic?

    View Slide

  14. "
    No.

    View Slide

  15. Swift5.1ʹ௥Ճ͞Εͨɺ3ͭͷ৽ػೳʹΑͬͯઆ໌Ͱ͖Δ
    • Implicit return from single expressions
    • Function builders
    • Opaque Result Type
    some View Λ࣮ݱ͢Δػೳ

    View Slide

  16. Swift5.1ʹ௥Ճ͞Εͨɺ3ͭͷ৽ػೳʹΑͬͯઆ໌Ͱ͖Δ
    • Swift Evolution: SE-0255
    • Implicit return from single expressions
    • Swift Evolution: SE-XXXX
    • Function builders
    • Swift Evolution: SE-0244
    • Opaque Result Type
    some View Λ࣮ݱ͢Δػೳ
    ͻͱͭΊʂ

    View Slide

  17. Implicit return from single expressions

    View Slide

  18. SE-0255: Implicit return from single expressions
    • ϝϦοτ
    • ͖ͬ͢Γ͢Δ
    ΫϩʔδϟͰ͸લ͔Βɺ͕ࣜͻͱ͔ͭ͠ͳ͍ͱ͖returnΛলུͰ͖ͨ
    let names = persons.map { $0.name }

    View Slide

  19. SE-0255: Implicit return from single expressions
    struct Rectangle {
    var width = 0.0, height = 0.0
    var area: Double {
    width * height
    }
    }
    ؔ਺΍ Computed Property Ͱ΋OK
    ΫϩʔδϟͰ͸લ͔Βɺ͕ࣜͻͱ͔ͭ͠ͳ͍ͱ͖returnΛলུͰ͖ͨ
    • ϝϦοτ
    • ͖ͬ͢Γ͢Δ
    let names = persons.map { $0.name }

    View Slide

  20. struct ContentView: View {
    var body: some View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }

    View Slide

  21. struct ContentView: View {
    var body: some View {
    return VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    SFUVSO͕লུ͞Ε͍ͯͨ

    View Slide

  22. • Swift Evolution: SE-0255
    • Implicit return from single expressions
    • Swift Evolution: SE-XXXX
    • Function builders (draft proposal)
    • Swift Evolution: SE-0244
    • Opaque Result Type
    some View Λ࣮ݱ͢Δػೳ
    ;ͨͭΊʂ

    View Slide

  23. Function builders (draft proposal)

    View Slide

  24. • վߦ۠੾Γͷཁૉ͔ͨͪΒɺม਺એݴͱؔ਺ݺͼग़͠Λੜ੒͢Δ
    • @_functionBuilderΛ͚ͭͨؔ਺͸ɺFunction builderʹͳΔ
    SE-XXXX: Function Builders

    View Slide

  25. • վߦ۠੾Γͷཁૉ͔ͨͪΒɺม਺એݴͱؔ਺ݺͼग़͠Λੜ੒͢Δ
    • @_functionBuilderΛ͚ͭͨstruct͸ɺFunction builderʹͳΔ
    SE-XXXX: Function Builders
    // @TupleBuilder͸
    // @_functionBuilderͱͯ͠Ͳ͔͜Ͱఆٛ
    @TupleBuilder
    func build() -> (Int, Int, Int) {
    1
    2
    3
    }
    func build() -> (Int, Int, Int) {
    let _a = 1
    let _b = 2
    let _c = 3
    return TupleBuilder
    .buildBlock(_a, _b, _c)
    }
    ίϯύΠϥ͕ม׵

    View Slide

  26. struct ContentView: View {
    var body: some View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    ఆٛΛݟΔ

    View Slide

  27. public struct VStack : View where
    Content : View {
    @inlinable public init(

    alignment: HorizontalAlignment = .center,
    spacing: CGFloat? = nil,
    @ViewBuilder content: () -> Content)
    public typealias Body = Never
    }
    ఆٛΛݟΔ

    View Slide

  28. @_functionBuilder public struct ViewBuilder {
    public static func buildBlock
    (_ content: Content)
    -> Content where Content : View
    }
    Ҿ਺1ݸͰɺͦΕͱಉ͡ܕͷViewΛฦ͍ͯ͠Δ

    View Slide

  29. extension ViewBuilder {
    public static func buildBlock
    (_ c0: C0, _ c1: C1) ->
    TupleView<(C0, C1)>
    where C0 : View, C1 : View
    }
    Ҿ਺2ݸͰɺͦΕͱಉ͡ܕͷTupleView<(C0, C1)>Λฦ͍ͯ͠Δ

    View Slide

  30. extension ViewBuilder {
    public static func buildBlock
    (_ c0: C0, _ c1: C1) ->
    TupleView<(C0, C1)>
    where C0 : View, C1 : View
    }
    Ҿ਺2ݸͰɺͦΕͱಉ͡ܕͷTupleView<(C0, C1)>Λฦ͍ͯ͠Δ
    ʹ74UBDLͷҾ਺ͷਖ਼ମ

    View Slide

  31. extension ViewBuilder {
    public static func buildBlock(_
    c0: C0, _ c1: C1, _ c2: C2) -> TupleView<(C0, C1,
    C2)> where C0 : View, C1 : View, C2 : View
    }
    3ݸ൛

    View Slide

  32. extension ViewBuilder {
    public static func buildBlockC3>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3) ->
    TupleView<(C0, C1, C2, C3)> where C0 : View,
    C1 : View, C2 : View, C3 : View
    }
    4ݸ൛…

    View Slide

  33. extension ViewBuilder {
    public static func buildBlockC4, C5, C6, C7, C8, C9>(_ c0: C0, _ c1: C1, _ c2:
    C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7:
    C7, _ c8: C8, _ c9: C9) -> TupleView<(C0, C1, C2,
    C3, C4, C5, C6, C7, C8, C9)> where C0 : View,
    C1 : View, C2 : View, C3 : View, C4 : View, C5 :
    View, C6 : View, C7 : View, C8 : View, C9 : View
    }
    10ݸ൛·Ͱ͋Δ

    View Slide

  34. extension ViewBuilder {
    public static func buildBlockC4, C5, C6, C7, C8, C9>(_ c0: C0, _ c1: C1, _ c2:
    C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7:
    C7, _ c8: C8, _ c9: C9) -> TupleView<(C0, C1, C2,
    C3, C4, C5, C6, C7, C8, C9)> where C0 : View,
    C1 : View, C2 : View, C3 : View, C4 : View, C5 :
    View, C6 : View, C7 : View, C8 : View, C9 : View
    }
    10ݸ൛·Ͱ͋Δ
    ʹ10ݸ൛·Ͱ͔͠ͳ͍
    ͦΕҎ্ฒ΂͍ͨͱ͖͸ɺ
    GroupΛ࢖͏͜ͱ

    View Slide

  35. struct ContentView: View {
    var body: some View {
    return VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    ͭ·Γ͜ͷϒϩοΫ͸

    View Slide

  36. struct ContentView: View {
    var body: some View {
    return VStack {
    return ViewBuilder.buildBlock(
    Text(item.title),
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    }
    ͜͏ղऍ͞ΕΔ

    View Slide

  37. ฦΓ஋ͷܕ͸ $5FYU $5FYUͳͷͰɺ
    5VQMF7JFX 5FYU 5FYU
    ʹͳΔ
    struct ContentView: View {
    var body: some View {
    return VStack {
    return ViewBuilder.buildBlock(
    Text(item.title),
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    }

    View Slide

  38. ฦΓ஋ͷܕ͸ɺ74UBDLͷܕύϥϝʔλ$POUFOUʹΑͬͯ
    74UBDL5VQMF7JFX 5FYU 5FYU
    ʹͳΔ
    struct ContentView: View {
    var body: some View {
    return VStack {
    return ViewBuilder.buildBlock(
    Text(item.title),
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    }

    View Slide

  39. struct ContentView: View {
    var body: some View {
    let hoge = VStack {
    return ViewBuilder.buildBlock(
    Text(item.title),
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    )
    }
    return hoge
    }
    }
    ద౰ͳม਺ʹ୅ೖͯ͠ɺ
    PQU$MJDL͢Δͱɺ
    ܕΛ֬ೝ͢Δ͜ͱ͕Ͱ͖·͢

    View Slide

  40. @_functionBuilder public struct NumsBuilder {
    public static func buildBlock(_ nums: Int...) -> [Int] {
    nums
    }
    }
    func printNums(@NumsBuilder numsBuilder: () -> [Int]) {
    numsBuilder().forEach { print($0) }
    }
    printNums {
    1
    2
    3
    }
    ࣗ෼Ͱͭ͘Δ͜ͱ΋Ͱ͖Δ

    View Slide

  41. • Swift Evolution: SE-0255
    • Implicit return from single expressions
    • Swift Evolution: SE-XXXX
    • Function builders (draft proposal)
    • Swift Evolution: SE-0244
    • Opaque Result Type
    some View Λ࣮ݱ͢Δػೳ
    ͍͞͝ʂ

    View Slide

  42. Opaque Result Type

    View Slide

  43. • δΣωϦΫεͷΑ͏ͳ΋ͷ
    • some Protocol ͷΑ͏ͳܕ͕ Opaque Result Type
    • some View ͸

    ʮ۩ମతͳܕ͸ެ։͠ͳ͍͚ͲɺViewϓϩτίϧʹద߹ͨ͠ԿΒ͔ͷܕʯ

    Ͱ͋Δ͜ͱΛද͢
    SE-0244: Opaque Result Type

    View Slide

  44. struct ContentView: View {
    var body: some View {
    return VStack {
    return ViewBuilder.buildBlock(
    Text(item.title),
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    )
    }
    }
    }
    74UBDL5VQMF7JFX 5FYU 5FYU

    ͷ࣮ࡍͷܕ͸ɺ
    ͨͱ͑͹ɺTPNF7JFX
    ͕ͩɺ֎ଆ͔Βݟͨͱ͖͸7JFXͱͯ͠ৼΔ෣͏

    View Slide

  45. struct ContentView: View {
    var body: VStack> {
    return VStack (
    return ViewBuilder.buildBlock(
    Text(item.title),
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    )
    }
    }
    }
    ۩ମܕΛࢦఆͯ͠΋0,

    View Slide

  46. struct ContentView: View {
    var body: VStack> {
    return VStack (
    return ViewBuilder.buildBlock(
    Text(item.title),
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    )
    }
    }
    }
    ۩ମܕΛࢦఆͯ͠΋0,
    ͜ͷܗͳΒɺ΋͸΍4XJGUະຬͷจ๏Ͱ΋ҙຯ͕௨Δ

    View Slide

  47. ϝϦοτ
    • ͖ͬ͢Γ͢Δ
    SE-0244: Opaque Result Type

    View Slide

  48. ϝϦοτ
    • ͖ͬ͢Γ͢Δ
    SE-0244: Opaque Result Type
    VStack>
    ͦΕ͚ͩͰ͸ͳ͍
    ʢ˞Computed property ͸ܕએݴΛলུͰ͖ͳ͍ʣ

    View Slide

  49. • ίϯύΠϧ࣌ʹԿΒ͔ͷܕͰ͋Δ͜ͱ͕֬ఆ͢Δ
    • ίϯύΠϧ࣌ʹ෼͔ΔͷͰɺ࣮ߦ࣌ʹύϑΥʔϚϯεϩε͕ͳ͍
    SE-0244: Opaque Result Type

    View Slide

  50. SE-0244: Opaque Result Type
    func map(l: List, _ f: A -> B) -> List {
    switch l {
    case .Nil: return .Nil
    case let .Cons(x, xs):
    return cons(f(x), map(xs, f))
    }
    }
    [1,2].map { $0 * 2 }
    δΣωϦΫεͷܕ͸

    ֎ଆ͔Β࣮ࡍͷܕΛ

    ࢦఆͯ֬͠ఆ͢Δ
    Opaque Result Type Ͱ͸

    ಺ଆ͔Β࣮ࡍͷܕ͕֬ఆ͢Δ
    *OU*OU
    struct ContentView: View {
    var body: some View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    74UBDL
    5VQMF7JFX 5FYU 5FYU

    View Slide

  51. struct ContentView: View {
    var body: View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    7JFX͸QSPUPDPMͰɺ
    BTTPDJBUFEUZQFΛ΋ͭͷͰɺ
    ͜͏͸ॻ͚ͳ͍

    View Slide

  52. struct ContentView: View {
    var body: AnyView {
    AnyView(VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    })
    }
    }
    ܕফڈ͸Ͱ͖Δ͕ɺ
    ύϑΥʔϚϯεϩε͕͋Δ

    View Slide

  53. • Swift Evolution: SE-0255
    • Implicit return from single expressions
    • Swift Evolution: SE-XXXX
    • Function builders (draft proposal)
    • Swift Evolution: SE-0244
    • Opaque Result Type
    some View Λ࣮ݱ͢Δػೳ

    View Slide

  54. ·ͱΊ

    View Slide

  55. struct ContentView: View {
    var body: some View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    SFUVSO͸
    ࣜͭͳΒলུՄೳ

    View Slide

  56. struct ContentView: View {
    var body: some View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    SFUVSO͸
    ࣜͭͳΒলུՄೳ
    'VODUJPO#VJMEFSʹΑͬͯɺ
    7JFX#VJMEFSCVJME#MPDL 5FYU
    5FYU

    ͱղऍ͞ΕΔ
    ࣮ࡍͷܕ͸ɺ74UBDL5VQMF7JFX 5FYU 5FYU

    View Slide

  57. struct ContentView: View {
    var body: some View {
    VStack {
    Text(item.title)
    Text(item.subtitle)
    .foregroundColor(Color.gray)
    }
    }
    }
    TPNF7JFX͸
    7JFXʹద߹͢Δ
    ԿΒ͔ͷܕͰ͋Δ͜ͱΛࣔ͢
    'VODUJPO#VJMEFSʹΑͬͯɺ
    7JFX#VJMEFSCVJME#MPDL 5FYU
    5FYU

    ͱղऍ͞ΕΔ
    ࣮ࡍͷܕ͸ɺ74UBDL5VQMF7JFX 5FYU 5FYU

    SFUVSO͸
    ࣜͭͳΒলུՄೳ

    View Slide

  58. ୈҰ෦
    some Viewฤ
    ׬

    View Slide

  59. ୈೋ෦
    Property Wrappers ฤ

    View Slide

  60. View Slide

  61. View Slide

  62. struct ContentView: View {
    @State var isOn: Bool
    var body: some View {
    Toggle(isOn: $isOn) {
    if self.isOn {
    Text("On")
    } else {
    Text("Off")
    }
    }.frame(width: 200)
    }
    }
    #PPMܕͷ
    ϓϩύςΟ

    View Slide

  63. struct ContentView: View {
    @State var isOn: Bool
    var body: some View {
    Toggle(isOn: $isOn) {
    if self.isOn {
    Text("On")
    } else {
    Text("Off")
    }
    }.frame(width: 200)
    }
    }
    #PPMܕͷ
    ϓϩύςΟ
    UPHHMFΛͻͱͭ
    ΋ͭ7JFX

    View Slide

  64. struct ContentView: View {
    @State var isOn: Bool
    var body: some View {
    Toggle(isOn: $isOn) {
    if self.isOn {
    Text("On")
    } else {
    Text("Off")
    }
    }.frame(width: 200)
    }
    }
    #PPMܕͷ
    ϓϩύςΟ
    UPHHMFΛͻͱͭ
    ΋ͭ7JFX
    JT0OͳΒl0Oz
    JT0OͳΒl0⒎

    View Slide

  65. struct ContentView: View {
    @State var isOn: Bool
    var body: some View {
    Toggle(isOn: $isOn) {
    if self.isOn {
    Text("On")
    } else {
    Text("Off")
    }
    }.frame(width: 200)
    }
    }
    !4UBUF
    ͬͯԿʁ

    View Slide

  66. struct ContentView: View {
    @State var isOn: Bool
    var body: some View {
    Toggle(isOn: $isOn) {
    if self.isOn {
    Text("On")
    } else {
    Text("Off")
    }
    }.frame(width: 200)
    }
    }
    JT0O
    ͬͯԿʁ
    !4UBUF
    ͬͯԿʁ

    View Slide

  67. struct ContentView: View {
    @State var isOn: Bool
    var body: some View {
    Toggle(isOn: $isOn) {
    if self.isOn {
    Text("On")
    } else {
    Text("Off")
    }
    }.frame(width: 200)
    }
    }
    JT0O
    ͬͯԿʁ
    !4UBUF
    ͬͯԿʁ
    QSPKFDUFE7BMVFΛ
    ฦ͢ԋࢉࢠ
    1SPQFSUZ8SBQQFS

    View Slide

  68. • Swift Evolution: SE-0258
    • Property Wrappers
    ViewόΠϯσΟϯάΛ࣮ݱ͢Δػೳ

    View Slide

  69. • ϓϩύςΟʔͷϥούʔ
    • தʹਅͷ஋͕แ·Ε͍ͯΔΠϝʔδ
    • ϓϩύςΟͷsetterͱgetterΛݺΜͩͱ͖ʹ͍ͨ͠ॲཧΛɺ

    ผΫϥεʹҠৡ͢ΔσβΠϯύλʔϯΛ

    ίϯύΠϥʔϨϕϧͰఏڙͯ͘͠ΕΔػೳ
    • Kotlin ͷ Delegated Properties ͱࣅ͍ͯΔ
    Property Wrappers

    View Slide

  70. struct Foo {
    private var _foo: Int?
    var foo: Int {
    get {
    if let value = _foo { return value }
    let initialValue = 1738
    _foo = initialValue
    return initialValue
    }
    set {
    _foo = newValue
    }
    }
    }
    ஗ԆධՁ͢Δ
    ܭࢉܕϓϩύςΟ
    MB[ZWBSGPP
    ͱ͓ͳ͡

    ܭࢉܕϓϩύςΟʹ
    ౎౓ܭࢉͯ͠
    ݁ՌΛฦͨ͠Γ HFU

    ઃఆͨ͠ΓʢTFUʣ
    ͢Δ΋ͷ

    View Slide

  71. struct Foo {
    private var _foo: Int?
    var foo: Int {
    get {
    if let value = _foo { return value }
    let initialValue = 1738
    _foo = initialValue
    return initialValue
    }
    set {
    _foo = newValue
    }
    }
    }
    ௕͍͠ଟ͍

    View Slide

  72. @propertyWrapper
    enum Lazy {
    case uninitialized(() -> Value)
    case initialized(Value)
    init(wrappedValue: @autoclosure @escaping () -> Value) {
    self = .uninitialized(wrappedValue)}
    var wrappedValue: Value {
    mutating get {
    switch self {
    case .uninitialized(let initializer):
    let value = initializer()
    self = .initialized(value)
    return value
    case .initialized(let value):
    return value
    }}
    set { self = .initialized(newValue) }
    }}
    δΣωϦΫε൛Λ࡞ͬͯ
    TFUUFSͱHFUUFSɺ
    όοΩϯάϑΟʔϧυ
    ʢXSBQQFE7BMVFʣ
    ʹؔ͢Δ৘ใΛɺ
    1SPQFSUZ8SBQQFS
    ʹԡ͠ࠐΊΕ͹ʜ

    View Slide

  73. class Foo {
    @Lazy var foo: Int = 1738
    }
    ࢖͏ଆ͸͖ͬ͢Γʂ

    View Slide

  74. class Foo {
    @Lazy var foo: Int = 1738
    }
    private var _foo: Lazy = Lazy(wrappedValue: 1738)
    var foo: Int {
    get { return _foo.wrappedValue }
    set { _foo.wrappedValue = newValue }
    }
    ࣮ࡍʹ͸͜Μͳײ͡ͷίʔυʹల։͞ΕΔ

    View Slide

  75. @propertyWrapper
    // ͱ͘ʹҙຯ͸ͳ͍
    struct Nyan {
    ...
    var projectedValue: String {
    return "ʹΌʔΜ"
    }
    }
    QSPKFDUFE7BMVF͕͋Ε͹

    View Slide

  76. class Hoge {
    @Nyan var foo: Int = 1738
    func meow() {
    print($foo)
    }
    }
    Hoge().meow() // ʹΌʔΜ
    ͰऔΓग़ͤΔ
    ※ projectedValue ͕ఆٛ͞Εͯͳ͚Ε͹

    Use of unresolved identifier ‘$foo’

    ʹͳΓ·ͨ͠ (Xcode 11 GM Seed1)

    View Slide

  77. • Property wrapper Ͱ͸

    ϓϩύςΟʹؔ͢ΔॲཧΛଞͷܕʹҠৡͰ͖Δ
    • @Hoge var foo: Int ͱॻ͘ͱ

    var _foo: Hoge ͱ var foo: Int ʹల։͞ΕΔ
    • foo ͸ _foo.wrappedValue ͱ౳Ձ
    • $foo ͸ _foo.projectedValue ͱ౳Ձ
    • SwiftUIͰ͸Ͳ͏͍͏;͏ʹ࢖ΘΕ͍ͯΔͷ͔ʁ
    Property Wrapper ·ͱΊ

    View Slide

  78. struct ContentView: View {
    @State var isOn: Bool
    var body: some View {
    Toggle(isOn: $isOn) {
    if self.isOn {
    Text("On")
    } else {
    Text("Off")
    }
    }.frame(width: 200)
    }
    }
    ͜Ε͸
    1SPQFSUZ8SBQQFS

    View Slide

  79. struct ContentView: View {
    @State var isOn: Bool
    var body: some View {
    Toggle(isOn: $isOn) {
    if self.isOn {
    Text("On")
    } else {
    Text("Off")
    }
    }.frame(width: 200)
    }
    }
    ͜Ε͸
    1SPQFSUZ8SBQQFS
    ී௨ʹݺͿͱ
    XSBQQFE7BMVF͕औΕΔ

    View Slide

  80. struct ContentView: View {
    @State var isOn: Bool
    var body: some View {
    Toggle(isOn: $isOn) {
    if self.isOn {
    Text("On")
    } else {
    Text("Off")
    }
    }.frame(width: 200)
    }
    }
    ͜Ε͸
    1SPQFSUZ8SBQQFS
    ී௨ʹݺͿͱ
    XSBQQFE7BMVF͕औΕΔ
    ԋࢉࢠͰ
    QSPKFDUFE7BMVF͕औΕΔ

    View Slide

  81. มߋΛݕ஌ɾ௨஌͢Δ
    #JOEJOH7BMVF
    /// A linked View property that instantiates a persistent state
    /// value of type `Value`, allowing the view to read and update its
    /// value.
    @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
    @propertyWrapper public struct State : DynamicProperty {
    /// Initialize with the provided initial value.
    public init(wrappedValue value: Value)
    /// Initialize with the provided initial value.
    public init(initialValue value: Value)
    /// The current state value.
    public var wrappedValue: Value { get nonmutating set }
    /// Produces the binding referencing this state value
    public var projectedValue: Binding { get }
    }

    View Slide

  82. • Property wrapperͰget/set࣌ʹΠϕϯτͷൃߦΛߦ͏͜ͱʹΑΓɺ

    ஋ͷಉظ΍View΁ͷόΠϯσΟϯάΛՄೳʹ͍ͯ͠Δʢ˞ʣ
    • $ Ͱ Binding͕औΓग़ͤΔ
    • BindingΛViewʹ౉͢ͱɺViewʹόΠϯσΟϯά͞ΕΔ

    ʢมߋͨ͠ͱ͖ʹΠϕϯτΛൃߦͨ͠Γɺ

    ɹมߋΛݕ஌ͯ͠Viewʹ൓өͯ͘͠ΕͨΓ͢Δʣ
    SwiftUIͰ͍ͯ͠Δ͜ͱ ·ͱΊ
    ʢ˞ʣਪଌɻIF΍υΩϡϝϯτɺWWDCͷηογϣϯͷ಺༰ͳͲ͔Βਪଌ͢Δ͔͠ͳ͍

    View Slide

  83. • ެࣜ࡞ྫ
    • UserDefaults
    • Clamping
    • ϥΠϒϥϦ
    • ValidatedPropertyKit
    • DIAttribute
    ྫΛ৭ʑΈͯΠϝʔδΛ͔ͭ΋͏

    View Slide

  84. enum GlobalSettings {
    @UserDefault(key: "FOO_FEATURE_ENABLED", defaultValue: false)
    static var isFooFeatureEnabled: Bool
    @UserDefault(key: "BAR_FEATURE_ENABLED", defaultValue: false)
    static var isBarFeatureEnabled: Bool
    }
    User Defaults
    ม਺ʹॻ͘ϊϦͰӬଓԽ

    View Slide

  85. @propertyWrapper
    struct UserDefault {
    let key: String
    let defaultValue: T
    var wrappedValue: T {
    get {
    return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
    }
    set {
    UserDefaults.standard.set(newValue, forKey: key)
    }
    }
    }
    γϯϓϧ
    ʢγϯϓϧ͗ͯ͢ෆཁઆ΋͋Δʣ
    User Defaultsʢ࣮૷ʣ

    View Slide

  86. Clamping
    struct Color {
    @Clamping(min: 0, max: 255) var red: Int = 127
    @Clamping(min: 0, max: 255) var green: Int = 127
    @Clamping(min: 0, max: 255) var blue: Int = 127
    @Clamping(min: 0, max: 255) var alpha: Int = 255
    }
    ஋Λ
    ʹ·ΔΊͯ͘ΕΔ

    View Slide

  87. var wrappedValue: V {
    get { return value }
    set {
    if newValue < min {
    value = min
    } else if newValue > max {
    value = max
    } else {
    value = newValue
    }
    }
    }
    @propertyWrapper
    struct Clamping {
    var value: V
    let min: V
    let max: V
    init(wrappedValue: V,
    min: V, max: V) {
    value = wrappedValue
    self.min = min
    self.max = max
    assert(value >= min
    && value <= max)
    }
    Clampingʢ࣮૷ʣ

    View Slide

  88. • SvenTiigi/ValidatedPropertyKit
    • https://github.com/SvenTiigi/ValidatedPropertyKit
    • @Validated(.isEmail)Ͱ

    EϝʔϧΞυϨεͷόϦσʔγϣϯ͕Ͱ͖ͨΓ͢Δ
    • γϯϓϧ
    SvenTiigi/ValidatedPropertyKit

    View Slide

  89. SvenTiigi/ValidatedPropertyKit
    @Validated(.nonEmpty)
    var username: String?
    @Validated(.isEmail)
    var email: String?
    @Validated(.range(8...))
    var password: String?
    @Validated(.greaterOrEqual(1))
    var friends: Int?
    @Validated(.isURL && .hasPrefix("https"))
    var avatarURL: String?

    View Slide

  90. SvenTiigi/ValidatedPropertyKit
    @Validated(.nonEmpty)
    var username: String?
    @Validated(.isEmail)
    var email: String?
    @Validated(.range(8...))
    var password: String?
    @Validated(.greaterOrEqual(1))
    var friends: Int?
    @Validated(.isURL && .hasPrefix("https"))
    var avatarURL: String?
    &ϝʔϧͷܗࣜͳΒ&ϝʔϧɺ
    ෆਖ਼ͳܗࣜͳΒOJMʹͳΔ

    View Slide

  91. • tattn/DIAttribute
    • https://github.com/tattn/DIAttribute
    • AndroidͷDaggerͷΑ͏ͳΞϊςʔγϣϯϕʔεͰDIΛ࣮ݱ͢ΔϥΠ
    ϒϥϦ
    • γϯϓϧ
    tattn/DIAttribute

    View Slide

  92. tattn/DIAttribute
    DIResolver.register(ViewController.self,
    keyPath: \.apiClient,
    value: ProductionAPIClient())
    DIResolver.register(ViewController.self,
    keyPath: \.apiClient,
    value: MockAPIClient())

    View Slide

  93. tattn/DIAttribute
    final class ViewController: UIViewController {
    @Inject(Self.self) var apiClient: APIClientProtocol
    }
    %*3FTPMWFSSFHJTUFSͰ
    ొ࿥ͨ͠஋͕࢖ΘΕΔ

    View Slide

  94. import Foundation
    @propertyWrapper
    public struct Inject {
    public init(_ type: Target.Type) {}
    public var wrappedValue: Value {
    DIResolver.resolve(Target.self)!
    }
    }
    SFTPMWFΛୟ͍͍ͯΔ͚ͩ

    View Slide

  95. SFHJTUFSͨ͠ͱ͖ʹ%*3FTPMWFS͕ΫϩʔδϟΛอ͓࣋ͯ͠Γɺ
    SFTPMWFͰ͸ɺอ࣋ͨ͠ΫϩʔδϟΛධՁͯ͠ฦ͢
    public struct DIResolver {
    private static var factories:
    [ObjectIdentifier: [ObjectIdentifier: Factory]] = [:]
    public static func resolve(
    _ target: Target.Type = Target.self,
    value: Value.Type = Value.self) -> Value? {
    factories[.init(target)]?[.init(value)]?() as? Value
    }
    }

    View Slide

  96. ୈೋ෦
    Property Wrappers ฤ
    ׬

    View Slide

  97. SwiftUI ͸Swift5.1ͷػೳʹΑ͖ͬͯͬ͢Γॻ͚ΔΑ͏ʹͳ͍ͬͯΔ
    • Swift Evolution: SE-0255: Implicit return from single expressions
    • Swift Evolution: SE-XXXX :Function builders
    • Swift Evolution: SE-0244: Opaque Result Type
    • Swift Evolution: SE-0258: Property Wrappers
    • ܅΋܅͚ͩͷ

    ࠷ڧͷFunction Builders / Property WrappersΛ࡞ͬͯಆ͓͏ʂ
    ·ͱΊ

    View Slide

  98. • Swift Evolution
    • https://github.com/apple/swift-evolution/blob/master/proposals/0255-omit-return.md
    • https://github.com/apple/swift-evolution/blob/9992cf3c11c2d5e0ea20bee98657d93902d5b174/proposals/XXXX-
    function-builders.md
    • https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md
    • https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#
    References

    View Slide

  99. • SwiftUIͷຐ๏Λ࣮ݱ͢Δ࢓૊Έ (Custom Attributes, Function Builder)
    • https://qiita.com/kentrino/items/dc6e77a0ddd21187cc55
    • SwiftUIͷίʔυΛಡΈղ͘
    • https://blog.personal-factory.com/2019/06/07/understand-swiftui-code/
    • Swift 5.1 ʹಋೖ͞ΕΔ Opaque Result Type ͱ͸Կ͔
    • https://qiita.com/koher/items/338d2f2d0c4731e3508f
    • Opaque Result Typeͷղઆ
    • https://qiita.com/omochimetaru/items/f13fe3e54fab01648ba4
    • SwiftUIͰ࢖༻͞Ε͍ͯΔSwift5.1ͷ৽ػೳ - ΫοΫύου։ൃऀϒϩά
    • https://techlife.cookpad.com/entry/2019/06/25/120000Qa
    • SwiftUIͷProperty Wrappersͱσʔλ΁ͷΞΫηεํ๏
    • https://qiita.com/shiz/items/6eaf87fa79499623306a
    See Also

    View Slide

  100. Enjoy SwiftUI!

    View Slide